1 // Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file 2 // for details. All rights reserved. Use of this source code is governed by a 3 // BSD-style license that can be found in the LICENSE file. 4 package com.android.tools.r8; 5 6 import static com.android.tools.r8.TestCondition.compilers; 7 import static org.junit.Assert.assertEquals; 8 import static org.junit.Assert.fail; 9 10 import com.android.tools.r8.ToolHelper.ArtCommandBuilder; 11 import com.android.tools.r8.ToolHelper.DexVm; 12 import com.android.tools.r8.ToolHelper.ProcessResult; 13 import com.android.tools.r8.dex.Constants; 14 import com.android.tools.r8.errors.CompilationError; 15 import com.android.tools.r8.shaking.ProguardRuleParserException; 16 import com.android.tools.r8.utils.ArtErrorParser; 17 import com.android.tools.r8.utils.ArtErrorParser.ArtErrorInfo; 18 import com.android.tools.r8.utils.DexInspector; 19 import com.android.tools.r8.utils.FileUtils; 20 import com.android.tools.r8.utils.JarBuilder; 21 import com.android.tools.r8.utils.ListUtils; 22 import com.android.tools.r8.utils.OffOrAuto; 23 import com.google.common.base.Charsets; 24 import com.google.common.collect.ImmutableList; 25 import com.google.common.collect.ImmutableListMultimap; 26 import com.google.common.collect.ImmutableMap; 27 import com.google.common.collect.Multimap; 28 import com.google.common.collect.ObjectArrays; 29 import com.google.common.collect.Sets; 30 import java.io.File; 31 import java.io.FileNotFoundException; 32 import java.io.IOException; 33 import java.nio.file.Files; 34 import java.nio.file.Path; 35 import java.nio.file.Paths; 36 import java.util.ArrayList; 37 import java.util.Arrays; 38 import java.util.Collection; 39 import java.util.Collections; 40 import java.util.HashMap; 41 import java.util.HashSet; 42 import java.util.List; 43 import java.util.Map; 44 import java.util.Set; 45 import java.util.concurrent.ExecutionException; 46 import java.util.stream.Collectors; 47 import org.junit.ComparisonFailure; 48 import org.junit.Rule; 49 import org.junit.rules.ExpectedException; 50 import org.junit.rules.TemporaryFolder; 51 52 /** 53 * This test class is not invoked directly. Instead, the gradle script generates one subclass per 54 * actual art test. This allows us to run these in parallel. 55 */ 56 public abstract class R8RunArtTestsTest { 57 58 private static final boolean DEX_COMPARE_WITH_DEX_REFERENCE_ON_FAILURE = true; 59 60 private final String name; 61 private final DexTool toolchain; 62 63 @Rule 64 public ExpectedException thrown = ExpectedException.none(); 65 66 public enum DexTool { 67 JACK, 68 DX, 69 NONE // Working directly on .class files. 70 } 71 72 public enum CompilerUnderTest { 73 D8, 74 R8, 75 R8_AFTER_D8 // refers to the R8 (default: debug) step but implies a previous D8 step as well 76 } 77 78 private static final String ART_TESTS_DIR = "tests/2017-07-07/art"; 79 private static final String ART_LEGACY_TESTS_DIR = "tests/2016-12-19/art/"; 80 private static final String ART_TESTS_NATIVE_LIBRARY_DIR = "tests/2017-07-07/art/lib64"; 81 private static final String ART_LEGACY_TESTS_NATIVE_LIBRARY_DIR = "tests/2016-12-19/art/lib64"; 82 83 // Input jar for jctf tests. 84 private static final String JCTF_COMMON_JAR = "build/libs/jctfCommon.jar"; 85 86 // Parent dir for on-the-fly compiled jctf dex output. 87 private static final String JCTF_TESTS_PREFIX = "build/classes/jctfTests"; 88 private static final String JCTF_TESTS_LIB_PREFIX = 89 JCTF_TESTS_PREFIX + "/com/google/jctf/test/lib"; 90 private static final String JUNIT_TEST_RUNNER = "org.junit.runner.JUnitCore"; 91 private static final String JUNIT_JAR = "third_party/gradle/gradle/lib/plugins/junit-4.12.jar"; 92 private static final String HAMCREST_JAR = 93 "third_party/gradle/gradle/lib/plugins/hamcrest-core-1.3.jar"; 94 95 // Test that required to set min-api to a specific value. 96 private static Map<String, Integer> needMinSdkVersion = 97 new ImmutableMap.Builder<String, Integer>() 98 // Android O 99 .put("952-invoke-custom", Constants.ANDROID_O_API) 100 .put("953-invoke-polymorphic-compiler", Constants.ANDROID_O_API) 101 .put("957-methodhandle-transforms", Constants.ANDROID_O_API) 102 .put("958-methodhandle-stackframe", Constants.ANDROID_O_API) 103 .put("959-invoke-polymorphic-accessors", Constants.ANDROID_O_API) 104 // Test intentionally asserts presence of bridge default methods desugar removes. 105 .put("044-proxy", Constants.ANDROID_N_API) 106 // Test intentionally asserts absence of default interface method in a class. 107 .put("048-reflect-v8", Constants.ANDROID_N_API) 108 // Uses default interface methods. 109 .put("616-cha-interface-default", Constants.ANDROID_N_API) 110 // Interface initializer is not triggered after desugaring. 111 .put("962-iface-static", Constants.ANDROID_N_API) 112 // Interface initializer is not triggered after desugaring. 113 .put("964-default-iface-init-gen", Constants.ANDROID_N_API) 114 // AbstractMethodError (for method not implemented in class) instead of 115 // IncompatibleClassChangeError (for conflict of default interface methods). 116 .put("968-default-partial-compile-gen", Constants.ANDROID_N_API) 117 // NoClassDefFoundError (for companion class) instead of NoSuchMethodError. 118 .put("970-iface-super-resolution-gen", Constants.ANDROID_N_API) 119 // NoClassDefFoundError (for companion class) instead of AbstractMethodError. 120 .put("971-iface-super", Constants.ANDROID_N_API) 121 // Test for miranda methods is not relevant for desugaring scenario. 122 .put("972-default-imt-collision", Constants.ANDROID_N_API) 123 // Uses default interface methods. 124 .put("972-iface-super-multidex", Constants.ANDROID_N_API) 125 // java.util.Objects is missing and test has default methods. 126 .put("973-default-multidex", Constants.ANDROID_N_API) 127 // a.klass.that.does.not.Exist is missing and test has default methods. 128 .put("974-verify-interface-super", Constants.ANDROID_N_API) 129 // Desugaring of interface private methods is not yet supported. 130 .put("975-iface-private", Constants.ANDROID_N_API) 131 .build(); 132 133 // Tests requiring interface method desugaring because they explicitly or 134 // implicitly define static or default interface methods and are enabled 135 // on pre-N Android. 136 private static List<String> enableInterfaceMethodDesugaring = 137 ImmutableList.of( 138 "563-checker-invoke-super", 139 "604-hot-static-interface", 140 "961-default-iface-resolution-gen", 141 "963-default-range-smali", 142 "965-default-verify", 143 "967-default-ame", 144 "969-iface-super", 145 "978-virtual-interface" 146 ); 147 148 // Tests that timeout when run with Art. 149 private static final Multimap<String, TestCondition> timeoutOrSkipRunWithArt = 150 new ImmutableListMultimap.Builder<String, TestCondition>() 151 // Loops on art - timeout. 152 .put("109-suspend-check", TestCondition.match(TestCondition.runtimes(DexVm.ART_5_1_1))) 153 // Flaky loops on art. 154 .put("129-ThreadGetId", TestCondition.match(TestCondition.runtimes(DexVm.ART_5_1_1))) 155 // Takes ages to run on art 5.1.1 and behaves the same as on 6.0.1. Running this 156 // tests on 5.1.1 makes our buildbot cycles time too long. 157 .put("800-smali", TestCondition.match(TestCondition.runtimes(DexVm.ART_5_1_1))) 158 .build(); 159 160 // Tests that are flaky with the Art version we currently use. 161 // TODO(zerny): Amend flaky tests with an expected flaky result to track issues. 162 private static List<String> flakyRunWithArt = ImmutableList.of( 163 // Can crash but mostly passes 164 // Crashes: 165 // check_reference_map_visitor.h:44] At Main.f 166 // Check failed: size() >= sizeof(T) (size()=0, sizeof(T)=1) 167 // If art is passed -Xrelocate instead of -Xnorelocate the test passes. 168 "004-ReferenceMap", 169 // Also marked flaky in the art repo, sometimes hangs, sometimes fails with a segfault: 170 // line 105: 23283 Segmentation fault 171 "004-ThreadStress", 172 // When it fails: 173 // stack_walk_jni.cc:57] Check failed: 0xdU == GetDexPc() (0xdU=13, GetDexPc()=23) 174 // The R8/D8 code does not produce code with the same instructions. 175 "004-StackWalk", 176 // Nothing ensures the weak-ref is not cleared between makeRef and printWeakReference on lines 177 // Main.java:78-79. An expected flaky result contains: "but was:<wimp: [null]". 178 "036-finalizer", 179 // Can lead to art-master crash: concurrent_copying.cc:2135] Check failed: 180 // to_ref != nullptr Fall-back non-moving space allocation failed 181 "080-oom-fragmentation", 182 // Seen crash: currently no more information 183 "144-static-field-sigquit", 184 // Opens a lot of file descriptors and depending on the state of the machine this 185 // can crash art or not. Skip the run on art. 186 "151-OpenFileLimit", 187 // Can cause a segfault in the art vm 7.0.0 188 // tools/linux/art-7.0.0/bin/art: line 105: 14395 Segmentation fault 189 "607-daemon-stress", 190 // Marked as flaky in the Art repository. 191 "149-suspend-all-stress" 192 ); 193 194 // Tests that are never compiled or run. 195 private static List<String> skipAltogether = ImmutableList.of( 196 // This test contains an invalid type hierarchy, which we cannot currently handle. 197 "065-mismatched-implements", 198 // This test contains invalid dex code that reads uninitialized registers after an 199 // an instruction that would in any case throw (implicit via aget null 0). 200 "706-jit-skip-compilation", 201 // This test uses a user defined class loader to validate correct execution order 202 // between loadclass event and the execution of a static method. 203 // This does not work when performing inlining across classes. 204 "496-checker-inlining-class-loader" 205 ); 206 207 // Tests that may produce different output on consecutive runs or when processed or not. 208 private static List<String> outputMayDiffer = ImmutableList.of( 209 // One some versions of art, this will print the address of the boot classloader, which 210 // may change between runs. 211 "506-verify-aput", 212 // Art does fail during validation of dex files if it encounters an ill-typed virtual-invoke 213 // but allows interface-invokes to pass. As we rewrite one kind into the other, we remove 214 // a verification error and thus change output. 215 "135-MirandaDispatch", 216 // This test constructs a conflict between static and instance field and expects to throw 217 // an IllegalClassChangeException. However, with R8 or D8, the two accesses are rebound to 218 // their actual target, this resolving the conflict. 219 "073-mismatched-field" 220 ); 221 222 // Tests that make use of agents/native code. Our test setup does handle flags/linking of these. 223 private static List<String> usesNativeAgentCode = ImmutableList.of( 224 "497-inlining-and-class-loader", 225 "626-const-class-linking", 226 "642-fp-callees", 227 "909-attach-agent", 228 "914-hello-obsolescence", 229 "915-obsolete-2", 230 "916-obsolete-jit", 231 "917-fields-transformation", 232 "918-fields", 233 "919-obsolete-fields", 234 "920-objects", 235 "921-hello-failure", 236 "922-properties", 237 "923-monitors", 238 "924-threads", 239 "925-threadgroups", 240 "926-multi-obsolescence", 241 "927-timers", 242 "928-jni-table", 243 "929-search", 244 "930-hello-retransform", 245 "931-agent-thread", 246 "932-transform-saves", 247 "933-misc-events", 248 "934-load-transform", 249 "935-non-retransformable", 250 "936-search-onload", 251 "937-hello-retransform-package", 252 "938-load-transform-bcp", 253 "939-hello-transformation-bcp", 254 "940-recursive-obsolete", 255 "941-recurive-obsolete-jit", 256 "942-private-recursive", 257 "943-private-recursive-jit", 258 "944-transform-classloaders", 259 "945-obsolete-native", 260 "946-obsolete-throw", 261 "947-reflect-method", 262 "948-change-annotations", 263 "949-in-memory-transform", 264 "950-redefine-intrinsic", 265 "951-threaded-obsolete", 266 "980-redefine-object" 267 ); 268 269 // Tests with custom run. 270 private static List<String> customRun = ImmutableList.of( 271 "000-nop", 272 "018-stack-overflow", 273 "055-enum-performance", 274 "071-dexfile-map-clean", 275 "091-override-package-private-method", 276 "103-string-append", 277 "115-native-bridge", 278 "116-nodex2oat", 279 "117-nopatchoat", 280 "118-noimage-dex2oat", 281 "119-noimage-patchoat", 282 "126-miranda-multidex", 283 "127-checker-secondarydex", 284 "131-structural-change", 285 "133-static-invoke-super", 286 "134-nodex2oat-nofallback", 287 "137-cfi", 288 "138-duplicate-classes-check2", 289 "146-bad-interface", 290 "147-stripped-dex-fallback", 291 "304-method-tracing", 292 "529-checker-unresolved", 293 "555-checker-regression-x86const", 294 "569-checker-pattern-replacement", 295 "570-checker-osr", 296 "574-irreducible-and-constant-area", 297 "577-profile-foreign-dex", 298 "595-profile-saving", 299 "597-deopt-new-string", 300 "608-checker-unresolved-lse", 301 "613-inlining-dex-cache", 302 "636-wrong-static-access", 303 "900-hello-plugin", 304 "901-hello-ti-agent", 305 "902-hello-transformation", 306 "910-methods", 307 "911-get-stack-trace", 308 "912-classes", 309 "913-heaps" 310 ); 311 312 // Tests with C++ code (JNI) 313 private static List<String> useJNI = ImmutableList.of( 314 "004-JniTest", 315 "004-NativeAllocations", 316 "004-ReferenceMap", 317 "004-SignalTest", 318 "004-StackWalk", 319 "004-ThreadStress", 320 "004-UnsafeTest", 321 "044-proxy", 322 "051-thread", 323 "115-native-bridge", 324 "117-nopatchoat", 325 "136-daemon-jni-shutdown", 326 "137-cfi", 327 "139-register-natives", 328 "141-class-unload", 329 "148-multithread-gc-annotations", 330 "149-suspend-all-stress", 331 "154-gc-loop", 332 "155-java-set-resolved-type", 333 "157-void-class", 334 "158-app-image-class-table", 335 "454-get-vreg", 336 "457-regs", 337 "461-get-reference-vreg", 338 "466-get-live-vreg", 339 "497-inlining-and-class-loader", 340 "543-env-long-ref", 341 "566-polymorphic-inlining", 342 "570-checker-osr", 343 "595-profile-saving", 344 "596-app-images", 345 "597-deopt-new-string", 346 "616-cha", 347 "616-cha-abstract", 348 "616-cha-interface", 349 "616-cha-interface-default", 350 "616-cha-miranda", 351 "616-cha-regression-proxy-method", 352 "616-cha-native", 353 "616-cha-proxy-method-inline", 354 "626-const-class-linking", 355 "626-set-resolved-string", 356 "900-hello-plugin", 357 "901-hello-ti-agent", 358 "1337-gc-coverage" 359 ); 360 361 private static List<String> expectedToFailRunWithArtNonDefault = ImmutableList.of( 362 // Fails due to missing symbol, jni tests, fails on non-R8/D8 run. 363 "004-JniTest", 364 "004-SignalTest", 365 "004-ThreadStress", 366 "004-UnsafeTest", 367 "044-proxy", 368 "051-thread", 369 "136-daemon-jni-shutdown", 370 "139-register-natives", 371 "148-multithread-gc-annotations", 372 "149-suspend-all-stress", 373 "154-gc-loop", 374 "155-java-set-resolved-type", 375 "156-register-dex-file-multi-loader", 376 "157-void-class", 377 "158-app-image-class-table", 378 "466-get-live-vreg", 379 "497-inlining-and-class-loader", 380 "566-polymorphic-inlining", 381 "596-app-images", 382 "616-cha", 383 "616-cha-abstract", 384 "616-cha-regression-proxy-method", 385 "616-cha-native", 386 "626-set-resolved-string", 387 "629-vdex-speed", 388 "1337-gc-coverage", 389 390 // Addition of checks for super-class-initialization cause this to abort on non-ToT art. 391 "008-exceptions", 392 393 // Fails due to non-matching Exception messages. 394 "201-built-in-except-detail-messages", 395 396 // Fails on non-R8/D8 run 397 "031-class-attributes", 398 "617-clinit-oome" 399 ); 400 401 private static Map<DexVm, List<String>> expectedToFailRunWithArtVersion = ImmutableMap.of( 402 DexVm.ART_7_0_0, ImmutableList.of( 403 // Generally fails on non-R8/D8 running. 404 "412-new-array", 405 "610-arraycopy", 406 "625-checker-licm-regressions", 407 // Crashes the VM, cause is unclear. 408 "080-oom-throw" 409 ), 410 DexVm.ART_6_0_1, ImmutableList.of( 411 // Generally fails on non-R8/D8 running. 412 "004-checker-UnsafeTest18", 413 "005-annotations", 414 "008-exceptions", 415 "061-out-of-memory", 416 "082-inline-execute", 417 "099-vmdebug", 418 "412-new-array", 419 "530-checker-lse2", 420 "534-checker-bce-deoptimization", 421 "550-new-instance-clinit", 422 "580-checker-round", 423 "594-invoke-super", 424 "625-checker-licm-regressions", 425 "626-const-class-linking", 426 // Crashes the VM, cause is unclear. 427 "080-oom-throw" 428 ), 429 DexVm.ART_5_1_1, ImmutableList.of( 430 // Generally fails on non R8/D8 running. 431 "004-checker-UnsafeTest18", 432 "004-NativeAllocations", 433 "005-annotations", 434 "008-exceptions", 435 "082-inline-execute", 436 "099-vmdebug", 437 "143-string-value", 438 "530-checker-lse2", 439 "536-checker-intrinsic-optimization", 440 "552-invoke-non-existent-super", 441 "580-checker-round", 442 "580-checker-string-fact-intrinsics", 443 "594-invoke-super", 444 "605-new-string-from-bytes", 445 "626-const-class-linking" 446 ), 447 DexVm.ART_4_4_4, ImmutableList.of() 448 449 ); 450 451 // Tests where the R8/D8 output runs in Art but the original does not. 452 private static Multimap<String, TestCondition> failingRunWithArtOriginalOnly = 453 new ImmutableListMultimap.Builder<String, TestCondition>().build(); 454 455 // Tests where the output of R8 fails when run with Art. 456 private static final Multimap<String, TestCondition> failingRunWithArt = 457 new ImmutableListMultimap.Builder<String, TestCondition>() 458 // This test relies on specific field access patterns, which we rewrite. 459 .put("064-field-access", TestCondition.match(TestCondition.R8_COMPILER)) 460 // The growth limit test fails after processing by R8 because R8 will eliminate an 461 // "unneeded" const store. The following reflective call to the VM's GC will then see the 462 // large array as still live and the subsequent allocations will fail to reach the desired 463 // size before an out-of-memory error occurs. See: 464 // tests/art/{dx,jack}/104-growth-limit/src/Main.java:40 465 .put( 466 "104-growth-limit", 467 TestCondition.match(TestCondition.R8_COMPILER, TestCondition.RELEASE_MODE)) 468 .put( 469 "461-get-reference-vreg", 470 TestCondition.match( 471 TestCondition.D8_COMPILER, 472 TestCondition.runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1))) 473 // Unsatisfiable link error: 474 // libarttest.so: undefined symbol: _ZN3art6Thread18RunEmptyCheckpointEv 475 .put( 476 "543-env-long-ref", 477 TestCondition.match( 478 TestCondition.D8_COMPILER, 479 TestCondition.runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1))) 480 // Regression test for an issue that is not fixed on version 5.1.1. Throws an Exception 481 // instance instead of the expected NullPointerException. This bug is only tickled when 482 // running the R8 generated code when starting from jar or from dex code generated with 483 // dx. However, the code that R8 generates is valid and there is nothing we can do for 484 // this one. 485 .put( 486 "551-implicit-null-checks", 487 TestCondition.match( 488 TestCondition.tools(DexTool.NONE, DexTool.DX), 489 TestCondition.R8_COMPILER, 490 TestCondition.runtimes(DexVm.ART_5_1_1))) 491 // Contains a method (B.<init>) which pass too few arguments to invoke. Also, contains an 492 // iput on a static field. 493 .put( 494 "600-verifier-fails", 495 TestCondition.match( 496 TestCondition.D8_COMPILER, 497 TestCondition.runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1))) 498 .build(); 499 500 // Tests where the output of R8/D8 runs in Art but produces different output than the expected.txt 501 // checked into the Art repo. 502 private static final Multimap<String, TestCondition> failingRunWithArtOutput = 503 new ImmutableListMultimap.Builder<String, TestCondition>() 504 // This one is expected to have different output. It counts instances, but the list that 505 // keeps the instances alive is dead and could be garbage collected. The compiler reuses 506 // the register for the list and therefore there are no live instances. 507 .put("099-vmdebug", TestCondition.any()) 508 // This test relies on output on stderr, which we currently do not collect. 509 .put("143-string-value", TestCondition.any()) 510 // This one is expected to have different output. It counts instances, but the list that 511 // keeps the instances alive is dead and could be garbage collected. The compiler reuses 512 // the register for the list and therefore there are no live instances. 513 .put("099-vmdebug", TestCondition.any()) 514 // This test relies on output on stderr, which we currently do not collect. 515 .put("143-string-value", TestCondition.any()) 516 .put( 517 "800-smali", 518 TestCondition.match( 519 TestCondition.D8_COMPILER, 520 TestCondition.runtimes(DexVm.ART_5_1_1, DexVm.ART_6_0_1))) 521 // Triggers regression test in 6.0.1 when using R8/D8 in debug mode. 522 .put( 523 "474-fp-sub-neg", 524 TestCondition.match( 525 TestCondition.tools(DexTool.NONE, DexTool.JACK), 526 TestCondition.D8_COMPILER, 527 TestCondition.runtimes(DexVm.ART_6_0_1))) 528 .build(); 529 530 private static final TestCondition beforeAndroidN = 531 TestCondition 532 .match(TestCondition.runtimes(DexVm.ART_4_4_4, DexVm.ART_5_1_1, DexVm.ART_6_0_1)); 533 private static final TestCondition beforeAndroidO = 534 TestCondition.match( 535 TestCondition.runtimes( 536 DexVm.ART_4_4_4, DexVm.ART_5_1_1, DexVm.ART_6_0_1, DexVm.ART_7_0_0)); 537 538 // TODO(ager): Could we test that these fail in the way that we expect? 539 private static final Multimap<String, TestCondition> expectedToFailRunWithArt = 540 new ImmutableListMultimap.Builder<String, TestCondition>() 541 // Contains bad finalizer that times out. 542 .put("030-bad-finalizer", TestCondition.any()) 543 // Contains a direct method call with a null receiver. 544 .put("034-call-null", TestCondition.any()) 545 // Testing uncaught exceptions. 546 .put("054-uncaught", TestCondition.any()) 547 // Contains a null pointer exception. 548 .put("038-inner-null", TestCondition.any()) 549 // Null pointer exception. 550 .put("071-dexfile", TestCondition.any()) 551 // Array index out of bounds exception. 552 .put("088-monitor-verification", TestCondition.any()) 553 // Attempts to run hprof. 554 .put("130-hprof", TestCondition.any()) 555 // Null pointer exception. Test doesn't exist for dx. 556 .put("138-duplicate-classes-check", TestCondition.any()) 557 // Array index out of bounds exception. 558 .put("150-loadlibrary", TestCondition.any()) 559 // Uses dex file version 37 and therefore only runs on Android N and above. 560 .put( 561 "370-dex-v37", 562 TestCondition.match( 563 TestCondition.tools(DexTool.JACK, DexTool.DX), 564 compilers(CompilerUnderTest.R8, CompilerUnderTest.D8), 565 TestCondition.runtimes(DexVm.ART_4_4_4, DexVm.ART_5_1_1, DexVm.ART_6_0_1))) 566 // Array index out of bounds exception. 567 .put("449-checker-bce", TestCondition.any()) 568 // Fails: get_vreg_jni.cc:46] Check failed: value == 42u (value=314630384, 42u=42) 569 // The R8/D8 code does not produce values in the same registers as the tests expects in 570 // Main.testPairVReg where Main.doNativeCall is called (v1 vs v0). 571 .put( 572 "454-get-vreg", 573 TestCondition.match( 574 TestCondition.runtimes(DexVm.ART_4_4_4, DexVm.ART_5_1_1, 575 DexVm.ART_6_0_1, DexVm.ART_7_0_0))) 576 .put( 577 "454-get-vreg", 578 TestCondition.match( 579 TestCondition.tools(DexTool.NONE), 580 TestCondition.D8_COMPILER, 581 TestCondition.runtimes(DexVm.ART_DEFAULT))) 582 .put("454-get-vreg", TestCondition.match(TestCondition.R8_COMPILER)) 583 // Fails: regs_jni.cc:42] Check failed: GetVReg(m, 0, kIntVReg, &value) 584 // The R8/D8 code does not put values in the same registers as the tests expects. 585 .put( 586 "457-regs", 587 TestCondition.match( 588 TestCondition.runtimes(DexVm.ART_4_4_4, DexVm.ART_5_1_1, 589 DexVm.ART_6_0_1, DexVm.ART_7_0_0))) 590 .put( 591 "457-regs", 592 TestCondition.match( 593 TestCondition.tools(DexTool.NONE), 594 TestCondition.D8_COMPILER, 595 TestCondition.runtimes(DexVm.ART_DEFAULT))) 596 .put("457-regs", TestCondition.match(TestCondition.R8_COMPILER)) 597 // Class not found. 598 .put("529-checker-unresolved", TestCondition.any()) 599 // Fails: env_long_ref.cc:44] Check failed: GetVReg(m, 1, kReferenceVReg, &value) 600 // The R8/D8 code does not produce values in the same registers as the tests expects in 601 // the stack frame for TestCase.testCase checked by the native Main.lookForMyRegisters 602 // (v1 vs v0). 603 .put("543-env-long-ref", TestCondition.match(TestCondition.R8_COMPILER)) 604 // Array index out of bounds exception. 605 .put("555-UnsafeGetLong-regression", TestCondition.any()) 606 // Array index out of bounds exception. 607 .put("563-checker-fakestring", TestCondition.any()) 608 // Array index out of bounds exception. 609 .put("575-checker-string-init-alias", TestCondition.any()) 610 // Runtime exception. 611 .put("577-profile-foreign-dex", TestCondition.any()) 612 // Array index out of bounds exception. 613 .put("602-deoptimizeable", TestCondition.any()) 614 // Array index out of bounds exception. 615 .put("604-hot-static-interface", TestCondition.any()) 616 // Array index out of bounds exception. 617 .put("612-jit-dex-cache", TestCondition.any()) 618 // Fails: get_reference_vreg_jni.cc:43] Check failed: GetVReg(m, 1, kReferenceVReg, 619 // &value) 620 // The R8 code does not put values in the same registers as the tests expects in 621 // Main.testThisWithInstanceCall checked by the native Main.doNativeCallRef (v0 vs. v1 and 622 // only 1 register instead fof 2). 623 .put("461-get-reference-vreg", TestCondition.match(TestCondition.R8_COMPILER)) 624 // This test uses register r1 in method that is declared to only use 1 register (r0). This 625 // is in dex code which D8 does not convert. Therefore the error is a verification error 626 // at runtime and that is expected. 627 .put("142-classloader2", TestCondition.match(TestCondition.D8_COMPILER)) 628 // Invoke-custom is supported by D8 and R8, but it can only run on our newest version 629 // of art. 630 .put("952-invoke-custom", beforeAndroidO) 631 // Invoke-polymorphic is supported by D8 and R8, but it can only run on our newest version 632 // of art. 633 .put("953-invoke-polymorphic-compiler", beforeAndroidO) 634 .put("957-methodhandle-transforms", beforeAndroidO) 635 .put("958-methodhandle-stackframe", beforeAndroidO) 636 .put("959-invoke-polymorphic-accessors", beforeAndroidO) 637 .put("044-proxy", beforeAndroidN) // --min-sdk = 24 638 .put("048-reflect-v8", beforeAndroidN) // --min-sdk = 24 639 .put("962-iface-static", beforeAndroidN) // --min-sdk = 24 640 .put("964-default-iface-init-gen", beforeAndroidN) // --min-sdk = 24 641 .put("968-default-partial-compile-gen", beforeAndroidN) // --min-sdk = 24 642 .put("970-iface-super-resolution-gen", beforeAndroidN) // --min-sdk = 24 643 .put("971-iface-super", beforeAndroidN) // --min-sdk = 24 644 .put("972-default-imt-collision", beforeAndroidN) // --min-sdk = 24 645 .put("973-default-multidex", beforeAndroidN) // --min-sdk = 24 646 .put("974-verify-interface-super", beforeAndroidN) // --min-sdk = 24 647 .put("975-iface-private", beforeAndroidN) // --min-sdk = 24 648 // Uses dex file version 37 and therefore only runs on Android N and above. 649 .put("972-iface-super-multidex", 650 TestCondition.match(TestCondition.tools(DexTool.JACK, DexTool.DX), 651 TestCondition.runtimes(DexVm.ART_4_4_4, DexVm.ART_5_1_1, DexVm.ART_6_0_1))) 652 // Uses dex file version 37 and therefore only runs on Android N and above. 653 .put("978-virtual-interface", 654 TestCondition.match(TestCondition.tools(DexTool.JACK, DexTool.DX), 655 TestCondition.runtimes(DexVm.ART_4_4_4, DexVm.ART_5_1_1, DexVm.ART_6_0_1))) 656 .build(); 657 658 // Tests where code generation fails. 659 private static final Multimap<String, TestCondition> failingWithCompiler = 660 new ImmutableListMultimap.Builder<String, TestCondition>() 661 // Contains two methods with the same name and signature but different code. 662 .put("097-duplicate-method", TestCondition.any()) 663 // Contains a method (B.<init>) which pass too few arguments to invoke. Also, contains an 664 // iput on a static field. 665 .put("600-verifier-fails", TestCondition.match(TestCondition.R8_COMPILER)) 666 // Contains a method that falls off the end without a return. 667 .put("606-erroneous-class", TestCondition 668 .match(TestCondition.tools(DexTool.DX, DexTool.JACK), TestCondition.R8_COMPILER)) 669 .put("064-field-access", TestCondition 670 .match(TestCondition.tools(DexTool.NONE), TestCondition.D8_COMPILER)) 671 .build(); 672 673 // Tests that are invalid dex files and on which R8/D8 fails and that is OK. 674 private static final Multimap<String, TestCondition> expectedToFailWithCompiler = 675 new ImmutableListMultimap.Builder<String, TestCondition>() 676 // When starting from the Jar frontend we see the A$B class both from the Java source 677 // code and from the smali dex code. We reject that because there are then two definitions 678 // of the same class in the application. When running from the final dex files there is 679 // only on A$B class because of a custom build script that merges them. 680 .put("121-modifiers", TestCondition.match(TestCondition.tools(DexTool.NONE))) 681 // This test uses register r1 in method that is declared to only use 1 register (r0). 682 .put("142-classloader2", TestCondition.match(TestCondition.R8_COMPILER)) 683 // This test uses an uninitialized register. 684 .put("471-uninitialized-locals", TestCondition.match(TestCondition.R8_COMPILER)) 685 // This test is starting from invalid dex code. It splits up a double value and uses 686 // the first register of a double with the second register of another double. 687 .put("800-smali", TestCondition.match(TestCondition.R8_COMPILER)) 688 // Contains a loop in the class hierarchy. 689 .put("804-class-extends-itself", TestCondition.any()) 690 // It is not possible to compute target of method call due to ambiguous methods, thus fail 691 // to generate one dex from several dex inputs that represent an invalid program. 692 .put("004-JniTest", TestCondition.match(TestCondition.R8_COMPILER)) 693 .put("960-default-smali", TestCondition.match(TestCondition.R8_COMPILER)) 694 .put("966-default-conflict", TestCondition.match(TestCondition.R8_COMPILER)) 695 .put("972-iface-super-multidex", TestCondition.match(TestCondition.R8_COMPILER)) 696 .build(); 697 698 // Tests that does not have valid input for us to be compatible with jack/dx running. 699 private static List<String> noInputJar = ImmutableList.of( 700 "064-field-access", // Missing classes2 dir (has src2) 701 "097-duplicate-method", // No input class files. 702 "606-erroneous-class", // Based on multiple smali files. 703 "630-safecast-array", // No input class files. 704 "801-VoidCheckCast", // No input class files. 705 "804-class-extends-itself", // No input class files. 706 "972-iface-super-multidex", // Based on multiple smali files 707 "973-default-multidex", // Based on multiple smali files. 708 "974-verify-interface-super", // No input class files. 709 "975-iface-private", // No input class files. 710 "976-conflict-no-methods", // No input class files 711 "978-virtual-interface" // No input class files 712 ); 713 714 // Some JCTF test cases require test classes from other tests. These are listed here. 715 private static final Map<String, List<String>> jctfTestsExternalClassFiles = 716 new ImmutableMap.Builder<String, List<String>>() 717 .put("lang.RuntimePermission.Class.RuntimePermission_class_A13", 718 new ImmutableList.Builder<String>() 719 .add("lang/Thread/stop/Thread_stop_A02.class") 720 .add("lang/Thread/stopLjava_lang_Throwable/Thread_stop_A02.class") 721 .build()) 722 .put("lang.RuntimePermission.Class.RuntimePermission_class_A02", 723 new ImmutableList.Builder<String>() 724 .add("lang/Class/getClassLoader/Class_getClassLoader_A03.class") 725 .add("lang/ClassLoader/getParent/ClassLoader_getParent_A02.class") 726 .add("lang/Thread/getContextClassLoader/Thread_getContextClassLoader_A02.class") 727 .add("lang/Runtime/exitI/Runtime_exit_A02.class") 728 .build()) 729 .put("lang.Runtime.exitI.Runtime_exit_A03", 730 new ImmutableList.Builder<String>() 731 .add("lang/Runtime/exitI/Runtime_exit_A02.class") 732 .build()) 733 .build(); 734 735 // Tests to skip on some conditions 736 private static final Multimap<String, TestCondition> testToSkip = 737 new ImmutableListMultimap.Builder<String, TestCondition>() 738 // Old runtimes used the legacy test directory which does not contain input for tools 739 // NONE and DX. 740 .put("952-invoke-custom", TestCondition.match( 741 TestCondition.tools(DexTool.NONE, DexTool.DX), 742 TestCondition.runtimes( 743 DexVm.ART_4_4_4, DexVm.ART_5_1_1, DexVm.ART_6_0_1, DexVm.ART_7_0_0))) 744 .build(); 745 746 private static List<String> failuresToTriage = ImmutableList.of( 747 // This is flaky. 748 "104-growth-limit", 749 750 // Various failures. 751 "138-duplicate-classes-check", 752 "461-get-reference-vreg", 753 "629-vdex-speed", 754 "638-no-line-number", 755 "647-jni-get-field-id", 756 "649-vdex-duplicate-method", 757 "652-deopt-intrinsic", 758 "655-jit-clinit", 759 "656-annotation-lookup-generic-jni", 760 "656-loop-deopt", 761 "708-jit-cache-churn", 762 763 // These use "native trace". 764 "981-dedup-original-dex", 765 "982-ok-no-retransform", 766 "983-source-transform-verify", 767 "984-obsolete-invoke", 768 "985-re-obsolete", 769 "986-native-method-bind", 770 "987-agent-bind", 771 "988-method-trace", 772 "989-method-trace-throw", 773 "990-field-trace", 774 "991-field-trace-2", 775 "992-source-data", 776 "993-breakpoints", 777 "994-breakpoint-line", 778 "995-breakpoints-throw", 779 "996-breakpoint-obsolete", 780 "997-single-step", 781 782 // These two fail with missing *-hostdex.jar files. 783 "648-inline-caches-unresolved", 784 "998-redefine-use-after-free" 785 ); 786 787 private static class TestSpecification { 788 789 // Name of the Art test 790 private final String name; 791 // Directory of the test files (containing prebuild dex/jar files and expected output). 792 private final File directory; 793 // Compiler that these expectations are for dx or Jack, or none if running on class files. 794 private final DexTool dexTool; 795 // Native library to use for running this test - if any. 796 private final String nativeLibrary; 797 // Skip running this test with Art - most likely due to timeout. 798 private final boolean skipArt; 799 // Skip running this test altogether. For example, there might be no input in this configuration 800 // (e.g. no class files). 801 private final boolean skipTest; 802 // Fails compilation - most likely throws an exception. 803 private final boolean failsWithX8; 804 // Expected to fail compilation with a CompilationError. 805 private final boolean expectedToFailWithX8; 806 // Fails running the output in Art with an assertion error. Typically due to verification 807 // errors. 808 private final boolean failsWithArt; 809 // Runs in art but fails the run because it produces different results. 810 private final boolean failsWithArtOutput; 811 // Original fails in art but the R8/D8 version can run in art. 812 private final boolean failsWithArtOriginalOnly; 813 // Test might produce different outputs. 814 private final boolean outputMayDiffer; 815 TestSpecification(String name, DexTool dexTool, File directory, boolean skipArt, boolean skipTest, boolean failsWithX8, boolean failsWithArt, boolean failsWithArtOutput, boolean failsWithArtOriginalOnly, String nativeLibrary, boolean expectedToFailWithX8, boolean outputMayDiffer)816 TestSpecification(String name, DexTool dexTool, 817 File directory, boolean skipArt, boolean skipTest, boolean failsWithX8, 818 boolean failsWithArt, boolean failsWithArtOutput, boolean failsWithArtOriginalOnly, 819 String nativeLibrary, boolean expectedToFailWithX8, boolean outputMayDiffer) { 820 this.name = name; 821 this.dexTool = dexTool; 822 this.nativeLibrary = nativeLibrary; 823 this.directory = directory; 824 this.skipArt = skipArt; 825 this.skipTest = skipTest; 826 this.failsWithX8 = failsWithX8; 827 this.failsWithArt = failsWithArt; 828 this.failsWithArtOutput = failsWithArtOutput; 829 this.failsWithArtOriginalOnly = failsWithArtOriginalOnly; 830 this.expectedToFailWithX8 = expectedToFailWithX8; 831 this.outputMayDiffer = outputMayDiffer; 832 } 833 TestSpecification(String name, DexTool dexTool, File directory, boolean skipArt, boolean failsWithArt)834 TestSpecification(String name, DexTool dexTool, File directory, boolean skipArt, 835 boolean failsWithArt) { 836 this(name, dexTool, directory, skipArt, 837 false, false, failsWithArt, false, false, null, false, false); 838 } 839 resolveFile(String name)840 public File resolveFile(String name) { 841 return directory.toPath().resolve(name).toFile(); 842 } 843 toString()844 public String toString() { 845 return name + " (" + dexTool + ")"; 846 } 847 } 848 849 private static class SpecificationKey { 850 851 private final String name; 852 private final DexTool toolchain; 853 SpecificationKey(String name, DexTool toolchain)854 private SpecificationKey(String name, DexTool toolchain) { 855 assert name != null; 856 this.name = name; 857 this.toolchain = toolchain; 858 } 859 860 @Override equals(Object o)861 public boolean equals(Object o) { 862 if (!(o instanceof SpecificationKey)) { 863 return false; 864 } 865 866 SpecificationKey that = (SpecificationKey) o; 867 return name.equals(that.name) && toolchain == that.toolchain; 868 } 869 870 @Override hashCode()871 public int hashCode() { 872 return 31 * name.hashCode() + toolchain.hashCode(); 873 } 874 } 875 collectTestsMatchingConditions( DexTool dexTool, CompilerUnderTest compilerUnderTest, DexVm dexVm, CompilationMode mode, Multimap<String, TestCondition> testConditionsMap)876 private static Set<String> collectTestsMatchingConditions( 877 DexTool dexTool, 878 CompilerUnderTest compilerUnderTest, 879 DexVm dexVm, 880 CompilationMode mode, 881 Multimap<String, TestCondition> testConditionsMap) { 882 Set<String> set = Sets.newHashSet(); 883 for (Map.Entry<String, TestCondition> kv : testConditionsMap.entries()) { 884 if (kv.getValue().test(dexTool, compilerUnderTest, dexVm, mode)) { 885 set.add(kv.getKey()); 886 } 887 } 888 return set; 889 } 890 getTestsMap( CompilerUnderTest compilerUnderTest, CompilationMode compilationMode, DexVm version)891 private static Map<SpecificationKey, TestSpecification> getTestsMap( 892 CompilerUnderTest compilerUnderTest, CompilationMode compilationMode, DexVm version) { 893 File artTestDir = new File(ART_TESTS_DIR); 894 if (version != DexVm.ART_DEFAULT) { 895 artTestDir = new File(ART_LEGACY_TESTS_DIR); 896 } 897 if (!artTestDir.exists()) { 898 // Don't run any tests if the directory does not exist. 899 return Collections.emptyMap(); 900 } 901 Map<SpecificationKey, TestSpecification> data = new HashMap<>(); 902 903 // Collect tests where running Art is skipped (we still run R8/D8 on these). 904 Set<String> skipArt = Sets.newHashSet(flakyRunWithArt); 905 skipArt.addAll(customRun); 906 907 Set<String> skipTest = Sets.newHashSet(skipAltogether); 908 skipTest.addAll(usesNativeAgentCode); 909 skipTest.addAll(failuresToTriage); 910 911 // Collect the tests requiring the native library. 912 Set<String> useNativeLibrary = Sets.newHashSet(useJNI); 913 914 DexVm dexVm = ToolHelper.getDexVm(); 915 for (DexTool dexTool : DexTool.values()) { 916 // Collect the tests failing code generation. 917 Set<String> failsWithCompiler = 918 collectTestsMatchingConditions( 919 dexTool, compilerUnderTest, dexVm, compilationMode, failingWithCompiler); 920 921 // Collect tests that has no input: 922 if (dexTool == DexTool.NONE) { 923 skipTest.addAll(noInputJar); 924 } 925 926 // Collect the test that we should skip in this configuration 927 skipTest.addAll(collectTestsMatchingConditions( 928 dexTool, compilerUnderTest, dexVm, compilationMode, testToSkip)); 929 930 // Collect the test that we should skip in this configuration. 931 skipArt.addAll( 932 collectTestsMatchingConditions( 933 dexTool, compilerUnderTest, dexVm, compilationMode, timeoutOrSkipRunWithArt)); 934 935 // Collect the tests failing to run in Art (we still run R8/D8 on these). 936 Set<String> failsWithArt = 937 collectTestsMatchingConditions( 938 dexTool, compilerUnderTest, dexVm, compilationMode, failingRunWithArt); 939 { 940 Set<String> tmpSet = 941 collectTestsMatchingConditions( 942 dexTool, compilerUnderTest, dexVm, compilationMode, expectedToFailRunWithArt); 943 failsWithArt.addAll(tmpSet); 944 } 945 946 if (!ToolHelper.isDefaultDexVm()) { 947 // Generally failing when not TOT art. 948 failsWithArt.addAll(expectedToFailRunWithArtNonDefault); 949 // Version specific failures 950 failsWithArt.addAll(expectedToFailRunWithArtVersion.get(ToolHelper.getDexVm())); 951 } 952 953 // Collect the tests failing with output differences in Art. 954 Set<String> failsRunWithArtOutput = 955 collectTestsMatchingConditions( 956 dexTool, compilerUnderTest, dexVm, compilationMode, failingRunWithArtOutput); 957 Set<String> expectedToFailWithCompilerSet = 958 collectTestsMatchingConditions( 959 dexTool, compilerUnderTest, dexVm, compilationMode, expectedToFailWithCompiler); 960 961 // Collect the tests where the original works in Art and the R8/D8 generated output does not. 962 Set<String> failsRunWithArtOriginalOnly = 963 collectTestsMatchingConditions( 964 dexTool, compilerUnderTest, dexVm, compilationMode, failingRunWithArtOriginalOnly); 965 966 File compilerTestDir = artTestDir.toPath().resolve(dexToolDirectory(dexTool)).toFile(); 967 File[] testDirs = compilerTestDir.listFiles(); 968 assert testDirs != null; 969 for (File testDir : testDirs) { 970 String name = testDir.getName(); 971 // All the native code for all Art tests is currently linked into the 972 // libarttest.so file. 973 data.put( 974 new SpecificationKey(name, dexTool), 975 new TestSpecification(name, dexTool, testDir, skipArt.contains(name), 976 skipTest.contains(name), 977 failsWithCompiler.contains(name), 978 failsWithArt.contains(name), 979 failsRunWithArtOutput.contains(name), 980 failsRunWithArtOriginalOnly.contains(name), 981 useNativeLibrary.contains(name) ? "arttest" : null, 982 expectedToFailWithCompilerSet.contains(name), 983 outputMayDiffer.contains(name))); 984 } 985 } 986 return data; 987 } 988 defaultCompilationMode(CompilerUnderTest compilerUnderTest)989 private static CompilationMode defaultCompilationMode(CompilerUnderTest compilerUnderTest) { 990 CompilationMode compilationMode = null; 991 switch (compilerUnderTest) { 992 case R8: 993 compilationMode = CompilationMode.RELEASE; 994 break; 995 case D8: 996 case R8_AFTER_D8: 997 compilationMode = CompilationMode.DEBUG; 998 break; 999 default: 1000 throw new RuntimeException("Unreachable."); 1001 } 1002 return compilationMode; 1003 } 1004 dexToolDirectory(DexTool tool)1005 private static String dexToolDirectory(DexTool tool) { 1006 // DexTool.NONE uses class files in the dx directory. 1007 return tool == DexTool.JACK ? "jack" : "dx"; 1008 } 1009 1010 @Rule 1011 public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest(); 1012 R8RunArtTestsTest(String name, DexTool toolchain)1013 public R8RunArtTestsTest(String name, DexTool toolchain) { 1014 this.name = name; 1015 this.toolchain = toolchain; 1016 } 1017 buildArtCommand( File dexFile, TestSpecification specification, DexVm artVersion)1018 private ArtCommandBuilder buildArtCommand( 1019 File dexFile, TestSpecification specification, DexVm artVersion) { 1020 ArtCommandBuilder builder = new ArtCommandBuilder(artVersion); 1021 builder.appendClasspath(dexFile.toString()); 1022 // All Art tests have the main class Main. 1023 builder.setMainClass("Main"); 1024 if (specification.nativeLibrary != null) { 1025 // All the native libraries for all Art tests is in the same directory. 1026 File artTestNativeLibraryDir = new File(ART_TESTS_NATIVE_LIBRARY_DIR); 1027 if (artVersion != DexVm.ART_DEFAULT) { 1028 artTestNativeLibraryDir = new File(ART_LEGACY_TESTS_NATIVE_LIBRARY_DIR); 1029 } 1030 builder.appendArtSystemProperty( 1031 "java.library.path", 1032 artTestNativeLibraryDir.getAbsolutePath()); 1033 builder.appendProgramArgument(specification.nativeLibrary); 1034 } 1035 return builder; 1036 } 1037 runArtTest()1038 protected void runArtTest() throws Throwable { 1039 // Use the default dex VM specified. 1040 runArtTest(ToolHelper.getDexVm(), CompilerUnderTest.R8); 1041 } 1042 runArtTest(CompilerUnderTest compilerUnderTest)1043 protected void runArtTest(CompilerUnderTest compilerUnderTest) throws Throwable { 1044 // Use the default dex VM specified. 1045 runArtTest(ToolHelper.getDexVm(), compilerUnderTest); 1046 } 1047 executeCompilerUnderTest( CompilerUnderTest compilerUnderTest, Collection<String> fileNames, String resultPath, CompilationMode compilationMode)1048 private void executeCompilerUnderTest( 1049 CompilerUnderTest compilerUnderTest, 1050 Collection<String> fileNames, 1051 String resultPath, 1052 CompilationMode compilationMode) 1053 throws IOException, ProguardRuleParserException, ExecutionException, CompilationException { 1054 executeCompilerUnderTest(compilerUnderTest, fileNames, resultPath, compilationMode, null); 1055 } 1056 executeCompilerUnderTest( CompilerUnderTest compilerUnderTest, Collection<String> fileNames, String resultPath, CompilationMode mode, String keepRulesFile)1057 private void executeCompilerUnderTest( 1058 CompilerUnderTest compilerUnderTest, 1059 Collection<String> fileNames, 1060 String resultPath, 1061 CompilationMode mode, 1062 String keepRulesFile) 1063 throws IOException, ProguardRuleParserException, ExecutionException, CompilationException { 1064 assert mode != null; 1065 switch (compilerUnderTest) { 1066 case D8: 1067 { 1068 assert keepRulesFile == null : "Keep-rules file specified for D8."; 1069 D8Command.Builder builder = 1070 D8Command.builder() 1071 .setMode(mode) 1072 .addProgramFiles(ListUtils.map(fileNames, Paths::get)); 1073 Integer minSdkVersion = needMinSdkVersion.get(name); 1074 if (minSdkVersion != null) { 1075 builder.setMinApiLevel(minSdkVersion); 1076 } 1077 D8Output output = D8.run(builder.build()); 1078 output.write(Paths.get(resultPath)); 1079 break; 1080 } 1081 case R8: 1082 { 1083 R8Command.Builder builder = 1084 R8Command.builder() 1085 .setMode(mode) 1086 .setOutputPath(Paths.get(resultPath)) 1087 .addProgramFiles(ListUtils.map(fileNames, Paths::get)) 1088 .setIgnoreMissingClasses(true); 1089 Integer minSdkVersion = needMinSdkVersion.get(name); 1090 if (minSdkVersion != null) { 1091 builder.setMinApiLevel(minSdkVersion); 1092 } 1093 if (keepRulesFile != null) { 1094 builder.addProguardConfigurationFiles(Paths.get(keepRulesFile)); 1095 } 1096 // Add internal flags for testing purposes. 1097 ToolHelper.runR8( 1098 builder.build(), 1099 options -> { 1100 if (enableInterfaceMethodDesugaring.contains(name)) { 1101 options.interfaceMethodDesugaring = OffOrAuto.Auto; 1102 } 1103 }); 1104 break; 1105 } 1106 default: 1107 assert false : compilerUnderTest; 1108 } 1109 } 1110 setDefaultArgs(R8Command.Builder builder)1111 private static R8Command.Builder setDefaultArgs(R8Command.Builder builder) { 1112 return builder.setMinification(false); 1113 } 1114 isAuxClassFile(String fileName, String auxClassFileBase)1115 private static boolean isAuxClassFile(String fileName, String auxClassFileBase) { 1116 return fileName.endsWith(".class") 1117 && (fileName.startsWith(auxClassFileBase + "$") 1118 || fileName.startsWith(auxClassFileBase + "_")); 1119 } 1120 1121 getJctfTestAuxClassFiles(File classFile)1122 private ArrayList<File> getJctfTestAuxClassFiles(File classFile) { 1123 // Collect additional files from the same directory with file names like 1124 // <dir>/<filename_wo_ext>$*.class and <dir>/<filename_wo_ext>_*.class 1125 String classFileString = classFile.toString(); 1126 assert classFileString.endsWith(".class"); 1127 1128 String auxClassFileBase = 1129 new File( 1130 classFileString.substring(0, classFileString.length() - ".class".length())) 1131 .getName(); 1132 1133 ArrayList<File> auxClassFiles = new ArrayList<>(); 1134 1135 File[] files = classFile.getParentFile() 1136 .listFiles( 1137 (File file) -> isAuxClassFile(file.getName(), auxClassFileBase)); 1138 if (files != null) { 1139 auxClassFiles.addAll(Arrays.asList(files)); 1140 } 1141 1142 if (auxClassFileBase.matches(".*[A-Z]\\d\\d")) { 1143 // Also collect all the files in this directory that doesn't match this pattern 1144 // They will be helper classes defined in one of the test class files but we don't know in 1145 // which one, so we just add them to all tests. 1146 final int SUFFIX_LENGTH_TO_STRIP = 3; // one letter (usually 'A' and two digits) 1147 String testClassFilePattern = 1148 auxClassFileBase.substring(0, auxClassFileBase.length() - SUFFIX_LENGTH_TO_STRIP) 1149 + "[A-Z]\\d\\d.*\\.class"; 1150 files = classFile.getParentFile() 1151 .listFiles( 1152 (File file) -> file.getName().matches(".*\\.class") && !file.getName() 1153 .matches(testClassFilePattern)); 1154 if (files != null) { 1155 auxClassFiles.addAll(Arrays.asList(files)); 1156 } 1157 } 1158 1159 return auxClassFiles; 1160 } 1161 runJctfTest(CompilerUnderTest compilerUnderTest, String classFilePath, String fullClassName)1162 protected void runJctfTest(CompilerUnderTest compilerUnderTest, String classFilePath, 1163 String fullClassName) 1164 throws IOException, ProguardRuleParserException, ExecutionException, CompilationException { 1165 1166 DexVm dexVm = ToolHelper.getDexVm(); 1167 1168 CompilerUnderTest firstCompilerUnderTest = 1169 compilerUnderTest == CompilerUnderTest.R8_AFTER_D8 1170 ? CompilerUnderTest.D8 1171 : compilerUnderTest; 1172 CompilationMode compilationMode = defaultCompilationMode(compilerUnderTest); 1173 1174 File resultDir = temp.newFolder(firstCompilerUnderTest.toString().toLowerCase() + "-output"); 1175 1176 JctfTestSpecifications.Outcome expectedOutcome = 1177 JctfTestSpecifications.getExpectedOutcome( 1178 name, firstCompilerUnderTest, dexVm, compilationMode); 1179 TestSpecification specification = new TestSpecification(name, DexTool.NONE, resultDir, 1180 expectedOutcome == JctfTestSpecifications.Outcome.TIMEOUTS_WITH_ART 1181 || expectedOutcome == JctfTestSpecifications.Outcome.FLAKY_WITH_ART, 1182 expectedOutcome == JctfTestSpecifications.Outcome.FAILS_WITH_ART); 1183 1184 if (specification.skipTest) { 1185 return; 1186 } 1187 1188 File classFile = new File(JCTF_TESTS_PREFIX + "/" + classFilePath); 1189 if (!classFile.exists()) { 1190 throw new FileNotFoundException( 1191 "Class file for Jctf test not found: \"" + classFile.toString() + "\"."); 1192 } 1193 1194 ArrayList<File> classFiles = new ArrayList<>(); 1195 classFiles.add(classFile); 1196 1197 // some tests need files from other tests 1198 int langIndex = fullClassName.indexOf(".java."); 1199 assert langIndex >= 0; 1200 List<String> externalClassFiles = jctfTestsExternalClassFiles 1201 .get(fullClassName.substring(langIndex + ".java.".length())); 1202 1203 if (externalClassFiles != null) { 1204 for (String s : externalClassFiles) { 1205 classFiles.add(new File(JCTF_TESTS_LIB_PREFIX + "/java/" + s)); 1206 } 1207 } 1208 1209 ArrayList<File> allClassFiles = new ArrayList<>(); 1210 1211 for (File f : classFiles) { 1212 allClassFiles.add(f); 1213 allClassFiles.addAll(getJctfTestAuxClassFiles(f)); 1214 } 1215 1216 File jctfCommonFile = new File(JCTF_COMMON_JAR); 1217 if (!jctfCommonFile.exists()) { 1218 throw new FileNotFoundException( 1219 "Jar file of Jctf tests common code not found: \"" + jctfCommonFile.toString() + "\"."); 1220 } 1221 1222 File junitFile = new File(JUNIT_JAR); 1223 if (!junitFile.exists()) { 1224 throw new FileNotFoundException( 1225 "Junit Jar not found: \"" + junitFile.toString() + "\"."); 1226 } 1227 1228 File hamcrestFile = new File(HAMCREST_JAR); 1229 if (!hamcrestFile.exists()) { 1230 throw new FileNotFoundException( 1231 "Hamcrest Jar not found: \"" + hamcrestFile.toString() + "\"."); 1232 } 1233 1234 // allClassFiles may contain duplicated files, that's why the HashSet 1235 Set<String> fileNames = new HashSet<>(); 1236 1237 fileNames.addAll(Arrays.asList( 1238 jctfCommonFile.getCanonicalPath(), 1239 junitFile.getCanonicalPath(), 1240 hamcrestFile.getCanonicalPath() 1241 )); 1242 1243 for (File f : allClassFiles) { 1244 fileNames.add(f.getCanonicalPath()); 1245 } 1246 1247 runJctfTestDoRunOnArt( 1248 fileNames, 1249 specification, 1250 firstCompilerUnderTest, 1251 fullClassName, 1252 compilationMode, 1253 dexVm, 1254 resultDir); 1255 1256 // second pass if D8_R8Debug 1257 if (compilerUnderTest == CompilerUnderTest.R8_AFTER_D8) { 1258 List<String> d8OutputFileNames = 1259 Files.list(resultDir.toPath()) 1260 .filter(FileUtils::isDexFile) 1261 .map(Path::toString) 1262 .collect(Collectors.toList()); 1263 File r8ResultDir = temp.newFolder("r8-output"); 1264 compilationMode = CompilationMode.DEBUG; 1265 expectedOutcome = 1266 JctfTestSpecifications.getExpectedOutcome( 1267 name, CompilerUnderTest.R8_AFTER_D8, dexVm, compilationMode); 1268 specification = new TestSpecification(name, DexTool.DX, r8ResultDir, 1269 expectedOutcome == JctfTestSpecifications.Outcome.TIMEOUTS_WITH_ART 1270 || expectedOutcome == JctfTestSpecifications.Outcome.FLAKY_WITH_ART, 1271 expectedOutcome == JctfTestSpecifications.Outcome.FAILS_WITH_ART); 1272 if (specification.skipTest) { 1273 return; 1274 } 1275 runJctfTestDoRunOnArt( 1276 d8OutputFileNames, 1277 specification, 1278 CompilerUnderTest.R8, 1279 fullClassName, 1280 compilationMode, 1281 dexVm, 1282 r8ResultDir); 1283 } 1284 } 1285 runJctfTestDoRunOnArt( Collection<String> fileNames, TestSpecification specification, CompilerUnderTest compilerUnderTest, String fullClassName, CompilationMode mode, DexVm dexVm, File resultDir)1286 private void runJctfTestDoRunOnArt( 1287 Collection<String> fileNames, 1288 TestSpecification specification, 1289 CompilerUnderTest compilerUnderTest, 1290 String fullClassName, 1291 CompilationMode mode, 1292 DexVm dexVm, 1293 File resultDir) 1294 throws IOException, ProguardRuleParserException, ExecutionException, CompilationException { 1295 executeCompilerUnderTest(compilerUnderTest, fileNames, resultDir.getAbsolutePath(), mode); 1296 1297 if (!ToolHelper.artSupported()) { 1298 return; 1299 } 1300 1301 File processedFile; 1302 1303 // Collect the generated dex files. 1304 File[] outputFiles = 1305 resultDir.listFiles((File file) -> file.getName().endsWith(".dex")); 1306 if (outputFiles.length == 1) { 1307 // Just run Art on classes.dex. 1308 processedFile = outputFiles[0]; 1309 } else { 1310 // Run Art on JAR file with multiple dex files. 1311 processedFile 1312 = temp.getRoot().toPath().resolve(specification.name + ".jar").toFile(); 1313 JarBuilder.buildJar(outputFiles, processedFile); 1314 } 1315 1316 boolean compileOnly = System.getProperty("jctf_compile_only", "0").equals("1"); 1317 if (compileOnly || specification.skipArt) { 1318 // verify dex code instead of running it 1319 Path oatFile = temp.getRoot().toPath().resolve("all.oat"); 1320 ToolHelper.runDex2Oat(processedFile.toPath(), oatFile); 1321 return; 1322 } 1323 1324 ArtCommandBuilder builder = buildArtCommand(processedFile, specification, dexVm); 1325 builder.appendArtOption("-Ximage:/system/non/existent/jdwp/image.art"); 1326 for (String s : ToolHelper.getArtBootLibs()) { 1327 builder.appendBootClassPath(new File(s).getCanonicalPath()); 1328 } 1329 builder.setMainClass(JUNIT_TEST_RUNNER); 1330 builder.appendProgramArgument(fullClassName); 1331 1332 if (specification.failsWithArt) { 1333 thrown.expect(AssertionError.class); 1334 } 1335 1336 try { 1337 ToolHelper.runArt(builder); 1338 } catch (AssertionError e) { 1339 addDexInformationToVerificationError(fileNames, processedFile, 1340 specification.resolveFile("classes.dex"), e); 1341 throw e; 1342 } 1343 if (specification.failsWithArt) { 1344 System.err.println("Should have failed run with art"); 1345 return; 1346 } 1347 } 1348 runArtTest(DexVm version, CompilerUnderTest compilerUnderTest)1349 protected void runArtTest(DexVm version, CompilerUnderTest compilerUnderTest) 1350 throws Throwable { 1351 CompilerUnderTest firstCompilerUnderTest = 1352 compilerUnderTest == CompilerUnderTest.R8_AFTER_D8 1353 ? CompilerUnderTest.D8 1354 : compilerUnderTest; 1355 1356 CompilationMode compilationMode = defaultCompilationMode(compilerUnderTest); 1357 1358 TestSpecification specification = 1359 getTestsMap(firstCompilerUnderTest, compilationMode, version) 1360 .get(new SpecificationKey(name, toolchain)); 1361 1362 if (specification == null) { 1363 if (version == DexVm.ART_DEFAULT) { 1364 throw new RuntimeException("Test " + name + " has no specification for toolchain" 1365 + toolchain + "."); 1366 } else { 1367 // For older VMs the test might not exist, as the tests are currently generates from the 1368 // directories present in the art test directory for AOSP master. 1369 return; 1370 } 1371 } 1372 1373 if (specification.skipTest) { 1374 return; 1375 } 1376 1377 File[] inputFiles; 1378 if (toolchain == DexTool.NONE) { 1379 File classes = new File(specification.directory, "classes"); 1380 inputFiles = 1381 com.google.common.io.Files.fileTreeTraverser().breadthFirstTraversal(classes).filter( 1382 (File f) -> !f.isDirectory()).toArray(File.class); 1383 File smali = new File(specification.directory, "smali"); 1384 if (smali.exists()) { 1385 File smaliDex = new File(smali, "out.dex"); 1386 assert smaliDex.exists(); 1387 inputFiles = ObjectArrays.concat(inputFiles, smaliDex); 1388 } 1389 File classes2 = new File(specification.directory, "classes2"); 1390 if (classes2.exists()) { 1391 inputFiles = ObjectArrays.concat(inputFiles, 1392 com.google.common.io.Files.fileTreeTraverser().breadthFirstTraversal(classes2).filter( 1393 (File f) -> !f.isDirectory()).toArray(File.class), File.class); 1394 } 1395 } else { 1396 inputFiles = 1397 specification.directory.listFiles((File file) -> file.getName().endsWith(".dex")); 1398 } 1399 List<String> fileNames = new ArrayList<>(); 1400 for (File file : inputFiles) { 1401 fileNames.add(file.getCanonicalPath()); 1402 } 1403 1404 File resultDir = temp.newFolder(firstCompilerUnderTest.toString().toLowerCase() + "-output"); 1405 1406 runArtTestDoRunOnArt( 1407 version, firstCompilerUnderTest, specification, fileNames, resultDir, compilationMode); 1408 1409 if (compilerUnderTest == CompilerUnderTest.R8_AFTER_D8) { 1410 compilationMode = CompilationMode.DEBUG; 1411 specification = 1412 getTestsMap(CompilerUnderTest.R8_AFTER_D8, compilationMode, version) 1413 .get(new SpecificationKey(name, DexTool.DX)); 1414 1415 if (specification == null) { 1416 throw new RuntimeException( 1417 "Test " + name + " has no specification for toolchain" + toolchain + "."); 1418 } 1419 1420 if (specification.skipTest) { 1421 return; 1422 } 1423 1424 fileNames.clear(); 1425 for (File file : resultDir.listFiles((File file) -> file.getName().endsWith(".dex"))) { 1426 fileNames.add(file.getCanonicalPath()); 1427 } 1428 1429 resultDir = temp.newFolder("r8-output"); 1430 1431 runArtTestDoRunOnArt( 1432 version, CompilerUnderTest.R8, specification, fileNames, resultDir, compilationMode); 1433 } 1434 } 1435 runArtTestDoRunOnArt( DexVm version, CompilerUnderTest compilerUnderTest, TestSpecification specification, List<String> fileNames, File resultDir, CompilationMode compilationMode)1436 private void runArtTestDoRunOnArt( 1437 DexVm version, 1438 CompilerUnderTest compilerUnderTest, 1439 TestSpecification specification, 1440 List<String> fileNames, 1441 File resultDir, 1442 CompilationMode compilationMode) 1443 throws Throwable { 1444 if (specification.expectedToFailWithX8) { 1445 thrown.expect(CompilationError.class); 1446 try { 1447 executeCompilerUnderTest( 1448 compilerUnderTest, fileNames, resultDir.getCanonicalPath(), compilationMode, null); 1449 } catch (CompilationException e) { 1450 throw new CompilationError(e.getMessage(), e); 1451 } catch (ExecutionException e) { 1452 throw e.getCause(); 1453 } 1454 System.err.println("Should have failed R8/D8 compilation with a CompilationError."); 1455 return; 1456 } else if (specification.failsWithX8) { 1457 thrown.expect(Throwable.class); 1458 executeCompilerUnderTest( 1459 compilerUnderTest, fileNames, resultDir.getCanonicalPath(), compilationMode); 1460 System.err.println("Should have failed R8/D8 compilation with an exception."); 1461 return; 1462 } else { 1463 executeCompilerUnderTest( 1464 compilerUnderTest, fileNames, resultDir.getCanonicalPath(), compilationMode); 1465 } 1466 1467 if (!specification.skipArt && ToolHelper.artSupported()) { 1468 File originalFile; 1469 File processedFile; 1470 1471 // Collect the generated dex files. 1472 File[] outputFiles = 1473 resultDir.listFiles((File file) -> file.getName().endsWith(".dex")); 1474 if (outputFiles.length == 1) { 1475 // Just run Art on classes.dex. 1476 processedFile = outputFiles[0]; 1477 } else { 1478 // Run Art on JAR file with multiple dex files. 1479 processedFile 1480 = temp.getRoot().toPath().resolve(specification.name + ".jar").toFile(); 1481 JarBuilder.buildJar(outputFiles, processedFile); 1482 } 1483 1484 File expectedFile = specification.resolveFile("expected.txt"); 1485 String expected = com.google.common.io.Files.toString(expectedFile, Charsets.UTF_8); 1486 if (specification.failsWithArt) { 1487 thrown.expect(AssertionError.class); 1488 } 1489 1490 ArtCommandBuilder builder = buildArtCommand(processedFile, specification, version); 1491 String output; 1492 try { 1493 output = ToolHelper.runArt(builder); 1494 } catch (AssertionError e) { 1495 addDexInformationToVerificationError(fileNames, processedFile, 1496 specification.resolveFile("classes.dex"), e); 1497 throw e; 1498 } 1499 if (specification.failsWithArt) { 1500 System.err.println("Should have failed run with art"); 1501 return; 1502 } 1503 1504 File checkCommand = specification.resolveFile("check"); 1505 if (checkCommand.exists()) { 1506 // Run the Art test custom check command. 1507 File actualFile = temp.newFile(); 1508 com.google.common.io.Files.asByteSink(actualFile).write(output.getBytes(Charsets.UTF_8)); 1509 ProcessBuilder processBuilder = new ProcessBuilder(); 1510 processBuilder.command( 1511 specification.resolveFile("check").toString(), expectedFile.toString(), 1512 actualFile.toString()); 1513 ProcessResult result = ToolHelper.runProcess(processBuilder); 1514 if (result.exitCode != 0 && !specification.failsWithArtOutput) { 1515 System.err.println("ERROR: check script failed. Building comparison of dex files"); 1516 failWithDexDiff(specification.resolveFile("classes.dex"), processedFile); 1517 } 1518 } else { 1519 if (!expected.equals(output)) { 1520 // The expected.txt in the Android repository might not match what our version of Art 1521 // produces. 1522 originalFile = specification.resolveFile(specification.name + ".jar"); 1523 if (specification.failsWithArtOriginalOnly) { 1524 thrown.expect(AssertionError.class); 1525 } 1526 builder = buildArtCommand(originalFile, specification, version); 1527 expected = ToolHelper.runArt(builder); 1528 if (specification.failsWithArtOriginalOnly) { 1529 System.err.println("Original file should have failed run with art"); 1530 return; 1531 } 1532 } 1533 if (specification.failsWithArtOutput) { 1534 thrown.expect(ComparisonFailure.class); 1535 } 1536 if (!specification.outputMayDiffer) { 1537 assertEquals(expected, output); 1538 } 1539 } 1540 } 1541 } 1542 failWithDexDiff(File originalFile, File processedFile)1543 private void failWithDexDiff(File originalFile, File processedFile) 1544 throws IOException, ExecutionException { 1545 DexInspector inspectOriginal = 1546 new DexInspector(originalFile.toPath().toAbsolutePath()); 1547 DexInspector inspectProcessed = 1548 new DexInspector(processedFile.toPath().toAbsolutePath()); 1549 StringBuilder builderOriginal = new StringBuilder(); 1550 StringBuilder builderProcessed = new StringBuilder(); 1551 inspectOriginal.forAllClasses((clazz) -> builderOriginal.append(clazz.dumpMethods())); 1552 inspectProcessed.forAllClasses((clazz) -> builderProcessed.append(clazz.dumpMethods())); 1553 assertEquals(builderOriginal.toString(), builderProcessed.toString()); 1554 fail(); 1555 } 1556 addDexInformationToVerificationError( Collection<String> inputFiles, File processedFile, File referenceFile, AssertionError verificationError)1557 private void addDexInformationToVerificationError( 1558 Collection<String> inputFiles, File processedFile, File referenceFile, 1559 AssertionError verificationError) { 1560 List<ComparisonFailure> errors; 1561 try { 1562 // Parse all the verification errors. 1563 DexInspector processed = new DexInspector(processedFile.toPath()); 1564 DexInspector original = DEX_COMPARE_WITH_DEX_REFERENCE_ON_FAILURE 1565 ? new DexInspector(referenceFile.toPath()) 1566 : new DexInspector(inputFiles.stream().map(Paths::get).collect(Collectors.toList())); 1567 List<ArtErrorInfo> errorInfo = ArtErrorParser.parse(verificationError.getMessage()); 1568 errors = ListUtils.map(errorInfo, (error) -> 1569 new ComparisonFailure( 1570 error.getMessage(), 1571 "ORIGINAL\n" + error.dump(original, false) + "\nEND ORIGINAL", 1572 "PROCESSED\n" + error.dump(processed, true) + "\nEND PROCESSED")); 1573 } catch (Throwable e) { 1574 System.err.println("Failed to add extra dex information to the verification error:"); 1575 e.printStackTrace(); 1576 throw verificationError; 1577 } 1578 1579 // If we failed to annotate anything, rethrow the original exception. 1580 if (errors.isEmpty()) { 1581 throw verificationError; 1582 } 1583 1584 // Otherwise, we print each error and throw the last one, since Intellij only supports nice 1585 // comparison-diff if thrown and we can only throw one :-( 1586 System.err.println(verificationError.getMessage()); 1587 for (ComparisonFailure error : errors.subList(0, errors.size() - 1)) { 1588 System.err.println(error.toString()); 1589 } 1590 throw errors.get(errors.size() - 1); 1591 } 1592 } 1593