• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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