Merge "Update nullability tests for local type analysis."
diff --git a/.gitignore b/.gitignore
index 0661690..b66a429 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,6 +20,8 @@
tools/*/art-7.0.0.tar.gz
tools/*/dalvik
tools/*/dalvik.tar.gz
+tools/*/dalvik-4.0.4
+tools/*/dalvik-4.0.4.tar.gz
tools/*/dx
tools/*/dx.tar.gz
third_party/android_jar/lib
diff --git a/build.gradle b/build.gradle
index 418474a..2d98276 100644
--- a/build.gradle
+++ b/build.gradle
@@ -271,6 +271,8 @@
"android_jar/lib-v15",
"android_jar/lib-v19",
"android_jar/lib-v21",
+ "android_jar/lib-v22",
+ "android_jar/lib-v23",
"android_jar/lib-v24",
"android_jar/lib-v25",
"android_jar/lib-v26",
@@ -292,6 +294,7 @@
"linux/art-6.0.1",
"linux/art-7.0.0",
"linux/dalvik",
+ "linux/dalvik-4.0.4",
"${osString}/dx",
]
]
@@ -709,6 +712,18 @@
destinationDir file('tests')
}
+task buildD8ApiUsageSample(type: Jar) {
+ from sourceSets.apiUsageSample.output
+ baseName 'd8_api_usage_sample'
+ destinationDir file('tests')
+}
+
+task buildR8ApiUsageSample(type: Jar) {
+ from sourceSets.apiUsageSample.output
+ baseName 'r8_api_usage_sample'
+ destinationDir file('tests')
+}
+
task buildDebugInfoExamplesDex {
def examplesDir = file("src/test/java")
def hostJar = "debuginfo_examples.jar"
@@ -1667,22 +1682,58 @@
}
task javadocD8(type: Javadoc) {
+ title "D8 API"
classpath = sourceSets.main.compileClasspath
source = sourceSets.main.allJava
include '**/com/android/tools/r8/ArchiveClassFileProvider.java'
+ include '**/com/android/tools/r8/ArchiveProgramResourceProvider.java'
include '**/com/android/tools/r8/BaseCommand.java'
- include '**/com/android/tools/r8/BaseOutput.java'
+ include '**/com/android/tools/r8/BaseCompilerCommand.java'
include '**/com/android/tools/r8/ClassFileResourceProvider.java'
include '**/com/android/tools/r8/CompilationFailedException.java'
include '**/com/android/tools/r8/CompilationMode.java'
include '**/com/android/tools/r8/D8.java'
include '**/com/android/tools/r8/D8Command.java'
- include '**/com/android/tools/r8/D8Output.java'
+ include '**/com/android/tools/r8/DexIndexedConsumer.java'
+ include '**/com/android/tools/r8/DexFilePerClassFileConsumer.java'
include '**/com/android/tools/r8/Diagnostic.java'
include '**/com/android/tools/r8/DiagnosticsHandler.java'
- include '**/com/android/tools/r8/Location.java'
+ include '**/com/android/tools/r8/ProgramConsumer.java'
+ include '**/com/android/tools/r8/ProgramResource.java'
+ include '**/com/android/tools/r8/ProgramResourceProvider.java'
include '**/com/android/tools/r8/Resource.java'
- include '**/com/android/tools/r8/TextRangeLocation.java'
+ include '**/com/android/tools/r8/ResourceException.java'
+ include '**/com/android/tools/r8/StringConsumer.java'
+ include '**/com/android/tools/r8/StringResource.java'
+ include '**/com/android/tools/r8/Version.java'
+ include '**/com/android/tools/r8/origin/*.java'
+}
+
+task javadocR8(type: Javadoc) {
+ title "R8 API"
+ classpath = sourceSets.main.compileClasspath
+ source = sourceSets.main.allJava
+ include '**/com/android/tools/r8/ArchiveClassFileProvider.java'
+ include '**/com/android/tools/r8/ArchiveProgramResourceProvider.java'
+ include '**/com/android/tools/r8/BaseCommand.java'
+ include '**/com/android/tools/r8/BaseCompilerCommand.java'
+ include '**/com/android/tools/r8/ClassFileConsumer.java'
+ include '**/com/android/tools/r8/ClassFileResourceProvider.java'
+ include '**/com/android/tools/r8/CompilationFailedException.java'
+ include '**/com/android/tools/r8/CompilationMode.java'
+ include '**/com/android/tools/r8/R8.java'
+ include '**/com/android/tools/r8/R8Command.java'
+ include '**/com/android/tools/r8/DexIndexedConsumer.java'
+ include '**/com/android/tools/r8/Diagnostic.java'
+ include '**/com/android/tools/r8/DiagnosticsHandler.java'
+ include '**/com/android/tools/r8/ProgramConsumer.java'
+ include '**/com/android/tools/r8/ProgramResource.java'
+ include '**/com/android/tools/r8/ProgramResourceProvider.java'
+ include '**/com/android/tools/r8/Resource.java'
+ include '**/com/android/tools/r8/ResourceException.java'
+ include '**/com/android/tools/r8/StringConsumer.java'
+ include '**/com/android/tools/r8/StringResource.java'
+ include '**/com/android/tools/r8/Version.java'
include '**/com/android/tools/r8/origin/*.java'
}
diff --git a/src/main/java/com/android/tools/r8/BaseCommand.java b/src/main/java/com/android/tools/r8/BaseCommand.java
index 10c92dc..ffd51c0 100644
--- a/src/main/java/com/android/tools/r8/BaseCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCommand.java
@@ -23,9 +23,9 @@
/**
* Base class for commands and command builders for applications/tools which take an Android
- * application (and a main-dex list) as input.
+ * application sources (and optional main-dex list) as input.
*/
-abstract class BaseCommand {
+public abstract class BaseCommand {
private final boolean printHelp;
private final boolean printVersion;
@@ -175,12 +175,6 @@
return self();
}
- /** Add dex program-data. */
- public B addDexProgramData(byte[] data, Origin origin) {
- guard(() -> app.addDexProgramData(data, origin));
- return self();
- }
-
/**
* Add main-dex list files.
*
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index 4ed89d4..f42353a 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -12,15 +12,18 @@
import com.android.tools.r8.utils.OutputMode;
import com.android.tools.r8.utils.Reporter;
import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
/**
* Base class for commands and command builders for compiler applications/tools which besides an
- * Android application (and a main-dex list) also takes compilation output, compilation mode and
- * min API level as input.
+ * Android application (and optional main-dex list) also configure compilation output, compilation
+ * mode and min API level.
*/
-abstract class BaseCompilerCommand extends BaseCommand {
+public abstract class BaseCompilerCommand extends BaseCommand {
// TODO(b/70656566): Remove this once the deprecated API is removed.
+ @Deprecated
protected static class OutputOptions {
final Path path;
final OutputMode mode;
@@ -297,8 +300,31 @@
@Override
protected void validate() {
- assert mode != null;
+ if (mode == null) {
+ reporter.error("Expected valid compilation mode, was null");
+ }
FileUtils.validateOutputFile(outputPath, reporter);
+ List<Class> programConsumerClasses = new ArrayList<>(3);
+ if (programConsumer instanceof DexIndexedConsumer) {
+ programConsumerClasses.add(DexIndexedConsumer.class);
+ }
+ if (programConsumer instanceof DexFilePerClassFileConsumer) {
+ programConsumerClasses.add(DexFilePerClassFileConsumer.class);
+ }
+ if (programConsumer instanceof ClassFileConsumer) {
+ programConsumerClasses.add(ClassFileConsumer.class);
+ }
+ if (programConsumerClasses.size() > 1) {
+ StringBuilder builder = new StringBuilder()
+ .append("Invalid program consumer.")
+ .append(" A program consumer can implement at most one consumer type but ")
+ .append(programConsumer.getClass().getName())
+ .append(" implements types:");
+ for (Class clazz : programConsumerClasses) {
+ builder.append(" ").append(clazz.getName());
+ }
+ reporter.error(builder.toString());
+ }
super.validate();
}
}
diff --git a/src/main/java/com/android/tools/r8/BaseOutput.java b/src/main/java/com/android/tools/r8/BaseOutput.java
index 222ea6d..3ca5665 100644
--- a/src/main/java/com/android/tools/r8/BaseOutput.java
+++ b/src/main/java/com/android/tools/r8/BaseOutput.java
@@ -45,7 +45,7 @@
*/
public List<Resource> getDexResources() {
try {
- return ImmutableList.copyOf(app.getDexProgramResources());
+ return ImmutableList.copyOf(app.getDexProgramResourcesForTesting());
} catch (IOException e) {
throw new InternalCompilerError("Unexpected resource error", e);
}
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index fca359f..f10bd31 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -44,7 +44,7 @@
* <pre>
* D8.run(D8Command.builder()
* .addProgramFiles(inputPathA, inputPathB)
- * .setOutputPath(outputPath)
+ * .setOutput(outputPath, OutputMode.DexIndexed)
* .build());
* </pre>
*
@@ -80,10 +80,7 @@
}
/**
- * Main API entry for the D8 dexer.
- *
- * <p>The D8 dexer API is intentionally limited and should "do the right thing" given a set of
- * inputs. If the API does not suffice please contact the R8 team.
+ * Main API entry for the D8 dexer with a externally supplied executor service.
*
* @param command D8 command.
* @param executor executor service from which to get threads for multi-threaded processing.
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index fece59d..39462f9 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -28,6 +28,7 @@
* D8Command command = D8Command.builder()
* .addProgramFiles(path1, path2)
* .setMode(CompilationMode.RELEASE)
+ * .setOutput(Paths.get("output.zip", OutputMode.DexIndexed))
* .build();
* </pre>
*/
@@ -52,7 +53,13 @@
private Builder(AndroidApp app) {
super(app);
setMode(CompilationMode.DEBUG);
- }
+ }
+
+ /** Add dex program-data. */
+ public Builder addDexProgramData(byte[] data, Origin origin) {
+ guard(() -> getAppBuilder().addDexProgramData(data, origin));
+ return self();
+ }
/** Add classpath file resources. */
public Builder addClasspathFiles(Path... files) {
diff --git a/src/main/java/com/android/tools/r8/DexRoundTrip.java b/src/main/java/com/android/tools/r8/DexRoundTrip.java
index 01af3b8..c99e939 100644
--- a/src/main/java/com/android/tools/r8/DexRoundTrip.java
+++ b/src/main/java/com/android/tools/r8/DexRoundTrip.java
@@ -42,7 +42,8 @@
return consumer.build();
}
- public static void main(String[] args) throws CompilationFailedException, IOException {
+ public static void main(String[] args)
+ throws CompilationFailedException, IOException, ResourceException {
List<ProgramResource> resources = new ArrayList<>(args.length);
for (String arg : args) {
Path file = Paths.get(arg);
@@ -53,6 +54,6 @@
resources.add(ProgramResource.fromFile(Kind.DEX, file));
}
AndroidApp result = process(resources);
- process(result.getDexProgramResources());
+ process(result.computeAllProgramResources());
}
}
diff --git a/src/main/java/com/android/tools/r8/DexSegments.java b/src/main/java/com/android/tools/r8/DexSegments.java
index 1d9f129..72ac766 100644
--- a/src/main/java/com/android/tools/r8/DexSegments.java
+++ b/src/main/java/com/android/tools/r8/DexSegments.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.dex.DexFileReader;
import com.android.tools.r8.dex.Segment;
import com.android.tools.r8.origin.CommandLineOrigin;
@@ -85,7 +86,7 @@
}
public static void main(String[] args)
- throws IOException, CompilationFailedException {
+ throws IOException, CompilationFailedException, ResourceException {
Command.Builder builder = Command.parse(args);
Command command = builder.build();
if (command.isPrintHelp()) {
@@ -95,12 +96,14 @@
AndroidApp app = command.getInputApp();
Map<String, Integer> result = new HashMap<>();
try (Closer closer = Closer.create()) {
- for (Resource resource : app.getDexProgramResources()) {
- for (Segment segment :
- DexFileReader.parseMapFrom(
- closer.register(resource.getStream()), resource.getOrigin())) {
- int value = result.computeIfAbsent(segment.typeName(), (key) -> 0);
- result.put(segment.typeName(), value + segment.size());
+ for (ProgramResource resource : app.computeAllProgramResources()) {
+ if (resource.getKind() == Kind.DEX) {
+ for (Segment segment :
+ DexFileReader.parseMapFrom(
+ closer.register(resource.getByteStream()), resource.getOrigin())) {
+ int value = result.computeIfAbsent(segment.typeName(), (key) -> 0);
+ result.put(segment.typeName(), value + segment.size());
+ }
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ExtractMarker.java b/src/main/java/com/android/tools/r8/ExtractMarker.java
index 3a7ab99..272d2e5 100644
--- a/src/main/java/com/android/tools/r8/ExtractMarker.java
+++ b/src/main/java/com/android/tools/r8/ExtractMarker.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.dex.VDexFile;
@@ -51,9 +52,11 @@
AndroidApp.Builder appBuilder = AndroidApp.builder();
addDexResources(appBuilder, file);
int size = 0;
- for (ProgramResource resource : appBuilder.build().getDexProgramResources()) {
- try (InputStream input = resource.getByteStream()) {
- size += ByteStreams.toByteArray(input).length;
+ for (ProgramResource resource : appBuilder.build().computeAllProgramResources()) {
+ if (resource.getKind() == Kind.DEX) {
+ try (InputStream input = resource.getByteStream()) {
+ size += ByteStreams.toByteArray(input).length;
+ }
}
}
return size;
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 304dde3..032c07a 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -46,7 +46,6 @@
import com.android.tools.r8.shaking.protolite.ProtoLiteExtension;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.AndroidAppConsumers;
import com.android.tools.r8.utils.CfgPrinter;
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.FileUtils;
@@ -72,6 +71,35 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
+/**
+ * The R8 compiler.
+ *
+ * <p>R8 performs whole-program optimizing compilation of Java bytecode. It supports compilation of
+ * Java bytecode to Java bytecode or DEX bytecode. R8 supports tree-shaking the program to remove
+ * unneeded code and it supports minification of the program names to reduce the size of the
+ * resulting program.
+ *
+ * <p>The R8 API is intentionally limited and should "do the right thing" given a command. If this
+ * API does not suffice please contact the D8/R8 team.
+ *
+ * <p>R8 supports some configuration using configuration files mostly compatible with the format of
+ * the <a href="https://www.guardsquare.com/en/proguard">ProGuard</a> optimizer.
+ *
+ * <p>The compiler is invoked by calling {@link #run(R8Command) R8.run} with an appropriate {@link
+ * R8Command}. For example:
+ *
+ * <pre>
+ * R8.run(R8Command.builder()
+ * .addProgramFiles(inputPathA, inputPathB)
+ * .addLibraryFiles(androidJar)
+ * .setOutput(outputPath, OutputMode.DexIndexed)
+ * .build());
+ * </pre>
+ *
+ * The above reads the input files denoted by {@code inputPathA} and {@code inputPathB}, compiles
+ * them to DEX bytecode, using {@code androidJar} as the reference of the system runtime library,
+ * and then writes the result to the directory or zip archive specified by {@code outputPath}.
+ */
public class R8 {
private final Timing timing = new Timing("R8");
@@ -82,6 +110,49 @@
options.itemFactory.resetSortedIndices();
}
+ /**
+ * Main API entry for the R8 compiler.
+ *
+ * <p>The R8 API is intentionally limited and should "do the right thing" given a command. If this
+ * API does not suffice please contact the R8 team.
+ *
+ * @param command R8 command.
+ */
+ public static void run(R8Command command) throws CompilationFailedException {
+ AndroidApp app = command.getInputApp();
+ InternalOptions options = command.getInternalOptions();
+ ExecutorService executor = ThreadUtils.getExecutorService(options);
+ ExceptionUtils.withR8CompilationHandler(
+ command.getReporter(),
+ () -> {
+ try {
+ run(app, options, executor);
+ } finally {
+ executor.shutdown();
+ }
+ });
+ }
+
+ /**
+ * Main API entry for the R8 compiler.
+ *
+ * <p>The R8 API is intentionally limited and should "do the right thing" given a command. If this
+ * API does not suffice please contact the R8 team.
+ *
+ * @param command R8 command.
+ * @param executor executor service from which to get threads for multi-threaded processing.
+ */
+ public static void run(R8Command command, ExecutorService executor)
+ throws CompilationFailedException {
+ AndroidApp app = command.getInputApp();
+ InternalOptions options = command.getInternalOptions();
+ ExceptionUtils.withR8CompilationHandler(
+ command.getReporter(),
+ () -> {
+ run(app, options, executor);
+ });
+ }
+
// Compute the marker to be placed in the main dex file.
private static Marker getMarker(InternalOptions options) {
if (options.hasMarker()) {
@@ -96,7 +167,7 @@
return marker;
}
- public static void writeApplication(
+ static void writeApplication(
ExecutorService executorService,
DexApplication application,
String deadCode,
@@ -424,71 +495,6 @@
}
}
- /**
- * Main API entry for the R8 compiler.
- *
- * <p>The R8 API is intentionally limited and should "do the right thing" given a command. If this
- * API does not suffice please contact the R8 team.
- *
- * @param command R8 command.
- */
- public static void run(R8Command command) throws CompilationFailedException {
- AndroidApp app = command.getInputApp();
- InternalOptions options = command.getInternalOptions();
- ExecutorService executor = ThreadUtils.getExecutorService(options);
- ExceptionUtils.withR8CompilationHandler(
- command.getReporter(),
- () -> {
- try {
- run(app, options, executor);
- } finally {
- executor.shutdown();
- }
- });
- }
-
- /**
- * Main API entry for the R8 compiler.
- *
- * <p>The R8 API is intentionally limited and should "do the right thing" given a command. If this
- * API does not suffice please contact the R8 team.
- *
- * @param command R8 command.
- * @param executor executor service from which to get threads for multi-threaded processing.
- */
- public static void run(R8Command command, ExecutorService executor)
- throws CompilationFailedException {
- AndroidApp app = command.getInputApp();
- InternalOptions options = command.getInternalOptions();
- ExceptionUtils.withR8CompilationHandler(
- command.getReporter(),
- () -> {
- run(app, options, executor);
- });
- }
-
- /** TODO(sgjesse): Get rid of this. */
- public static AndroidApp runInternal(R8Command command) throws IOException, CompilationException {
- InternalOptions options = command.getInternalOptions();
- ExecutorService executorService = ThreadUtils.getExecutorService(options);
- try {
- return runInternal(command, executorService);
- } finally {
- executorService.shutdown();
- }
- }
-
- /**
- * TODO(sgjesse): Get rid of this.
- */
- public static AndroidApp runInternal(R8Command command, ExecutorService executor)
- throws IOException, CompilationException {
- InternalOptions options = command.getInternalOptions();
- AndroidAppConsumers compatConsumers = new AndroidAppConsumers(options);
- run(command.getInputApp(), options, executor);
- return compatConsumers.build();
- }
-
private static void run(String[] args) throws CompilationFailedException {
R8Command command = R8Command.parse(args, CommandLineOrigin.INSTANCE).build();
if (command.isPrintHelp()) {
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index cae894d..f382506 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.origin.Origin;
@@ -27,6 +28,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
@@ -112,10 +114,8 @@
return self();
}
- /**
- * Add proguard configuration file resources for automatic main dex list calculation.
- */
- public Builder addMainDexRulesFiles(List<Path> paths) {
+ /** Add proguard configuration file resources for automatic main dex list calculation. */
+ public Builder addMainDexRulesFiles(Collection<Path> paths) {
guard(() -> {
for (Path path : paths) {
mainDexRules.add(new ProguardConfigurationSourceFile(path));
@@ -192,20 +192,6 @@
}
/**
- * Add and/or chain proguard configuration consumer(s) for testing.
- */
- public Builder addProguardConfigurationConsumer(Consumer<ProguardConfiguration.Builder> c) {
- Consumer<ProguardConfiguration.Builder> oldConsumer = proguardConfigurationConsumer;
- proguardConfigurationConsumer = builder -> {
- if (oldConsumer != null) {
- oldConsumer.accept(builder);
- }
- c.accept(builder);
- };
- return self();
- }
-
- /**
* Set an output destination to which proguard-map content should be written.
*
* <p>This is a short-hand for setting a {@link StringConsumer.FileConsumer} using {@link
@@ -244,6 +230,12 @@
}
@Override
+ public Builder addProgramResourceProvider(ProgramResourceProvider programProvider) {
+ return super.addProgramResourceProvider(
+ new EnsureNonDexProgramResourceProvider(programProvider));
+ }
+
+ @Override
protected void validate() {
// TODO(b/70656566): Move up super once the deprecated API is removed.
if (getProgramConsumer() == null) {
@@ -259,6 +251,12 @@
reporter.error(
"Option --main-dex-list-output require --main-dex-rules and/or --main-dex-list");
}
+ for (Path file : programFiles) {
+ if (FileUtils.isDexFile(file)) {
+ reporter.error(new StringDiagnostic(
+ "R8 does not support compiling DEX inputs", new PathOrigin(file)));
+ }
+ }
super.validate();
}
@@ -337,6 +335,40 @@
return command;
}
+
+ // Internal for-testing method to add post-processors of the proguard configuration.
+ void addProguardConfigurationConsumerForTesting(Consumer<ProguardConfiguration.Builder> c) {
+ Consumer<ProguardConfiguration.Builder> oldConsumer = proguardConfigurationConsumer;
+ proguardConfigurationConsumer =
+ builder -> {
+ if (oldConsumer != null) {
+ oldConsumer.accept(builder);
+ }
+ c.accept(builder);
+ };
+ }
+ }
+
+ // Wrapper class to ensure that R8 does not allow DEX as program inputs.
+ private static class EnsureNonDexProgramResourceProvider implements ProgramResourceProvider {
+
+ final ProgramResourceProvider provider;
+
+ public EnsureNonDexProgramResourceProvider(ProgramResourceProvider provider) {
+ this.provider = provider;
+ }
+
+ @Override
+ public Collection<ProgramResource> getProgramResources() throws ResourceException {
+ Collection<ProgramResource> resources = provider.getProgramResources();
+ for (ProgramResource resource : resources) {
+ if (resource.getKind() == Kind.DEX) {
+ throw new ResourceException(resource.getOrigin(),
+ "R8 does not support compiling DEX inputs");
+ }
+ }
+ return resources;
+ }
}
// Internal state to verify parsing properties not enforced by the builder.
diff --git a/src/main/java/com/android/tools/r8/Utf8Consumer.java b/src/main/java/com/android/tools/r8/Utf8Consumer.java
deleted file mode 100644
index 4da33d5..0000000
--- a/src/main/java/com/android/tools/r8/Utf8Consumer.java
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8;
-
-import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.origin.PathOrigin;
-import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.IOExceptionDiagnostic;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.file.Path;
-
-/** Interface for receiving UTF8 encoded text data. */
-public interface Utf8Consumer {
-
- /**
- * Callback to receive UTF8 encoded text data.
- *
- * <p>The consumer is expected not to throw, but instead report any errors via the diagnostics
- * {@param handler}. If an error is reported via {@param handler} and no exceptions are thrown,
- * then the compiler guaranties to exit with an error.
- *
- * @param data UTF-8 encoded text data.
- * @param handler Diagnostics handler for reporting.
- */
- void accept(byte[] data, DiagnosticsHandler handler);
-
- static EmptyConsumer emptyConsumer() {
- return EmptyConsumer.EMPTY_CONSUMER;
- }
-
- /** Empty consumer to request the production of the resource but ignore its value. */
- class EmptyConsumer implements Utf8Consumer {
-
- private static EmptyConsumer EMPTY_CONSUMER = new EmptyConsumer();
-
- @Override
- public void accept(byte[] data, DiagnosticsHandler handler) {
- // Ignore content.
- }
- }
-
- /** Forwarding consumer to delegate to an optional existing consumer. */
- class ForwardingConsumer implements Utf8Consumer {
-
- private final Utf8Consumer consumer;
-
- /** @param consumer Consumer to forward to, if null, nothing will be forwarded. */
- public ForwardingConsumer(Utf8Consumer consumer) {
- this.consumer = consumer;
- }
-
- @Override
- public void accept(byte[] data, DiagnosticsHandler handler) {
- if (consumer != null) {
- consumer.accept(data, handler);
- }
- }
- }
-
- /** File consumer to write contents to a file-system file. */
- class FileConsumer extends ForwardingConsumer {
-
- private final Path outputPath;
-
- /** Consumer that writes to {@param outputPath}. */
- public FileConsumer(Path outputPath) {
- this(outputPath, null);
- }
-
- /** Consumer that forwards to {@param consumer} and also writes to {@param outputPath}. */
- public FileConsumer(Path outputPath, Utf8Consumer consumer) {
- super(consumer);
- this.outputPath = outputPath;
- }
-
- @Override
- public void accept(byte[] data, DiagnosticsHandler handler) {
- super.accept(data, handler);
- try {
- FileUtils.writeToFile(outputPath, null, data);
- } catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, new PathOrigin(outputPath)));
- }
- }
- }
-
- /**
- * Stream consumer to write contents to an output stream.
- *
- * <p>Note: No close events are given to this stream so it should either be a permanent stream or
- * the closing needs to happen outside of the compilation itself. If the stream is not one of the
- * standard streams, i.e., System.out or System.err, you should likely implement yor own consumer.
- */
- class StreamConsumer extends ForwardingConsumer {
-
- private final Origin origin;
- private final OutputStream outputStream;
-
- /** Consumer that writes to {@param outputStream}. */
- public StreamConsumer(Origin origin, OutputStream outputStream) {
- this(origin, outputStream, null);
- }
-
- /** Consumer that forwards to {@param consumer} and also writes to {@param outputStream}. */
- public StreamConsumer(Origin origin, OutputStream outputStream, Utf8Consumer consumer) {
- super(consumer);
- this.origin = origin;
- this.outputStream = outputStream;
- }
-
- @Override
- public void accept(byte[] data, DiagnosticsHandler handler) {
- super.accept(data, handler);
- try {
- outputStream.write(data);
- } catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, origin));
- }
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 0e6c94d..f4056e7 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.utils.VersionProperties;
+/** Version of the D8/R8 library. */
public final class Version {
// This field is accessed from release scripts using simple pattern matching.
@@ -20,6 +21,7 @@
System.out.println(VersionProperties.INSTANCE.getDescription());
}
+ /** Is this a development version of the D8/R8 library. */
public static boolean isDev() {
return LABEL.endsWith("-dev") || VersionProperties.INSTANCE.isEngineering();
}
diff --git a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
index 7c50696..17c4706 100644
--- a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
+++ b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
@@ -6,18 +6,20 @@
import com.android.tools.r8.CompilationException;
import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.R8;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.Version;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.origin.CommandLineOrigin;
import com.android.tools.r8.utils.AbortException;
-import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.OutputMode;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.List;
+import java.util.Set;
/**
* Proguard + dx compatibility interface for r8.
@@ -170,10 +172,16 @@
if (options.mainDexList != null) {
builder.addMainDexListFiles(Paths.get(options.mainDexList));
}
- AndroidApp result = R8.runInternal(builder.build());
+
+ // Wrap the output consumer so we can count the number of output files.
+ CountOutputConsumer outputConsumer =
+ new CountOutputConsumer((DexIndexedConsumer) builder.getProgramConsumer());
+ builder.setProgramConsumer(outputConsumer);
+
+ R8.run(builder.build());
if (!options.multiDex) {
- if (result.getDexProgramResources().size() > 1) {
+ if (outputConsumer.count > 1) {
throw new CompilationError(
"Compilation result could not fit into a single dex file. "
+ "Reduce the input-program size or run with --multi-dex enabled");
@@ -193,4 +201,20 @@
System.exit(1);
}
}
+
+ private static class CountOutputConsumer extends DexIndexedConsumer.ForwardingConsumer {
+
+ int count = 0;
+
+ public CountOutputConsumer(DexIndexedConsumer consumer) {
+ super(consumer);
+ }
+
+ @Override
+ public synchronized void accept(int fileIndex, byte[] data, Set<String> descriptors,
+ DiagnosticsHandler handler) {
+ super.accept(fileIndex, data, descriptors, handler);
+ count++;
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index 450cdab..b6a3ef6 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.ClassFileResourceProvider;
import com.android.tools.r8.ProgramResource;
+import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.ResourceException;
import com.android.tools.r8.StringResource;
import com.android.tools.r8.errors.CompilationError;
@@ -39,6 +40,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
@@ -237,8 +239,19 @@
}
void readSources() throws IOException, ResourceException {
- readDexSources(inputApp.getDexProgramResources(), PROGRAM, programClasses);
- readClassSources(inputApp.getClassProgramResources(), PROGRAM, programClasses);
+ Collection<ProgramResource> resources = inputApp.computeAllProgramResources();
+ List<ProgramResource> dexResources = new ArrayList<>(resources.size());
+ List<ProgramResource> cfResources = new ArrayList<>(resources.size());
+ for (ProgramResource resource : resources) {
+ if (resource.getKind() == Kind.DEX) {
+ dexResources.add(resource);
+ } else {
+ assert resource.getKind() == Kind.CF;
+ cfResources.add(resource);
+ }
+ }
+ readDexSources(dexResources, PROGRAM, programClasses);
+ readClassSources(cfResources, PROGRAM, programClasses);
}
private <T extends DexClass> ClassProvider<T> buildClassProvider(ClassKind classKind,
diff --git a/src/main/java/com/android/tools/r8/dex/Constants.java b/src/main/java/com/android/tools/r8/dex/Constants.java
index 03f2ad5..77a82df 100644
--- a/src/main/java/com/android/tools/r8/dex/Constants.java
+++ b/src/main/java/com/android/tools/r8/dex/Constants.java
@@ -15,8 +15,8 @@
public static final int MIN_VDEX_VERSION = 10;
public static final int MAX_VDEX_VERSION = 11;
- // We apply Java 7 class file constraints on DEX files.
- public static final int CORRESPONDING_CLASS_FILE_VERSION = 51;
+ // We apply Java 6 class file constraints on DEX files.
+ public static final int CORRESPONDING_CLASS_FILE_VERSION = 50;
public static final int DEX_MAGIC_SIZE = 8;
diff --git a/src/main/java/com/android/tools/r8/dexfilemerger/DexFileMerger.java b/src/main/java/com/android/tools/r8/dexfilemerger/DexFileMerger.java
index 249e7a1..7bcb4fc 100644
--- a/src/main/java/com/android/tools/r8/dexfilemerger/DexFileMerger.java
+++ b/src/main/java/com/android/tools/r8/dexfilemerger/DexFileMerger.java
@@ -16,6 +16,7 @@
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
@@ -159,13 +160,37 @@
return null;
}
- private static Options parseArguments(String[] args) {
+ private static Options parseArguments(String[] args) throws IOException {
+ // We may have a single argument which is a parameter file path, prefixed with '@'.
+ if (args.length == 1 && args[0].startsWith("@")) {
+ // TODO(tamaskenez) Implement more sophisticated processing
+ // which is aligned with Blaze's
+ // com.google.devtools.common.options.ShellQuotedParamsFilePreProcessor
+ Path paramsFile = Paths.get(args[0].substring(1));
+ List<String> argsList = new ArrayList<>();
+ for (String s : Files.readAllLines(paramsFile)) {
+ s = s.trim();
+ if (s.isEmpty()) {
+ continue;
+ }
+ // Trim optional enclosing single quotes. Unescaping omitted for now.
+ if (s.length() >= 2 && s.startsWith("'") && s.endsWith("'")) {
+ s = s.substring(1, s.length() - 1);
+ }
+ argsList.add(s);
+ }
+ args = argsList.toArray(new String[argsList.size()]);
+ }
+
Options options = new Options();
ParseContext context = new ParseContext(args);
List<String> strings;
String string;
Boolean b;
while (context.head() != null) {
+ if (context.head().startsWith("@")) {
+ throw new RuntimeException("A params file must be the only argument: " + context.head());
+ }
strings = tryParseMulti(context, "--input");
if (strings != null) {
options.inputArchives.addAll(strings);
@@ -322,7 +347,7 @@
if (options.inputArchives.size() != 1) {
throw new RuntimeException("'--multidex=given_shard' requires exactly one --input.");
}
- singleFixedFileIndex = parseFileIndexFromShardFilename(options.inputArchives.get(0));
+ singleFixedFileIndex = parseFileIndexFromShardFilename(options.inputArchives.get(0)) - 1;
break;
case MINIMAL:
case BEST_EFFORT:
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
index 58d8473..dba4842 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
@@ -236,7 +236,7 @@
+ registerNum * 7
+ name.hashCode() * 13
+ type.hashCode() * 17
- + (signature == null ? 0 : signature.hashCode()) * 19;
+ + Objects.hashCode(signature) * 19;
}
@Override
@@ -254,7 +254,7 @@
if (!type.equals(o.type)) {
return false;
}
- return (signature == o.signature || signature.equals(o.signature));
+ return Objects.equals(signature, o.signature);
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index d4a2ef9..a2078f8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -130,6 +130,7 @@
public final DexString stringBufferDescriptor = createString("Ljava/lang/StringBuffer;");
public final DexString varHandleDescriptor = createString("Ljava/lang/invoke/VarHandle;");
public final DexString methodHandleDescriptor = createString("Ljava/lang/invoke/MethodHandle;");
+ public final DexString methodTypeDescriptor = createString("Ljava/lang/invoke/MethodType;");
public final DexString intFieldUpdaterDescriptor =
createString("Ljava/util/concurrent/atomic/AtomicIntegerFieldUpdater;");
@@ -181,9 +182,12 @@
public final DexType varHandleType = createType(varHandleDescriptor);
public final DexType methodHandleType = createType(methodHandleDescriptor);
+ public final DexType methodTypeType = createType(methodTypeDescriptor);
- public final StringBuildingMethods stringBuilderMethods = new StringBuildingMethods(stringBuilderType);
- public final StringBuildingMethods stringBufferMethods = new StringBuildingMethods(stringBufferType);
+ public final StringBuildingMethods stringBuilderMethods =
+ new StringBuildingMethods(stringBuilderType);
+ public final StringBuildingMethods stringBufferMethods =
+ new StringBuildingMethods(stringBufferType);
public final ObjectsMethods objectsMethods = new ObjectsMethods();
public final ObjectMethods objectMethods = new ObjectMethods();
public final LongMethods longMethods = new LongMethods();
@@ -239,7 +243,7 @@
public final DexMethod getClass;
private ObjectMethods() {
- getClass = createMethod(objectsDescriptor, getClassMethodName, classDescriptor,
+ getClass = createMethod(objectDescriptor, getClassMethodName, classDescriptor,
DexString.EMPTY_ARRAY);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
index 1cd1b20..2d45a308 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
@@ -4,8 +4,11 @@
package com.android.tools.r8.ir.code;
import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.conversion.DexBuilder;
+import java.util.function.Function;
public class ConstMethodHandle extends ConstInstruction {
@@ -76,4 +79,10 @@
public ConstMethodHandle asConstMethodHandle() {
return this;
}
+
+ @Override
+ public TypeLatticeElement evaluate(
+ AppInfoWithSubtyping appInfo, Function<Value, TypeLatticeElement> getLatticeElement) {
+ return TypeLatticeElement.fromDexType(appInfo, appInfo.dexItemFactory.methodHandleType, false);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
index cbcf143..aff7b9b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
@@ -4,8 +4,11 @@
package com.android.tools.r8.ir.code;
import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.conversion.DexBuilder;
+import java.util.function.Function;
public class ConstMethodType extends ConstInstruction {
@@ -76,4 +79,10 @@
public ConstMethodType asConstMethodType() {
return this;
}
+
+ @Override
+ public TypeLatticeElement evaluate(
+ AppInfoWithSubtyping appInfo, Function<Value, TypeLatticeElement> getLatticeElement) {
+ return TypeLatticeElement.fromDexType(appInfo, appInfo.dexItemFactory.methodTypeType, false);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index 9e6d79c..e21a9fa 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -85,8 +85,17 @@
return new Builder(app);
}
- /** Get input streams for all dex program resources. */
- public List<ProgramResource> getDexProgramResources() throws IOException {
+ /** Get full collection of all program resources from all program providers. */
+ public Collection<ProgramResource> computeAllProgramResources() throws ResourceException {
+ List<ProgramResource> resources = new ArrayList<>();
+ for (ProgramResourceProvider provider : programResourceProviders) {
+ resources.addAll(provider.getProgramResources());
+ }
+ return resources;
+ }
+
+ // TODO(zerny): Remove this method.
+ public List<ProgramResource> getDexProgramResourcesForTesting() throws IOException {
try {
return filter(programResourceProviders, Kind.DEX);
} catch (ResourceException e) {
@@ -98,8 +107,8 @@
}
}
- /** Get input streams for all Java-bytecode program resources. */
- public List<ProgramResource> getClassProgramResources() throws IOException {
+ // TODO(zerny): Remove this method.
+ public List<ProgramResource> getClassProgramResourcesForTesting() throws IOException {
try {
return filter(programResourceProviders, Kind.CF);
} catch (ResourceException e) {
@@ -192,7 +201,7 @@
* Write the dex program resources and proguard resource to @code{directory}.
*/
public void writeToDirectory(Path directory, OutputMode outputMode) throws IOException {
- List<ProgramResource> dexProgramSources = getDexProgramResources();
+ List<ProgramResource> dexProgramSources = getDexProgramResourcesForTesting();
if (outputMode.isDexIndexed()) {
DexIndexedConsumer.DirectoryConsumer.writeResources(directory, dexProgramSources);
} else {
@@ -205,7 +214,7 @@
* Write the dex program resources to @code{archive} and the proguard resource as its sibling.
*/
public void writeToZip(Path archive, OutputMode outputMode) throws IOException {
- List<ProgramResource> resources = getDexProgramResources();
+ List<ProgramResource> resources = getDexProgramResourcesForTesting();
if (outputMode.isDexIndexed()) {
DexIndexedConsumer.ArchiveConsumer.writeResources(archive, resources);
} else if (outputMode.isDexFilePerClassFile()) {
diff --git a/src/test/apiUsageSample/com/android/tools/apiusagesample/D8ApiUsageSample.java b/src/test/apiUsageSample/com/android/tools/apiusagesample/D8ApiUsageSample.java
new file mode 100644
index 0000000..4e080b7
--- /dev/null
+++ b/src/test/apiUsageSample/com/android/tools/apiusagesample/D8ApiUsageSample.java
@@ -0,0 +1,498 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.apiusagesample;
+
+import com.android.tools.r8.ArchiveClassFileProvider;
+import com.android.tools.r8.ArchiveProgramResourceProvider;
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.DexFilePerClassFileConsumer;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.ProgramResource;
+import com.android.tools.r8.ProgramResource.Kind;
+import com.android.tools.r8.ProgramResourceProvider;
+import com.android.tools.r8.ResourceException;
+import com.android.tools.r8.origin.ArchiveEntryOrigin;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.utils.StringDiagnostic;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+public class D8ApiUsageSample {
+
+ private static final Origin origin =
+ new Origin(Origin.root()) {
+ @Override
+ public String part() {
+ return "D8ApiUsageSample";
+ }
+ };
+
+ private static final DiagnosticsHandler handler = new D8DiagnosticsHandler();
+
+ /**
+ * Example invocation:
+ *
+ * <pre>
+ * java -jar d8-api-uses.jar \
+ * --output path/to/output/dir \
+ * --min-api minApiLevel \
+ * --lib path/to/library.jar \
+ * --classpath path/to/classpath.jar \
+ * path/to/input{1,2,3}.{jar,class}
+ * </pre>
+ */
+ public static void main(String[] args) {
+ // Parse arguments with the commandline parser to make use of its API.
+ D8Command.Builder cmd = D8Command.parse(args, origin);
+ CompilationMode mode = cmd.getMode();
+ Path temp = cmd.getOutputPath();
+ int minApiLevel = cmd.getMinApiLevel();
+ // The Builder API does not provide access to the concrete paths
+ // (everything is put into providers) so manually parse them here.
+ List<Path> libraries = new ArrayList<>(1);
+ List<Path> classpath = new ArrayList<>(args.length);
+ List<Path> mainDexList = new ArrayList<>(1);
+ List<Path> inputs = new ArrayList<>(args.length);
+ for (int i = 0; i < args.length; i++) {
+ if (args[i].equals("--lib")) {
+ libraries.add(Paths.get(args[++i]));
+ } else if (args[i].equals("--classpath")) {
+ classpath.add(Paths.get(args[++i]));
+ } else if (args[i].equals("--main-dex-list")) {
+ mainDexList.add(Paths.get(args[++i]));
+ } else if (isArchive(args[i]) || isClassFile(args[i])) {
+ inputs.add(Paths.get(args[i]));
+ }
+ }
+ if (!Files.exists(temp) || !Files.isDirectory(temp)) {
+ throw new RuntimeException("Must supply a temp/output directory");
+ }
+ if (inputs.isEmpty()) {
+ throw new RuntimeException("Must supply program inputs");
+ }
+ if (classpath.isEmpty()) {
+ throw new RuntimeException("Must supply classpath inputs");
+ }
+ if (libraries.isEmpty()) {
+ throw new RuntimeException("Must supply library inputs");
+ }
+ if (mainDexList.isEmpty()) {
+ throw new RuntimeException("Must supply main-dex-list inputs");
+ }
+
+ useProgramFileList(CompilationMode.DEBUG, minApiLevel, libraries, classpath, inputs);
+ useProgramFileList(CompilationMode.RELEASE, minApiLevel, libraries, classpath, inputs);
+ useProgramData(minApiLevel, libraries, classpath, inputs);
+ useProgramResourceProvider(minApiLevel, libraries, classpath, inputs);
+ useLibraryAndClasspathProvider(minApiLevel, libraries, classpath, inputs);
+ useMainDexListFiles(minApiLevel, libraries, classpath, inputs, mainDexList);
+ useMainDexClasses(minApiLevel, libraries, classpath, inputs, mainDexList);
+ useVArgVariants(minApiLevel, libraries, classpath, inputs, mainDexList);
+ incrementalCompileAndMerge(minApiLevel, libraries, classpath, inputs);
+ }
+
+ // Check API support for compiling Java class-files from the file system.
+ private static void useProgramFileList(
+ CompilationMode mode,
+ int minApiLevel,
+ Collection<Path> libraries,
+ Collection<Path> classpath,
+ Collection<Path> inputs) {
+ try {
+ D8.run(
+ D8Command.builder(handler)
+ .setMode(mode)
+ .setMinApiLevel(minApiLevel)
+ .setProgramConsumer(new EnsureOutputConsumer())
+ .addLibraryFiles(libraries)
+ .addClasspathFiles(classpath)
+ .addProgramFiles(inputs)
+ .build());
+ } catch (CompilationFailedException e) {
+ throw new RuntimeException("Unexpected compilation exceptions", e);
+ }
+ }
+
+ // Check API support for compiling Java class-files from byte content.
+ private static void useProgramData(
+ int minApiLevel,
+ Collection<Path> libraries,
+ Collection<Path> classpath,
+ Collection<Path> inputs) {
+ try {
+ D8Command.Builder builder =
+ D8Command.builder(handler)
+ .setMinApiLevel(minApiLevel)
+ .setProgramConsumer(new EnsureOutputConsumer())
+ .addLibraryFiles(libraries)
+ .addClasspathFiles(classpath);
+ for (ClassFileContent classfile : readClassFiles(inputs)) {
+ builder.addClassProgramData(classfile.data, classfile.origin);
+ }
+ for (Path input : inputs) {
+ if (isDexFile(input)) {
+ builder.addDexProgramData(Files.readAllBytes(input), new PathOrigin(input));
+ }
+ }
+ D8.run(builder.build());
+ } catch (CompilationFailedException e) {
+ throw new RuntimeException("Unexpected compilation exceptions", e);
+ } catch (IOException e) {
+ throw new RuntimeException("Unexpected IO exception", e);
+ }
+ }
+
+ // Check API support for compiling Java class-files from a program provider abstraction.
+ private static void useProgramResourceProvider(
+ int minApiLevel,
+ Collection<Path> libraries,
+ Collection<Path> classpath,
+ Collection<Path> inputs) {
+ try {
+ D8Command.Builder builder =
+ D8Command.builder(handler)
+ .setMinApiLevel(minApiLevel)
+ .setProgramConsumer(new EnsureOutputConsumer())
+ .addLibraryFiles(libraries)
+ .addClasspathFiles(classpath);
+ for (Path input : inputs) {
+ if (isArchive(input)) {
+ builder.addProgramResourceProvider(
+ ArchiveProgramResourceProvider.fromArchive(
+ input, ArchiveProgramResourceProvider::includeClassFileEntries));
+ } else if (isClassFile(input)) {
+ builder.addProgramResourceProvider(
+ new ProgramResourceProvider() {
+ @Override
+ public Collection<ProgramResource> getProgramResources() throws ResourceException {
+ return Collections.singleton(ProgramResource.fromFile(Kind.CF, input));
+ }
+ });
+ } else if (isDexFile(input)) {
+ builder.addProgramResourceProvider(
+ new ProgramResourceProvider() {
+ @Override
+ public Collection<ProgramResource> getProgramResources() throws ResourceException {
+ return Collections.singleton(ProgramResource.fromFile(Kind.DEX, input));
+ }
+ });
+ }
+ }
+ D8.run(builder.build());
+ } catch (CompilationFailedException e) {
+ throw new RuntimeException("Unexpected compilation exceptions", e);
+ }
+ }
+
+ private static void useLibraryAndClasspathProvider(
+ int minApiLevel,
+ Collection<Path> libraries,
+ Collection<Path> classpath,
+ Collection<Path> inputs) {
+ try {
+ D8Command.Builder builder =
+ D8Command.builder(handler)
+ .setMinApiLevel(minApiLevel)
+ .setProgramConsumer(new EnsureOutputConsumer())
+ .addProgramFiles(inputs);
+ for (Path library : libraries) {
+ builder.addLibraryResourceProvider(new ArchiveClassFileProvider(library));
+ }
+ for (Path path : classpath) {
+ builder.addClasspathResourceProvider(new ArchiveClassFileProvider(path));
+ }
+ D8.run(builder.build());
+ } catch (CompilationFailedException e) {
+ throw new RuntimeException("Unexpected compilation exceptions", e);
+ } catch (IOException e) {
+ throw new RuntimeException("Unexpected IO exception", e);
+ }
+ }
+
+ private static void useMainDexListFiles(
+ int minApiLevel,
+ Collection<Path> libraries,
+ Collection<Path> classpath,
+ Collection<Path> inputs,
+ Collection<Path> mainDexList) {
+ try {
+ D8.run(
+ D8Command.builder(handler)
+ .setMinApiLevel(minApiLevel)
+ .setProgramConsumer(new EnsureOutputConsumer())
+ .addLibraryFiles(libraries)
+ .addClasspathFiles(classpath)
+ .addProgramFiles(inputs)
+ .addMainDexListFiles(mainDexList)
+ .build());
+ } catch (CompilationFailedException e) {
+ throw new RuntimeException("Unexpected compilation exceptions", e);
+ }
+ }
+
+ private static void useMainDexClasses(
+ int minApiLevel,
+ Collection<Path> libraries,
+ Collection<Path> classpath,
+ Collection<Path> inputs,
+ Collection<Path> mainDexList) {
+ try {
+ List<String> mainDexClasses = new ArrayList<>(1);
+ for (Path path : mainDexList) {
+ for (String line : Files.readAllLines(path)) {
+ String entry = line.trim();
+ if (entry.isEmpty() || entry.startsWith("#") || !entry.endsWith(".class")) {
+ continue;
+ }
+ mainDexClasses.add(entry.replace(".class", "").replace("/", "."));
+ }
+ }
+ D8.run(
+ D8Command.builder(handler)
+ .setMinApiLevel(minApiLevel)
+ .setProgramConsumer(new EnsureOutputConsumer())
+ .addLibraryFiles(libraries)
+ .addClasspathFiles(classpath)
+ .addProgramFiles(inputs)
+ .addMainDexClasses(mainDexClasses)
+ .build());
+ } catch (CompilationFailedException e) {
+ throw new RuntimeException("Unexpected compilation exceptions", e);
+ } catch (IOException e) {
+ throw new RuntimeException("Unexpected IO exception", e);
+ }
+ }
+
+ // Check API support for all the varg variants.
+ private static void useVArgVariants(
+ int minApiLevel,
+ List<Path> libraries,
+ List<Path> classpath,
+ List<Path> inputs,
+ List<Path> mainDexList) {
+ try {
+ D8.run(
+ D8Command.builder(handler)
+ .setMinApiLevel(minApiLevel)
+ .setProgramConsumer(new EnsureOutputConsumer())
+ .addLibraryFiles(libraries.get(0))
+ .addLibraryFiles(libraries.stream().skip(1).toArray(Path[]::new))
+ .addClasspathFiles(classpath.get(0))
+ .addClasspathFiles(classpath.stream().skip(1).toArray(Path[]::new))
+ .addProgramFiles(inputs.get(0))
+ .addProgramFiles(inputs.stream().skip(1).toArray(Path[]::new))
+ .addMainDexListFiles(mainDexList.get(0))
+ .addMainDexListFiles(mainDexList.stream().skip(1).toArray(Path[]::new))
+ .build());
+ } catch (CompilationFailedException e) {
+ throw new RuntimeException("Unexpected compilation exceptions", e);
+ }
+ }
+
+ private static void incrementalCompileAndMerge(
+ int minApiLevel,
+ Collection<Path> libraries,
+ Collection<Path> classpath,
+ Collection<Path> inputs) {
+ // Compile and merge via index intermediates.
+ mergeIntermediates(
+ minApiLevel, compileToIndexedIntermediates(minApiLevel, libraries, classpath, inputs));
+ // Compile and merge via per-classfile intermediates.
+ mergeIntermediates(
+ minApiLevel, compileToPerClassFileIntermediates(minApiLevel, libraries, classpath, inputs));
+ }
+
+ private static Collection<byte[]> compileToIndexedIntermediates(
+ int minApiLevel,
+ Collection<Path> libraries,
+ Collection<Path> classpath,
+ Collection<Path> inputs) {
+ IndexIntermediatesConsumer consumer = new IndexIntermediatesConsumer();
+ try {
+ D8.run(
+ D8Command.builder(handler)
+ .setMinApiLevel(minApiLevel)
+ .setIntermediate(true)
+ .setProgramConsumer(consumer)
+ .addClasspathFiles(classpath)
+ .addLibraryFiles(libraries)
+ .addProgramFiles(inputs)
+ .build());
+ } catch (CompilationFailedException e) {
+ throw new RuntimeException("Unexpected compilation exceptions", e);
+ }
+ return consumer.bytes;
+ }
+
+ private static Collection<byte[]> compileToPerClassFileIntermediates(
+ int minApiLevel,
+ Collection<Path> libraries,
+ Collection<Path> classpath,
+ Collection<Path> inputs) {
+ PerClassIntermediatesConsumer consumer = new PerClassIntermediatesConsumer();
+ try {
+ D8.run(
+ D8Command.builder(handler)
+ .setMinApiLevel(minApiLevel)
+ .setProgramConsumer(consumer)
+ .addLibraryFiles(libraries)
+ .addClasspathFiles(classpath)
+ .addProgramFiles(inputs)
+ .build());
+ } catch (CompilationFailedException e) {
+ throw new RuntimeException("Unexpected compilation exceptions", e);
+ }
+ return consumer.bytes;
+ }
+
+ private static void mergeIntermediates(int minApiLevel, Collection<byte[]> intermediates) {
+ D8Command.Builder builder =
+ D8Command.builder(handler)
+ .setMinApiLevel(minApiLevel)
+ .setProgramConsumer(new EnsureOutputConsumer());
+ for (byte[] intermediate : intermediates) {
+ builder.addDexProgramData(intermediate, Origin.unknown());
+ }
+ try {
+ D8.run(builder.build());
+ } catch (CompilationFailedException e) {
+ throw new RuntimeException("Unexpected merging error", e);
+ }
+ }
+
+ // Helpers for tests.
+ // Some of this reimplements stuff in R8 utils, but that is not public API and we should not
+ // rely on it.
+
+ private static List<ClassFileContent> readClassFiles(Collection<Path> files) throws IOException {
+ List<ClassFileContent> classfiles = new ArrayList<>();
+ for (Path file : files) {
+ if (isArchive(file)) {
+ Origin zipOrigin = new PathOrigin(file);
+ ZipInputStream zip = new ZipInputStream(Files.newInputStream(file));
+ ZipEntry entry;
+ while (null != (entry = zip.getNextEntry())) {
+ if (isClassFile(Paths.get(entry.getName()))) {
+ Origin origin = new ArchiveEntryOrigin(entry.getName(), zipOrigin);
+ classfiles.add(new ClassFileContent(origin, readBytes(zip)));
+ }
+ }
+ } else if (isClassFile(file)) {
+ classfiles.add(new ClassFileContent(new PathOrigin(file), Files.readAllBytes(file)));
+ }
+ }
+ return classfiles;
+ }
+
+ private static byte[] readBytes(InputStream stream) throws IOException {
+ try (ByteArrayOutputStream bytes = new ByteArrayOutputStream()) {
+ byte[] buffer = new byte[0xffff];
+ for (int length; (length = stream.read(buffer)) != -1; ) {
+ bytes.write(buffer, 0, length);
+ }
+ return bytes.toByteArray();
+ }
+ }
+
+ private static boolean isClassFile(Path file) {
+ return isClassFile(file.toString());
+ }
+
+ private static boolean isClassFile(String file) {
+ file = file.toLowerCase();
+ return file.endsWith(".class");
+ }
+
+ private static boolean isDexFile(Path file) {
+ return isDexFile(file.toString());
+ }
+
+ private static boolean isDexFile(String file) {
+ file = file.toLowerCase();
+ return file.endsWith(".dex");
+ }
+
+ private static boolean isArchive(Path file) {
+ return isArchive(file.toString());
+ }
+
+ private static boolean isArchive(String file) {
+ file = file.toLowerCase();
+ return file.endsWith(".zip") || file.endsWith(".jar");
+ }
+
+ private static class ClassFileContent {
+ final Origin origin;
+ final byte[] data;
+
+ public ClassFileContent(Origin origin, byte[] data) {
+ this.origin = origin;
+ this.data = data;
+ }
+ }
+
+ private static class IndexIntermediatesConsumer implements DexIndexedConsumer {
+
+ List<byte[]> bytes = new ArrayList<>();
+
+ @Override
+ public synchronized void accept(
+ int fileIndex, byte[] data, Set<String> descriptors, DiagnosticsHandler handler) {
+ bytes.add(data);
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {}
+ }
+
+ private static class PerClassIntermediatesConsumer implements DexFilePerClassFileConsumer {
+
+ List<byte[]> bytes = new ArrayList<>();
+
+ @Override
+ public synchronized void accept(
+ String primaryClassDescriptor,
+ byte[] data,
+ Set<String> descriptors,
+ DiagnosticsHandler handler) {
+ bytes.add(data);
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {}
+ }
+
+ private static class EnsureOutputConsumer implements DexIndexedConsumer {
+ boolean hasOutput = false;
+
+ @Override
+ public synchronized void accept(
+ int fileIndex, byte[] data, Set<String> descriptors, DiagnosticsHandler handler) {
+ hasOutput = true;
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ if (!hasOutput) {
+ handler.error(new StringDiagnostic("Expected to produce output but had none"));
+ }
+ }
+ }
+}
diff --git a/src/test/apiUsageSample/com/android/tools/apiusagesample/D8DiagnosticsHandler.java b/src/test/apiUsageSample/com/android/tools/apiusagesample/D8DiagnosticsHandler.java
index d9f11b0..9b08352 100644
--- a/src/test/apiUsageSample/com/android/tools/apiusagesample/D8DiagnosticsHandler.java
+++ b/src/test/apiUsageSample/com/android/tools/apiusagesample/D8DiagnosticsHandler.java
@@ -49,7 +49,7 @@
Origin origin = diagnostic.getOrigin();
Position positionInOrigin = diagnostic.getPosition();
String position;
- if (origin instanceof PathOrigin) {
+ if (origin.parent() instanceof PathOrigin) {
if (positionInOrigin instanceof TextRange) {
TextRange textRange = (TextRange) positionInOrigin;
position = ((PathOrigin) origin.parent()).getPath().toFile() + ": "
diff --git a/src/test/apiUsageSample/com/android/tools/apiusagesample/R8ApiUsageSample.java b/src/test/apiUsageSample/com/android/tools/apiusagesample/R8ApiUsageSample.java
new file mode 100644
index 0000000..7230f55
--- /dev/null
+++ b/src/test/apiUsageSample/com/android/tools/apiusagesample/R8ApiUsageSample.java
@@ -0,0 +1,450 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.apiusagesample;
+
+import com.android.tools.r8.ArchiveClassFileProvider;
+import com.android.tools.r8.ArchiveProgramResourceProvider;
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.ProgramResource;
+import com.android.tools.r8.ProgramResource.Kind;
+import com.android.tools.r8.ProgramResourceProvider;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ResourceException;
+import com.android.tools.r8.origin.ArchiveEntryOrigin;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.utils.StringDiagnostic;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+public class R8ApiUsageSample {
+
+ private static final Origin origin =
+ new Origin(Origin.root()) {
+ @Override
+ public String part() {
+ return "R8ApiUsageSample";
+ }
+ };
+
+ private static final DiagnosticsHandler handler = new D8DiagnosticsHandler();
+
+ /**
+ * Example invocation:
+ *
+ * <pre>
+ * java -jar r8-api-uses.jar \
+ * --output path/to/output/dir \
+ * --min-api minApiLevel \
+ * --lib path/to/library.jar \
+ * path/to/input{1,2,3}.{jar,class}
+ * </pre>
+ */
+ public static void main(String[] args) {
+ // Parse arguments with the commandline parser to make use of its API.
+ R8Command.Builder cmd = R8Command.parse(args, origin);
+ CompilationMode mode = cmd.getMode();
+ Path temp = cmd.getOutputPath();
+ int minApiLevel = cmd.getMinApiLevel();
+ // The Builder API does not provide access to the concrete paths
+ // (everything is put into providers) so manually parse them here.
+ List<Path> libraries = new ArrayList<>(1);
+ List<Path> mainDexList = new ArrayList<>(1);
+ List<Path> mainDexRules = new ArrayList<>(1);
+ List<Path> pgConf = new ArrayList<>(1);
+ List<Path> inputs = new ArrayList<>(args.length);
+ for (int i = 0; i < args.length; i++) {
+ if (args[i].equals("--lib")) {
+ libraries.add(Paths.get(args[++i]));
+ } else if (args[i].equals("--main-dex-list")) {
+ mainDexList.add(Paths.get(args[++i]));
+ } else if (args[i].equals("--main-dex-rules")) {
+ mainDexRules.add(Paths.get(args[++i]));
+ } else if (args[i].equals("--pg-conf")) {
+ pgConf.add(Paths.get(args[++i]));
+ } else if (isArchive(args[i]) || isClassFile(args[i])) {
+ inputs.add(Paths.get(args[i]));
+ }
+ }
+ if (!Files.exists(temp) || !Files.isDirectory(temp)) {
+ throw new RuntimeException("Must supply a temp/output directory");
+ }
+ if (inputs.isEmpty()) {
+ throw new RuntimeException("Must supply program inputs");
+ }
+ if (libraries.isEmpty()) {
+ throw new RuntimeException("Must supply library inputs");
+ }
+ if (mainDexList.isEmpty()) {
+ throw new RuntimeException("Must supply main-dex-list inputs");
+ }
+ if (mainDexRules.isEmpty()) {
+ throw new RuntimeException("Must supply main-dex-rules inputs");
+ }
+ if (pgConf.isEmpty()) {
+ throw new RuntimeException("Must supply pg-conf inputs");
+ }
+
+ useProgramFileList(CompilationMode.DEBUG, minApiLevel, libraries, inputs);
+ useProgramFileList(CompilationMode.RELEASE, minApiLevel, libraries, inputs);
+ useProgramData(minApiLevel, libraries, inputs);
+ useProgramResourceProvider(minApiLevel, libraries, inputs);
+ useLibraryResourceProvider(minApiLevel, libraries, inputs);
+ useMainDexListFiles(minApiLevel, libraries, inputs, mainDexList);
+ useMainDexClasses(minApiLevel, libraries, inputs, mainDexList);
+ useMainDexRulesFiles(minApiLevel, libraries, inputs, mainDexRules);
+ useMainDexRules(minApiLevel, libraries, inputs, mainDexRules);
+ useProguardConfigFiles(minApiLevel, libraries, inputs, mainDexList, pgConf);
+ useProguardConfigLines(minApiLevel, libraries, inputs, mainDexList, pgConf);
+ useVArgVariants(minApiLevel, libraries, inputs, mainDexList, mainDexRules, pgConf);
+ }
+
+ // Check API support for compiling Java class-files from the file system.
+ private static void useProgramFileList(
+ CompilationMode mode, int minApiLevel, Collection<Path> libraries, Collection<Path> inputs) {
+ try {
+ R8.run(
+ R8Command.builder(handler)
+ .setMode(mode)
+ .setMinApiLevel(minApiLevel)
+ .setProgramConsumer(new EnsureOutputConsumer())
+ .addLibraryFiles(libraries)
+ .addProgramFiles(inputs)
+ .build());
+ } catch (CompilationFailedException e) {
+ throw new RuntimeException("Unexpected compilation exceptions", e);
+ }
+ }
+
+ // Check API support for compiling Java class-files from byte content.
+ private static void useProgramData(
+ int minApiLevel, Collection<Path> libraries, Collection<Path> inputs) {
+ try {
+ R8Command.Builder builder =
+ R8Command.builder(handler)
+ .setMinApiLevel(minApiLevel)
+ .setProgramConsumer(new EnsureOutputConsumer())
+ .addLibraryFiles(libraries);
+ for (ClassFileContent classfile : readClassFiles(inputs)) {
+ builder.addClassProgramData(classfile.data, classfile.origin);
+ }
+ R8.run(builder.build());
+ } catch (CompilationFailedException e) {
+ throw new RuntimeException("Unexpected compilation exceptions", e);
+ } catch (IOException e) {
+ throw new RuntimeException("Unexpected IO exception", e);
+ }
+ }
+
+ // Check API support for compiling Java class-files from a program provider abstraction.
+ private static void useProgramResourceProvider(
+ int minApiLevel, Collection<Path> libraries, Collection<Path> inputs) {
+ try {
+ R8Command.Builder builder =
+ R8Command.builder(handler)
+ .setMinApiLevel(minApiLevel)
+ .setProgramConsumer(new EnsureOutputConsumer())
+ .addLibraryFiles(libraries);
+ for (Path input : inputs) {
+ if (isArchive(input)) {
+ builder.addProgramResourceProvider(
+ ArchiveProgramResourceProvider.fromArchive(
+ input, ArchiveProgramResourceProvider::includeClassFileEntries));
+ } else {
+ builder.addProgramResourceProvider(
+ new ProgramResourceProvider() {
+ @Override
+ public Collection<ProgramResource> getProgramResources() throws ResourceException {
+ return Collections.singleton(ProgramResource.fromFile(Kind.CF, input));
+ }
+ });
+ }
+ }
+ R8.run(builder.build());
+ } catch (CompilationFailedException e) {
+ throw new RuntimeException("Unexpected compilation exceptions", e);
+ }
+ }
+
+ private static void useLibraryResourceProvider(
+ int minApiLevel, Collection<Path> libraries, Collection<Path> inputs) {
+ try {
+ R8Command.Builder builder =
+ R8Command.builder(handler)
+ .setMinApiLevel(minApiLevel)
+ .setProgramConsumer(new EnsureOutputConsumer())
+ .addProgramFiles(inputs);
+ for (Path library : libraries) {
+ builder.addLibraryResourceProvider(new ArchiveClassFileProvider(library));
+ }
+ R8.run(builder.build());
+ } catch (CompilationFailedException e) {
+ throw new RuntimeException("Unexpected compilation exceptions", e);
+ } catch (IOException e) {
+ throw new RuntimeException("Unexpected IO exception", e);
+ }
+ }
+
+ private static void useMainDexListFiles(
+ int minApiLevel,
+ Collection<Path> libraries,
+ Collection<Path> inputs,
+ Collection<Path> mainDexList) {
+ try {
+ R8.run(
+ R8Command.builder(handler)
+ .setMinApiLevel(minApiLevel)
+ .setProgramConsumer(new EnsureOutputConsumer())
+ .addLibraryFiles(libraries)
+ .addProgramFiles(inputs)
+ .addMainDexListFiles(mainDexList)
+ .build());
+ } catch (CompilationFailedException e) {
+ throw new RuntimeException("Unexpected compilation exceptions", e);
+ }
+ }
+
+ private static void useMainDexClasses(
+ int minApiLevel,
+ Collection<Path> libraries,
+ Collection<Path> inputs,
+ Collection<Path> mainDexList) {
+ try {
+ List<String> mainDexClasses = new ArrayList<>(1);
+ for (Path path : mainDexList) {
+ for (String line : Files.readAllLines(path)) {
+ String entry = line.trim();
+ if (entry.isEmpty() || entry.startsWith("#") || !entry.endsWith(".class")) {
+ continue;
+ }
+ mainDexClasses.add(entry.replace(".class", "").replace("/", "."));
+ }
+ }
+ R8.run(
+ R8Command.builder(handler)
+ .setMinApiLevel(minApiLevel)
+ .setProgramConsumer(new EnsureOutputConsumer())
+ .addLibraryFiles(libraries)
+ .addProgramFiles(inputs)
+ .addMainDexClasses(mainDexClasses)
+ .build());
+ } catch (CompilationFailedException e) {
+ throw new RuntimeException("Unexpected compilation exceptions", e);
+ } catch (IOException e) {
+ throw new RuntimeException("Unexpected IO exception", e);
+ }
+ }
+
+ private static void useMainDexRulesFiles(
+ int minApiLevel,
+ Collection<Path> libraries,
+ Collection<Path> inputs,
+ Collection<Path> mainDexRules) {
+ try {
+ R8.run(
+ R8Command.builder(handler)
+ .setMinApiLevel(minApiLevel)
+ .setProgramConsumer(new EnsureOutputConsumer())
+ .addLibraryFiles(libraries)
+ .addProgramFiles(inputs)
+ .addMainDexRulesFiles(mainDexRules)
+ .build());
+ } catch (CompilationFailedException e) {
+ throw new RuntimeException("Unexpected compilation exceptions", e);
+ }
+ }
+
+ private static void useMainDexRules(
+ int minApiLevel,
+ Collection<Path> libraries,
+ Collection<Path> inputs,
+ Collection<Path> mainDexRulesFiles) {
+ try {
+ R8Command.Builder builder =
+ R8Command.builder(handler)
+ .setMinApiLevel(minApiLevel)
+ .setProgramConsumer(new EnsureOutputConsumer())
+ .addLibraryFiles(libraries)
+ .addProgramFiles(inputs);
+ for (Path mainDexRulesFile : mainDexRulesFiles) {
+ builder.addMainDexRules(
+ Files.readAllLines(mainDexRulesFile), new PathOrigin(mainDexRulesFile));
+ }
+ R8.run(builder.build());
+ } catch (CompilationFailedException e) {
+ throw new RuntimeException("Unexpected compilation exceptions", e);
+ } catch (IOException e) {
+ throw new RuntimeException("Unexpected IO exception", e);
+ }
+ }
+
+ private static void useProguardConfigFiles(
+ int minApiLevel,
+ Collection<Path> libraries,
+ Collection<Path> inputs,
+ Collection<Path> mainDexList,
+ List<Path> pgConf) {
+ try {
+ R8.run(
+ R8Command.builder(handler)
+ .setMinApiLevel(minApiLevel)
+ .setProgramConsumer(new EnsureOutputConsumer())
+ .addLibraryFiles(libraries)
+ .addProgramFiles(inputs)
+ .addMainDexListFiles(mainDexList)
+ .addProguardConfigurationFiles(pgConf)
+ .build());
+ } catch (CompilationFailedException e) {
+ throw new RuntimeException("Unexpected compilation exceptions", e);
+ }
+ }
+
+ private static void useProguardConfigLines(
+ int minApiLevel,
+ Collection<Path> libraries,
+ Collection<Path> inputs,
+ Collection<Path> mainDexList,
+ List<Path> pgConf) {
+ try {
+ R8Command.Builder builder =
+ R8Command.builder(handler)
+ .setMinApiLevel(minApiLevel)
+ .setProgramConsumer(new EnsureOutputConsumer())
+ .addLibraryFiles(libraries)
+ .addProgramFiles(inputs)
+ .addMainDexListFiles(mainDexList);
+ for (Path file : pgConf) {
+ builder.addProguardConfiguration(Files.readAllLines(file), new PathOrigin(file));
+ }
+ R8.run(builder.build());
+ } catch (CompilationFailedException e) {
+ throw new RuntimeException("Unexpected compilation exceptions", e);
+ } catch (IOException e) {
+ throw new RuntimeException("Unexpected IO exception", e);
+ }
+ }
+
+ // Check API support for all the varg variants.
+ private static void useVArgVariants(
+ int minApiLevel,
+ List<Path> libraries,
+ List<Path> inputs,
+ List<Path> mainDexList,
+ List<Path> mainDexRules,
+ List<Path> pgConf) {
+ try {
+ R8.run(
+ R8Command.builder(handler)
+ .setMinApiLevel(minApiLevel)
+ .setProgramConsumer(new EnsureOutputConsumer())
+ .addLibraryFiles(libraries.get(0))
+ .addLibraryFiles(libraries.stream().skip(1).toArray(Path[]::new))
+ .addProgramFiles(inputs.get(0))
+ .addProgramFiles(inputs.stream().skip(1).toArray(Path[]::new))
+ .addMainDexListFiles(mainDexList.get(0))
+ .addMainDexListFiles(mainDexList.stream().skip(1).toArray(Path[]::new))
+ .addMainDexRulesFiles(mainDexRules.get(0))
+ .addMainDexRulesFiles(mainDexRules.stream().skip(1).toArray(Path[]::new))
+ .addProguardConfigurationFiles(pgConf.get(0))
+ .addProguardConfigurationFiles(pgConf.stream().skip(1).toArray(Path[]::new))
+ .build());
+ } catch (CompilationFailedException e) {
+ throw new RuntimeException("Unexpected compilation exceptions", e);
+ }
+ }
+
+ // Helpers for tests.
+ // Some of this reimplements stuff in R8 utils, but that is not public API and we should not
+ // rely on it.
+
+ private static List<ClassFileContent> readClassFiles(Collection<Path> files) throws IOException {
+ List<ClassFileContent> classfiles = new ArrayList<>();
+ for (Path file : files) {
+ if (isArchive(file)) {
+ Origin zipOrigin = new PathOrigin(file);
+ ZipInputStream zip = new ZipInputStream(Files.newInputStream(file));
+ ZipEntry entry;
+ while (null != (entry = zip.getNextEntry())) {
+ if (isClassFile(Paths.get(entry.getName()))) {
+ Origin origin = new ArchiveEntryOrigin(entry.getName(), zipOrigin);
+ classfiles.add(new ClassFileContent(origin, readBytes(zip)));
+ }
+ }
+ } else if (isClassFile(file)) {
+ classfiles.add(new ClassFileContent(new PathOrigin(file), Files.readAllBytes(file)));
+ }
+ }
+ return classfiles;
+ }
+
+ private static byte[] readBytes(InputStream stream) throws IOException {
+ try (ByteArrayOutputStream bytes = new ByteArrayOutputStream()) {
+ byte[] buffer = new byte[0xffff];
+ for (int length; (length = stream.read(buffer)) != -1; ) {
+ bytes.write(buffer, 0, length);
+ }
+ return bytes.toByteArray();
+ }
+ }
+
+ private static boolean isClassFile(Path file) {
+ return isClassFile(file.toString());
+ }
+
+ private static boolean isClassFile(String file) {
+ file = file.toLowerCase();
+ return file.endsWith(".class");
+ }
+
+ private static boolean isArchive(Path file) {
+ return isArchive(file.toString());
+ }
+
+ private static boolean isArchive(String file) {
+ file = file.toLowerCase();
+ return file.endsWith(".zip") || file.endsWith(".jar");
+ }
+
+ private static class ClassFileContent {
+ final Origin origin;
+ final byte[] data;
+
+ public ClassFileContent(Origin origin, byte[] data) {
+ this.origin = origin;
+ this.data = data;
+ }
+ }
+
+ private static class EnsureOutputConsumer implements DexIndexedConsumer {
+ boolean hasOutput = false;
+
+ @Override
+ public synchronized void accept(
+ int fileIndex, byte[] data, Set<String> descriptors, DiagnosticsHandler handler) {
+ hasOutput = true;
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ if (!hasOutput) {
+ handler.error(new StringDiagnostic("Expected to produce output but had none"));
+ }
+ }
+ }
+}
diff --git a/src/test/examples/staticinlining/Main.java b/src/test/examples/staticinlining/Main.java
new file mode 100644
index 0000000..dfbafa6
--- /dev/null
+++ b/src/test/examples/staticinlining/Main.java
@@ -0,0 +1,26 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package staticinlining;
+
+public class Main {
+ public static String tag = null;
+
+ public static void printMessage(String msg) {
+ System.out.println(msg);
+ }
+
+ public static void test() {
+ tag = "Sub1";
+ Sub1.inlineMe(); // triggers class init of Sub1 (and its hierarchy)
+ tag = "Sub2";
+ Sub2.doNotInlineMe(); // triggers class init of Sub2 (and its hierarchy)
+ printMessage("SuperClass.INIT_TAG=" + SuperClass.INIT_TAG);
+ }
+
+ public static void main(String[] args) {
+ test();
+ }
+
+}
diff --git a/src/test/examples/staticinlining/Sub1.java b/src/test/examples/staticinlining/Sub1.java
new file mode 100644
index 0000000..2e06601
--- /dev/null
+++ b/src/test/examples/staticinlining/Sub1.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package staticinlining;
+
+import inlining.AlwaysInline;
+
+public class Sub1 extends SuperClass {
+
+ @AlwaysInline
+ public static void inlineMe() {
+ Main.printMessage("Sub1");
+ }
+
+}
diff --git a/src/test/examples/staticinlining/Sub2.java b/src/test/examples/staticinlining/Sub2.java
new file mode 100644
index 0000000..e253f79
--- /dev/null
+++ b/src/test/examples/staticinlining/Sub2.java
@@ -0,0 +1,22 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package staticinlining;
+
+public class Sub2 extends SuperClass {
+
+ public static void doNotInlineMe() {
+ System.out.println("Do not inline me 1");
+ System.out.println("Do not inline me 2");
+ System.out.println("Do not inline me 3");
+ System.out.println("Do not inline me 4");
+ System.out.println("Do not inline me 5");
+ System.out.println("Do not inline me 6");
+ System.out.println("Do not inline me 7");
+ System.out.println("Do not inline me 8");
+ System.out.println("Do not inline me 9");
+ System.out.println("Do not inline me 10");
+ }
+
+}
diff --git a/src/test/examples/staticinlining/SuperClass.java b/src/test/examples/staticinlining/SuperClass.java
new file mode 100644
index 0000000..237c11d
--- /dev/null
+++ b/src/test/examples/staticinlining/SuperClass.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package staticinlining;
+
+public class SuperClass {
+ public static final String INIT_TAG = Main.tag;
+}
diff --git a/src/test/examples/staticinlining/keep-rules.txt b/src/test/examples/staticinlining/keep-rules.txt
new file mode 100644
index 0000000..1de091f
--- /dev/null
+++ b/src/test/examples/staticinlining/keep-rules.txt
@@ -0,0 +1,13 @@
+# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class staticinlining.Main {
+ public static void main(...);
+}
+
+-alwaysinline class * {
+ @inlining.AlwaysInline <methods>;
+}
diff --git a/src/test/java/com/android/tools/r8/D8APiBinaryCompatibilityTests.java b/src/test/java/com/android/tools/r8/D8APiBinaryCompatibilityTests.java
deleted file mode 100644
index 5a3b0f8..0000000
--- a/src/test/java/com/android/tools/r8/D8APiBinaryCompatibilityTests.java
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8;
-
-import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
-
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import com.google.common.collect.ImmutableList;
-import com.google.common.io.Files;
-import java.io.File;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.List;
-import org.junit.Assert;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-
-public class D8APiBinaryCompatibilityTests {
-
- @Rule
- public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
-
- @Test
- public void testCompatibility() throws IOException {
- Path compilerJar = Paths.get("tests", "api_usage_sample.jar");
- String compiler = "com.android.tools.apiusagesample.D8Compiler";
-
- String output = temp.newFolder().getAbsolutePath();
- int minSdkVersion = AndroidApiLevel.K.getLevel();
- String androidJar = ToolHelper.getAndroidJar(minSdkVersion);
- Path lib1 = Paths.get(ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR,
- "desugaringwithmissingclasslib1" + JAR_EXTENSION);
- Path lib2 = Paths.get(ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR,
- "desugaringwithmissingclasslib2" + JAR_EXTENSION);
- Path input = Paths.get(ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR,
- "classes", "desugaringwithmissingclasstest1");
- File mainDexClasses = temp.newFile();
- Files.asCharSink(mainDexClasses, StandardCharsets.UTF_8)
- .write("desugaringwithmissingclasstest1/Main.class");
-
- List<String> command = ImmutableList.of(
- ToolHelper.getJavaExecutable(),
- "-cp",
- compilerJar.toString() + File.pathSeparator + System.getProperty("java.class.path"),
- compiler,
- // Compiler arguments.
- output,
- input.toString(),
- Integer.toString(minSdkVersion),
- mainDexClasses.getAbsolutePath(),
- androidJar,
- lib1.toString(),
- lib2.toString());
- ProcessBuilder builder = new ProcessBuilder(command);
- ProcessResult result = ToolHelper.runProcess(builder);
-
- Assert.assertEquals(result.stderr + "\n" + result.stdout, 0, result.exitCode);
- }
-}
diff --git a/src/test/java/com/android/tools/r8/D8ApiBinaryCompatibilityTests.java b/src/test/java/com/android/tools/r8/D8ApiBinaryCompatibilityTests.java
new file mode 100644
index 0000000..86f0319
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/D8ApiBinaryCompatibilityTests.java
@@ -0,0 +1,121 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.FileUtils;
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.Files;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class D8ApiBinaryCompatibilityTests {
+
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ @Test
+ public void testCompatibilityDeprecatedApi() throws IOException {
+ Path compilerJar = Paths.get("tests", "api_usage_sample.jar");
+ String compiler = "com.android.tools.apiusagesample.D8Compiler";
+ String output = temp.newFolder().getAbsolutePath();
+ int minSdkVersion = AndroidApiLevel.K.getLevel();
+ String androidJar = ToolHelper.getAndroidJar(minSdkVersion);
+ Path lib1 = Paths.get(ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR,
+ "desugaringwithmissingclasslib1" + JAR_EXTENSION);
+ Path lib2 = Paths.get(ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR,
+ "desugaringwithmissingclasslib2" + JAR_EXTENSION);
+ Path input = Paths.get(ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR,
+ "classes", "desugaringwithmissingclasstest1");
+ File mainDexClasses = temp.newFile();
+ Files.asCharSink(mainDexClasses, StandardCharsets.UTF_8)
+ .write("desugaringwithmissingclasstest1/Main.class");
+
+ List<String> command = ImmutableList.of(
+ ToolHelper.getJavaExecutable(),
+ "-cp",
+ compilerJar.toString() + File.pathSeparator + System.getProperty("java.class.path"),
+ compiler,
+ // Compiler arguments.
+ output,
+ input.toString(),
+ Integer.toString(minSdkVersion),
+ mainDexClasses.getAbsolutePath(),
+ androidJar,
+ lib1.toString(),
+ lib2.toString());
+ ProcessBuilder builder = new ProcessBuilder(command);
+ ProcessResult result = ToolHelper.runProcess(builder);
+ Assert.assertEquals(result.stderr + "\n" + result.stdout, 0, result.exitCode);
+ Assert.assertTrue(result.stdout, result.stdout.isEmpty());
+ Assert.assertTrue(result.stderr, result.stderr.isEmpty());
+ }
+
+ @Test
+ public void testCompatibilityNewApi() throws IOException {
+ Path jar = Paths.get("tests", "d8_api_usage_sample.jar");
+ String main = "com.android.tools.apiusagesample.D8ApiUsageSample";
+ int minApiLevel = AndroidApiLevel.K.getLevel();
+
+ Path lib1 =
+ Paths.get(
+ ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR,
+ "desugaringwithmissingclasslib1" + JAR_EXTENSION);
+ Path lib2 =
+ Paths.get(
+ ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR,
+ "desugaringwithmissingclasslib2" + JAR_EXTENSION);
+ Path inputDir =
+ Paths.get(
+ ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR, "classes", "desugaringwithmissingclasstest1");
+ List<Path> input =
+ ImmutableList.of(
+ inputDir.resolve("ImplementMethodsWithDefault.class"), inputDir.resolve("Main.class"));
+
+ Path mainDexList = temp.getRoot().toPath().resolve("maindexlist.txt");
+ FileUtils.writeTextFile(mainDexList, "desugaringwithmissingclasstest1/Main.class");
+
+ List<String> command =
+ ImmutableList.<String>builder()
+ .addAll(
+ ImmutableList.of(
+ ToolHelper.getJavaExecutable(),
+ "-cp",
+ jar.toString() + File.pathSeparator + System.getProperty("java.class.path"),
+ main,
+ // Compiler arguments.
+ "--output",
+ temp.newFolder().getAbsolutePath(),
+ "--min-api",
+ Integer.toString(minApiLevel),
+ "--main-dex-list",
+ mainDexList.toString(),
+ "--lib",
+ ToolHelper.getAndroidJar(minApiLevel),
+ "--classpath",
+ lib1.toString(),
+ "--classpath",
+ lib2.toString()))
+ .addAll(input.stream().map(Path::toString).collect(Collectors.toList()))
+ .build();
+
+ ProcessBuilder builder = new ProcessBuilder(command);
+ ProcessResult result = ToolHelper.runProcess(builder);
+ Assert.assertEquals(result.stderr + "\n" + result.stdout, 0, result.exitCode);
+ Assert.assertTrue(result.stdout, result.stdout.isEmpty());
+ Assert.assertTrue(result.stderr, result.stderr.isEmpty());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index 7eb73df..f263d4e 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -23,6 +23,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
+import java.util.Set;
import java.util.zip.ZipFile;
import org.junit.Ignore;
import org.junit.Rule;
@@ -58,8 +59,8 @@
assertEquals(AndroidVersion.DEFAULT.getApiLevel(), command.getMinApiLevel());
assertTrue(command.getProgramConsumer() instanceof DexIndexedConsumer);
AndroidApp app = ToolHelper.getApp(command);
- assertEquals(0, app.getDexProgramResources().size());
- assertEquals(0, app.getClassProgramResources().size());
+ assertEquals(0, app.getDexProgramResourcesForTesting().size());
+ assertEquals(0, app.getClassProgramResourcesForTesting().size());
}
@Test
@@ -257,7 +258,10 @@
@Test(expected = CompilationFailedException.class)
public void vdexFileUnsupported() throws Throwable {
Path vdexFile = temp.newFile("test.vdex").toPath();
- D8Command.builder().addProgramFiles(vdexFile).build();
+ D8Command.builder()
+ .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+ .addProgramFiles(vdexFile)
+ .build();
}
@Test
@@ -294,6 +298,31 @@
}
}
+ @Test(expected = CompilationFailedException.class)
+ public void addMultiTypeProgramConsumer() throws CompilationFailedException {
+ class MultiTypeConsumer implements DexIndexedConsumer, DexFilePerClassFileConsumer {
+
+ @Override
+ public void accept(String primaryClassDescriptor, byte[] data, Set<String> descriptors,
+ DiagnosticsHandler handler) {
+
+ }
+
+ @Override
+ public void accept(int fileIndex, byte[] data, Set<String> descriptors,
+ DiagnosticsHandler handler) {
+
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+
+ }
+ }
+
+ D8Command.builder().setProgramConsumer(new MultiTypeConsumer()).build();
+ }
+
private D8Command parse(String... args) throws CompilationFailedException {
return D8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
}
diff --git a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
index 29ece33..8cfdee8 100644
--- a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
@@ -65,10 +65,10 @@
for (String classFile : classFiles) {
AndroidApp app = compileClassFiles(
testJarFile, Collections.singletonList(classFile), null, OutputMode.Indexed);
- assert app.getDexProgramResources().size() == 1;
+ assert app.getDexProgramResourcesForTesting().size() == 1;
fileToResource.put(
makeRelative(testJarFile, Paths.get(classFile)).toString(),
- app.getDexProgramResources().get(0));
+ app.getDexProgramResourcesForTesting().get(0));
}
return fileToResource;
}
@@ -80,7 +80,7 @@
List<String> classFiles = collectClassFiles(testJarFile);
AndroidApp app = compileClassFiles(
testJarFile, classFiles, output, OutputMode.FilePerInputClass);
- for (ProgramResource resource : app.getDexProgramResources()) {
+ for (ProgramResource resource : app.getDexProgramResourcesForTesting()) {
Set<String> descriptors = resource.getClassDescriptors();
String mainClassDescriptor = app.getPrimaryClassDescriptor(resource);
Assert.assertNotNull(mainClassDescriptor);
@@ -197,8 +197,8 @@
D8Command command = builder.build();
try {
AndroidApp app = ToolHelper.runD8(command, this::combinedOptionConsumer);
- assert app.getDexProgramResources().size() == 1;
- return app.getDexProgramResources().get(0);
+ assert app.getDexProgramResourcesForTesting().size() == 1;
+ return app.getDexProgramResourcesForTesting().get(0);
} catch (Unimplemented | CompilationError | InternalCompilerError re) {
throw re;
} catch (RuntimeException re) {
diff --git a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
index 2c723ff..36f92b4 100644
--- a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
@@ -110,13 +110,13 @@
options.interfaceMethodDesugaring = OffOrAuto.Auto;
options.setMarker(null);
});
- individalDexes.add(individualResult.getDexProgramResources().get(0));
+ individalDexes.add(individualResult.getDexProgramResourcesForTesting().get(0));
}
AndroidApp mergedResult = mergeDexResources(minAPILevel, individalDexes);
assertTrue(Arrays.equals(
- readResource(fullBuildResult.getDexProgramResources().get(0)),
- readResource(mergedResult.getDexProgramResources().get(0))));
+ readResource(fullBuildResult.getDexProgramResourcesForTesting().get(0)),
+ readResource(mergedResult.getDexProgramResourcesForTesting().get(0))));
}
private AndroidApp mergeDexResources(int minAPILevel, List<Resource> individalDexes)
diff --git a/src/test/java/com/android/tools/r8/R8ApiBinaryCompatibilityTests.java b/src/test/java/com/android/tools/r8/R8ApiBinaryCompatibilityTests.java
new file mode 100644
index 0000000..898e691
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/R8ApiBinaryCompatibilityTests.java
@@ -0,0 +1,75 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.FileUtils;
+import com.google.common.collect.ImmutableList;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class R8ApiBinaryCompatibilityTests {
+
+ static final Path JAR = Paths.get("tests", "r8_api_usage_sample.jar");
+ static final String MAIN = "com.android.tools.apiusagesample.R8ApiUsageSample";
+ static final int MIN_API = AndroidApiLevel.K.getLevel();
+
+ @Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ @Test
+ public void testCompatibility() throws IOException {
+ List<Path> inputs =
+ ImmutableList.of(Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "arithmetic.jar"));
+
+ Path pgConf = temp.getRoot().toPath().resolve("pg.conf");
+ FileUtils.writeTextFile(
+ pgConf, TestBase.keepMainProguardConfiguration("arithmetic.Arithmetic"));
+
+ Path mainDexRules = temp.getRoot().toPath().resolve("maindex.rules");
+ FileUtils.writeTextFile(
+ mainDexRules, TestBase.keepMainProguardConfiguration("arithmetic.Arithmetic"));
+
+ Path mainDexList = temp.getRoot().toPath().resolve("maindexlist.txt");
+ FileUtils.writeTextFile(mainDexList, "arithmetic/Arithmetic.class");
+
+ List<String> command =
+ ImmutableList.<String>builder()
+ .addAll(
+ ImmutableList.of(
+ ToolHelper.getJavaExecutable(),
+ "-cp",
+ JAR.toString() + File.pathSeparator + System.getProperty("java.class.path"),
+ MAIN,
+ // Compiler arguments.
+ "--output",
+ temp.newFolder().toString(),
+ "--min-api",
+ Integer.toString(MIN_API),
+ "--pg-conf",
+ pgConf.toString(),
+ "--main-dex-rules",
+ mainDexRules.toString(),
+ "--main-dex-list",
+ mainDexList.toString(),
+ "--lib",
+ ToolHelper.getAndroidJar(MIN_API)))
+ .addAll(inputs.stream().map(Path::toString).collect(Collectors.toList()))
+ .build();
+
+ ProcessBuilder builder = new ProcessBuilder(command);
+ ProcessResult result = ToolHelper.runProcess(builder);
+ Assert.assertEquals(result.stderr + "\n" + result.stdout, 0, result.exitCode);
+ Assert.assertTrue(result.stdout, result.stdout.isEmpty());
+ Assert.assertTrue(result.stderr, result.stderr.isEmpty());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/R8CodeCanonicalizationTest.java b/src/test/java/com/android/tools/r8/R8CodeCanonicalizationTest.java
index 8c9d2be..ae44a5a 100644
--- a/src/test/java/com/android/tools/r8/R8CodeCanonicalizationTest.java
+++ b/src/test/java/com/android/tools/r8/R8CodeCanonicalizationTest.java
@@ -17,7 +17,8 @@
public class R8CodeCanonicalizationTest {
- private static final String SOURCE_DEX = "invokeempty/classes.dex";
+ private static final Path SOURCE_DEX = Paths.get(
+ ToolHelper.EXAMPLES_BUILD_DIR, "invokeempty", "classes.dex");
private int readNumberOfCodes(Path file) throws IOException {
Segment[] segments = DexFileReader.parseMapFrom(file);
@@ -34,13 +35,12 @@
@Test
public void testNumberOfCodeItemsUnchanged() throws Exception {
- int numberOfCodes = readNumberOfCodes(Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + SOURCE_DEX));
- R8.run(
- R8Command.builder()
- .addProgramFiles(Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + SOURCE_DEX))
- .addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()))
- .setOutput(temp.getRoot().toPath(), OutputMode.DexIndexed)
- .build());
+ int numberOfCodes = readNumberOfCodes(SOURCE_DEX);
+ R8Command.Builder builder = R8Command.builder()
+ .addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()))
+ .setOutput(temp.getRoot().toPath(), OutputMode.DexIndexed);
+ ToolHelper.getAppBuilder(builder).addProgramFiles(SOURCE_DEX);
+ R8.run(builder.build());
int newNumberOfCodes = readNumberOfCodes(
Paths.get(temp.getRoot().getCanonicalPath(), "classes.dex"));
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java
index 9f4a0b1..1e79404 100644
--- a/src/test/java/com/android/tools/r8/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -6,16 +6,22 @@
import static com.android.tools.r8.ToolHelper.EXAMPLES_BUILD_DIR;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.origin.EmbeddedOrigin;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.OutputMode;
import com.google.common.collect.ImmutableList;
+import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;
@@ -23,6 +29,44 @@
public class R8CommandTest {
+ // Helper to check that a particular error occurred.
+ static class DiagnosticsChecker implements DiagnosticsHandler {
+ public List<Diagnostic> errors = new ArrayList<>();
+ public List<Diagnostic> warnings = new ArrayList<>();
+ public List<Diagnostic> infos = new ArrayList<>();
+
+ @Override
+ public void error(Diagnostic error) {
+ errors.add(error);
+ }
+
+ @Override
+ public void warning(Diagnostic warning) {
+ warnings.add(warning);
+ }
+
+ @Override
+ public void info(Diagnostic info) {
+ infos.add(info);
+ }
+
+ public interface FailingRunner {
+ void run(DiagnosticsHandler handler) throws CompilationFailedException;
+ }
+
+ public static void checkErrorsContains(String snippet, FailingRunner runner)
+ throws CompilationFailedException {
+ DiagnosticsChecker handler = new DiagnosticsChecker();
+ try {
+ runner.run(handler);
+ } catch (CompilationFailedException e) {
+ assertTrue(handler.errors.stream()
+ .anyMatch(d -> d.getDiagnosticMessage().contains(snippet)));
+ throw e;
+ }
+ }
+ }
+
@Rule
public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
@@ -47,8 +91,8 @@
}
private void verifyEmptyCommand(R8Command command) throws Throwable {
- assertEquals(0, ToolHelper.getApp(command).getDexProgramResources().size());
- assertEquals(0, ToolHelper.getApp(command).getClassProgramResources().size());
+ assertEquals(0, ToolHelper.getApp(command).getDexProgramResourcesForTesting().size());
+ assertEquals(0, ToolHelper.getApp(command).getClassProgramResourcesForTesting().size());
assertFalse(command.useMinification());
assertFalse(command.useTreeShaking());
assertEquals(CompilationMode.RELEASE, command.getMode());
@@ -255,7 +299,7 @@
@Test
public void argumentsInFile() throws Throwable {
- Path inputFile = temp.newFile("foobar.dex").toPath();
+ Path inputFile = temp.newFile("foobar.class").toPath();
Path pgConfFile = temp.newFile("pgconf.config").toPath();
Path argsFile = temp.newFile("more-args.txt").toPath();
FileUtils.writeTextFile(argsFile, ImmutableList.of(
@@ -267,7 +311,7 @@
assertEquals(CompilationMode.DEBUG, command.getMode());
assertFalse(command.useMinification());
assertFalse(command.useTreeShaking()); // We have no keep rules (proguard config file is empty).
- assertEquals(1, ToolHelper.getApp(command).getDexProgramResources().size());
+ assertEquals(1, ToolHelper.getApp(command).getClassProgramResourcesForTesting().size());
}
@Test
@@ -277,9 +321,46 @@
}
@Test(expected = CompilationFailedException.class)
+ public void dexFileUnsupported() throws Throwable {
+ Path dexFile = temp.newFile("test.dex").toPath();
+ DiagnosticsChecker.checkErrorsContains("DEX input", handler ->
+ R8Command
+ .builder(handler)
+ .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+ .addProgramFiles(dexFile)
+ .build());
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void dexProviderUnsupported() throws Throwable {
+ Path dexFile = temp.newFile("test.dex").toPath();
+ DiagnosticsChecker.checkErrorsContains("DEX input", handler ->
+ R8.run(R8Command
+ .builder(handler)
+ .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+ .addProgramResourceProvider(new ProgramResourceProvider() {
+ @Override
+ public Collection<ProgramResource> getProgramResources() throws ResourceException {
+ return Collections.singleton(ProgramResource.fromFile(Kind.DEX, dexFile));
+ }
+ })
+ .build()));
+ }
+
+ @Test
+ public void dexDataUnsupported() throws Throwable {
+ for (Method method : R8Command.Builder.class.getMethods()) {
+ assertNotEquals("addDexProgramData", method.getName());
+ }
+ }
+
+ @Test(expected = CompilationFailedException.class)
public void vdexFileUnsupported() throws Throwable {
Path vdexFile = temp.newFile("test.vdex").toPath();
- D8Command.builder().addProgramFiles(vdexFile).build();
+ R8Command.builder()
+ .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+ .addProgramFiles(vdexFile)
+ .build();
}
private R8Command parse(String... args) throws CompilationFailedException {
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 33fab08..569beda 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -87,6 +87,7 @@
private static final String ART_LEGACY_TESTS_NATIVE_LIBRARY_DIR = "tests/2016-12-19/art/lib64";
private static final RuntimeSet LEGACY_RUNTIME = TestCondition.runtimes(
+ DexVm.Version.V4_0_4,
DexVm.Version.V4_4_4,
DexVm.Version.V5_1_1,
DexVm.Version.V6_0_1,
@@ -160,7 +161,7 @@
.put("800-smali", TestCondition.match(TestCondition.runtimes(DexVm.Version.V5_1_1)))
// Hangs on dalvik.
.put("802-deoptimization",
- TestCondition.match(TestCondition.runtimes(DexVm.Version.V4_4_4)))
+ TestCondition.match(TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
.build();
// Tests that are flaky with the Art version we currently use.
@@ -190,10 +191,10 @@
// Failed on buildbot with: terminate called after throwing an instance
// of '__gnu_cxx::recursive_init_error'
.put("096-array-copy-concurrent-gc",
- TestCondition.match(TestCondition.runtimes(DexVm.Version.V4_4_4)))
+ TestCondition.match(TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
// Sometimes fails with out of memory on Dalvik.
.put("114-ParallelGC",
- TestCondition.match(TestCondition.runtimes(DexVm.Version.V4_4_4)))
+ TestCondition.match(TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
// Seen crash: currently no more information
.put("144-static-field-sigquit", TestCondition.any())
// Opens a lot of file descriptors and depending on the state of the machine this
@@ -521,12 +522,34 @@
"594-invoke-super",
"605-new-string-from-bytes",
"626-const-class-linking"
+ ),
+ DexVm.Version.V4_0_4, ImmutableList.of(
+ // Generally fails on non R8/D8 running.
+ "004-checker-UnsafeTest18",
+ "004-NativeAllocations",
+ "005-annotations",
+ "008-exceptions",
+ "082-inline-execute",
+ "099-vmdebug",
+ "143-string-value",
+ "530-checker-lse2",
+ "536-checker-intrinsic-optimization",
+ "552-invoke-non-existent-super",
+ "580-checker-round",
+ "580-checker-string-fact-intrinsics",
+ "594-invoke-super",
+ "605-new-string-from-bytes",
+ "626-const-class-linking"
)
);
// Tests where the R8/D8 output runs in Art but the original does not.
private static Multimap<String, TestCondition> failingRunWithArtOriginalOnly =
- new ImmutableListMultimap.Builder<String, TestCondition>().build();
+ new ImmutableListMultimap.Builder<String, TestCondition>()
+ .put("095-switch-MAX_INT",
+ TestCondition.match(
+ TestCondition.runtimes(DexVm.Version.V4_0_4)))
+ .build();
// Tests where the output of R8 fails when run with Art.
private static final Multimap<String, TestCondition> failingRunWithArt =
@@ -535,7 +558,7 @@
.put("064-field-access",
TestCondition.match(
TestCondition.R8_NOT_AFTER_D8_COMPILER,
- TestCondition.runtimes(DexVm.Version.V4_4_4)))
+ TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
.put("064-field-access",
TestCondition.match(
TestCondition.R8_COMPILER,
@@ -559,56 +582,56 @@
// Dalvik fails on reading an uninitialized local.
.put(
"471-uninitialized-locals",
- TestCondition.match(TestCondition.runtimes(DexVm.Version.V4_4_4)))
+ TestCondition.match(TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
// Out of memory.
.put("152-dead-large-object",
- TestCondition.match(TestCondition.runtimes(DexVm.Version.V4_4_4)))
+ TestCondition.match(TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
// Cannot resolve exception handler. Interestingly, D8 generates different code in
// release mode (which is also the code generated by R8) which passes.
.put("111-unresolvable-exception",
TestCondition.match(
TestCondition.D8_COMPILER,
- TestCondition.runtimes(DexVm.Version.V4_4_4)))
+ TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
.put("534-checker-bce-deoptimization",
TestCondition
.match(TestCondition.D8_COMPILER, TestCondition.runtimes(DexVm.Version.V6_0_1)))
// Type not present.
.put("124-missing-classes",
- TestCondition.match(TestCondition.runtimes(DexVm.Version.V4_4_4)))
+ TestCondition.match(TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
// Failed creating vtable.
.put("587-inline-class-error",
- TestCondition.match(TestCondition.runtimes(DexVm.Version.V4_4_4)))
+ TestCondition.match(TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
// Failed creating vtable.
.put("595-error-class",
- TestCondition.match(TestCondition.runtimes(DexVm.Version.V4_4_4)))
+ TestCondition.match(TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
// NoSuchFieldException: systemThreadGroup on Art 4.4.4.
.put("129-ThreadGetId",
- TestCondition.match(TestCondition.runtimes(DexVm.Version.V4_4_4)))
+ TestCondition.match(TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
// Verifier says: can't modify final field LMain;.staticFinalField.
.put("600-verifier-fails",
- TestCondition.match(TestCondition.runtimes(DexVm.Version.V4_4_4)))
+ TestCondition.match(TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
// VFY: args to if-eq/if-ne must both be refs or cat1.
.put("134-reg-promotion",
TestCondition.match(
TestCondition.R8_COMPILER,
- TestCondition.runtimes(DexVm.Version.V4_4_4)))
+ TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
.put("134-reg-promotion",
TestCondition.match(
TestCondition.tools(DexTool.NONE, DexTool.JACK),
TestCondition.D8_COMPILER,
- TestCondition.runtimes(DexVm.Version.V4_4_4)))
+ TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
// VFY: tried to get class from non-ref register.
.put("506-verify-aput",
- TestCondition.match(TestCondition.runtimes(DexVm.Version.V4_4_4)))
+ TestCondition.match(TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
// NoSuchMethod: startMethodTracing.
.put("545-tracing-and-jit",
- TestCondition.match(TestCondition.runtimes(DexVm.Version.V4_4_4)))
+ TestCondition.match(TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
// filled-new-array arg 0(1) not valid.
.put("412-new-array",
- TestCondition.match(TestCondition.runtimes(DexVm.Version.V4_4_4)))
+ TestCondition.match(TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
// TODO(ager): unclear what is failing here.
.put("098-ddmc",
- TestCondition.match(TestCondition.runtimes(DexVm.Version.V4_4_4)))
+ TestCondition.match(TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
// Unsatisfiable link error:
// libarttest.so: undefined symbol: _ZN3art6Thread18RunEmptyCheckpointEv
.put("543-env-long-ref",
@@ -618,7 +641,7 @@
.runtimes(DexVm.Version.V7_0_0, DexVm.Version.V6_0_1, DexVm.Version.V5_1_1)))
// lib64 libarttest.so: wrong ELF class ELFCLASS64.
.put("543-env-long-ref",
- TestCondition.match(TestCondition.runtimes(DexVm.Version.V4_4_4)))
+ TestCondition.match(TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
// Regression test for an issue that is not fixed on version 5.1.1. Throws an Exception
// instance instead of the expected NullPointerException. This bug is only tickled when
// running the R8 generated code when starting from jar or from dex code generated with
@@ -640,7 +663,23 @@
// some inlining. The generated code is correct and hence runs on newer versions.
.put("077-method-override",
TestCondition.match(TestCondition.compilers(CompilerUnderTest.R8),
- TestCondition.runtimes(DexVm.Version.V4_4_4)))
+ TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
+ // Dalvik 4.0.4 is missing ReflectiveOperationException class.
+ .put("140-field-packing",
+ TestCondition.match(
+ TestCondition.runtimes(DexVm.Version.V4_0_4)))
+ // Dalvik 4.0.4 is missing theUnsafe field.
+ .put("528-long-hint",
+ TestCondition.match(
+ TestCondition.runtimes(DexVm.Version.V4_0_4)))
+ // Cannot catch exception in Dalvik 4.0.4.
+ .put("084-class-init",
+ TestCondition.match(
+ TestCondition.runtimes(DexVm.Version.V4_0_4)))
+ // Tested regression still exists in Dalvik 4.0.4.
+ .put("301-abstract-protected",
+ TestCondition.match(
+ TestCondition.runtimes(DexVm.Version.V4_0_4)))
.build();
// Tests where the output of R8/D8 runs in Art but produces different output than the expected.txt
@@ -651,46 +690,44 @@
.put("072-precise-gc",
TestCondition.match(
TestCondition.R8_COMPILER,
- TestCondition.runtimes(DexVm.Version.V4_4_4)))
+ TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
.put("072-precise-gc",
TestCondition.match(
TestCondition.tools(DexTool.JACK, DexTool.NONE),
TestCondition.D8_COMPILER,
- TestCondition.runtimes(DexVm.Version.V4_4_4)))
+ TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
// This one is expected to have different output. It counts instances, but the list that
// keeps the instances alive is dead and could be garbage collected. The compiler reuses
// the register for the list and therefore there are no live instances.
.put("099-vmdebug", TestCondition.any())
// This test relies on output on stderr, which we currently do not collect.
.put("143-string-value", TestCondition.any())
- .put(
- "800-smali",
+ .put("800-smali",
TestCondition.match(
TestCondition.D8_COMPILER,
TestCondition.runtimes(DexVm.Version.V5_1_1, DexVm.Version.V6_0_1)))
// Triggers regression test in 6.0.1 when using R8/D8 in debug mode.
- .put(
- "474-fp-sub-neg",
+ .put("474-fp-sub-neg",
TestCondition.match(
TestCondition.tools(DexTool.NONE, DexTool.JACK),
TestCondition.D8_COMPILER,
TestCondition.runtimes(DexVm.Version.V6_0_1)))
+ // Produces wrong output
+ .put("015-switch",
+ TestCondition.match(
+ TestCondition.runtimes(DexVm.Version.V4_0_4)))
.build();
private static final TestCondition beforeAndroidN =
TestCondition
.match(TestCondition
- .runtimes(DexVm.Version.V4_4_4, DexVm.Version.V5_1_1, DexVm.Version.V6_0_1));
+ .runtimes(DexVm.Version.V4_0_4, DexVm.Version.V4_4_4, DexVm.Version.V5_1_1,
+ DexVm.Version.V6_0_1));
private static final TestCondition beforeAndroidO =
- TestCondition.match(
- TestCondition.runtimes(
- DexVm.Version.V4_4_4, DexVm.Version.V5_1_1, DexVm.Version.V6_0_1,
- DexVm.Version.V7_0_0));
+ TestCondition.match(TestCondition.runtimesUpTo(DexVm.Version.V7_0_0));
+ // TODO(herhut): Change to V8_0_0 once we have a new art VM.
private static final TestCondition beforeAndroidP =
- TestCondition.match(
- TestCondition.runtimes(
- DexVm.Version.V4_4_4, DexVm.Version.V5_1_1, DexVm.Version.V6_0_1,
- DexVm.Version.V7_0_0));
+ TestCondition.match(TestCondition.runtimesUpTo(DexVm.Version.V7_0_0));
// TODO(ager): Could we test that these fail in the way that we expect?
private static final Multimap<String, TestCondition> expectedToFailRunWithArt =
@@ -720,7 +757,8 @@
TestCondition.tools(DexTool.JACK, DexTool.DX),
TestCondition.compilers(CompilerUnderTest.R8, CompilerUnderTest.D8),
TestCondition
- .runtimes(DexVm.Version.V4_4_4, DexVm.Version.V5_1_1, DexVm.Version.V6_0_1)))
+ .runtimes(DexVm.Version.V4_0_4, DexVm.Version.V4_4_4, DexVm.Version.V5_1_1,
+ DexVm.Version.V6_0_1)))
// Array index out of bounds exception.
.put("449-checker-bce", TestCondition.any())
// Fails: get_vreg_jni.cc:46] Check failed: value == 42u (value=314630384, 42u=42)
@@ -729,8 +767,8 @@
.put(
"454-get-vreg",
TestCondition.match(
- TestCondition.runtimes(DexVm.Version.V4_4_4, DexVm.Version.V5_1_1,
- DexVm.Version.V6_0_1, DexVm.Version.V7_0_0)))
+ TestCondition.runtimes(DexVm.Version.V4_0_4, DexVm.Version.V4_4_4,
+ DexVm.Version.V5_1_1, DexVm.Version.V6_0_1, DexVm.Version.V7_0_0)))
.put(
"454-get-vreg",
TestCondition.match(
@@ -743,8 +781,8 @@
.put(
"457-regs",
TestCondition.match(
- TestCondition.runtimes(DexVm.Version.V4_4_4, DexVm.Version.V5_1_1,
- DexVm.Version.V6_0_1, DexVm.Version.V7_0_0)))
+ TestCondition.runtimes(DexVm.Version.V4_0_4, DexVm.Version.V4_4_4,
+ DexVm.Version.V5_1_1, DexVm.Version.V6_0_1, DexVm.Version.V7_0_0)))
.put(
"457-regs",
TestCondition.match(
@@ -808,12 +846,14 @@
.put("972-iface-super-multidex",
TestCondition.match(TestCondition.tools(DexTool.JACK, DexTool.DX),
TestCondition
- .runtimes(DexVm.Version.V4_4_4, DexVm.Version.V5_1_1, DexVm.Version.V6_0_1)))
+ .runtimes(DexVm.Version.V4_0_4, DexVm.Version.V4_4_4, DexVm.Version.V5_1_1,
+ DexVm.Version.V6_0_1)))
// Uses dex file version 37 and therefore only runs on Android N and above.
.put("978-virtual-interface",
TestCondition.match(TestCondition.tools(DexTool.JACK, DexTool.DX),
TestCondition
- .runtimes(DexVm.Version.V4_4_4, DexVm.Version.V5_1_1, DexVm.Version.V6_0_1)))
+ .runtimes(DexVm.Version.V4_0_4, DexVm.Version.V4_4_4, DexVm.Version.V5_1_1,
+ DexVm.Version.V6_0_1)))
.put("979-const-method-handle", beforeAndroidP)
.build();
@@ -857,12 +897,8 @@
.put("800-smali", TestCondition.match(TestCondition.R8_COMPILER))
// Contains a loop in the class hierarchy.
.put("804-class-extends-itself", TestCondition.any())
- // It is not possible to compute target of method call due to ambiguous methods, thus fail
- // to generate one dex from several dex inputs that represent an invalid program.
- .put("004-JniTest", TestCondition.match(TestCondition.R8_COMPILER))
// These tests have illegal class flag combinations, so we reject them.
.put("161-final-abstract-class", TestCondition.any())
- .put("004-JniTest", TestCondition.any())
.build();
// Tests that does not have dex input for some toolchains.
@@ -1233,7 +1269,7 @@
// Skip all tests compiled to dex with jack on Dalvik. They have a too high dex
// version number in the generated output.
boolean skip = skipTest.contains(name) ||
- (dexTool == DexTool.JACK && version == DexVm.Version.V4_4_4);
+ (dexTool == DexTool.JACK && version.isOlderThanOrEqual(DexVm.Version.V4_4_4));
// All the native code for all Art tests is currently linked into the
// libarttest.so file.
data.put(
@@ -1360,8 +1396,9 @@
R8Command.Builder builder =
R8Command.builder()
.setMode(mode)
- .setOutput(Paths.get(resultPath), OutputMode.DexIndexed)
- .addProgramFiles(ListUtils.map(fileNames, Paths::get));
+ .setOutput(Paths.get(resultPath), OutputMode.DexIndexed);
+ // Add program files directly to the underlying app to avoid errors on DEX inputs.
+ ToolHelper.getAppBuilder(builder).addProgramFiles(ListUtils.map(fileNames, Paths::get));
Integer minSdkVersion = needMinSdkVersion.get(name);
if (minSdkVersion != null) {
builder.setMinApiLevel(minSdkVersion);
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index cf1c6d7..57ae420 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -20,22 +20,31 @@
public class R8RunExamplesAndroidOTest extends RunExamplesAndroidOTest<R8Command.Builder> {
private static Map<DexVm.Version, List<String>> alsoFailsOn =
- ImmutableMap.of(
- Version.V4_4_4, ImmutableList.of(
+ ImmutableMap.<DexVm.Version, List<String>>builder()
+ .put(Version.V4_0_4,
+ ImmutableList.of(
"invokecustom-with-shrinking"
- ),
- Version.V5_1_1, ImmutableList.of(
+ ))
+ .put(Version.V4_4_4,
+ ImmutableList.of(
"invokecustom-with-shrinking"
- ),
- Version.V6_0_1, ImmutableList.of(
+ ))
+ .put(Version.V5_1_1,
+ ImmutableList.of(
"invokecustom-with-shrinking"
- ),
- Version.V7_0_0, ImmutableList.of(
+ ))
+ .put(Version.V6_0_1,
+ ImmutableList.of(
"invokecustom-with-shrinking"
- ),
- Version.DEFAULT, ImmutableList.of(
- )
- );
+ ))
+ .put(Version.V7_0_0,
+ ImmutableList.of(
+ "invokecustom-with-shrinking"
+ ))
+ .put(Version.DEFAULT,
+ ImmutableList.of(
+ ))
+ .build();
@Test
public void invokeCustomWithShrinking() throws Throwable {
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidPTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidPTest.java
index 39c3cd4..b063770 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidPTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidPTest.java
@@ -19,22 +19,31 @@
public class R8RunExamplesAndroidPTest extends RunExamplesAndroidPTest<R8Command.Builder> {
private static Map<DexVm.Version, List<String>> alsoFailsOn =
- ImmutableMap.of(
- DexVm.Version.V4_4_4, ImmutableList.of(
- "invokecustom-with-shrinking"
- ),
- DexVm.Version.V5_1_1, ImmutableList.of(
- "invokecustom-with-shrinking"
- ),
- DexVm.Version.V6_0_1, ImmutableList.of(
- "invokecustom-with-shrinking"
- ),
- DexVm.Version.V7_0_0, ImmutableList.of(
- "invokecustom-with-shrinking"
- ),
- DexVm.Version.DEFAULT, ImmutableList.of(
- )
- );
+ ImmutableMap.<DexVm.Version, List<String>>builder()
+ .put(DexVm.Version.V4_0_4,
+ ImmutableList.of(
+ "invokecustom-with-shrinking"
+ ))
+ .put(DexVm.Version.V4_4_4,
+ ImmutableList.of(
+ "invokecustom-with-shrinking"
+ ))
+ .put(DexVm.Version.V5_1_1,
+ ImmutableList.of(
+ "invokecustom-with-shrinking"
+ ))
+ .put(DexVm.Version.V6_0_1,
+ ImmutableList.of(
+ "invokecustom-with-shrinking"
+ ))
+ .put(DexVm.Version.V7_0_0,
+ ImmutableList.of(
+ "invokecustom-with-shrinking"
+ ))
+ .put(DexVm.Version.DEFAULT,
+ ImmutableList.of(
+ ))
+ .build();
@Test
public void invokeCustomWithShrinking() throws Throwable {
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java b/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java
index 9d275d4..8e23386 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
import com.android.tools.r8.utils.OutputMode;
import java.io.IOException;
+import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
@@ -92,6 +93,16 @@
}
}
+ private R8Command.Builder addInputFile(R8Command.Builder builder) throws NoSuchFileException {
+ if (input == Input.DX) {
+ // If input is DEX code, use the tool helper to add the DEX sources as R8 disallows them.
+ ToolHelper.getAppBuilder(builder).addProgramFiles(getInputFile());
+ } else {
+ builder.addProgramFiles(getInputFile());
+ }
+ return builder;
+ }
+
public Path getOriginalJarFile(String postFix) {
return Paths.get(getExampleDir(), pkg + postFix + JAR_EXTENSION);
}
@@ -116,17 +127,17 @@
switch (compiler) {
case D8: {
assertTrue(output == Output.DEX);
- ToolHelper.runD8(D8Command.builder()
- .addProgramFiles(getInputFile())
- .setOutput(getOutputFile(), outputMode)
- .setMode(mode)
- .build());
+ ToolHelper.runD8(
+ D8Command.builder()
+ .addProgramFiles(getInputFile())
+ .setOutput(getOutputFile(), outputMode)
+ .setMode(mode)
+ .build());
break;
}
case R8: {
ToolHelper.runR8(
- R8Command.builder()
- .addProgramFiles(getInputFile())
+ addInputFile(R8Command.builder())
.setOutput(getOutputFile(), outputMode)
.setMode(mode)
.build(),
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index 5416e95..f4783b2 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -156,15 +156,12 @@
// being true even when the converse is the case when running on the JVM.
.put("enclosingmethod.Main", TestCondition.any())
// Early art versions incorrectly print Float.MIN_VALUE.
- .put(
- "filledarray.FilledArray",
- TestCondition.match(
- TestCondition.runtimes(Version.V6_0_1, Version.V5_1_1, Version.V4_4_4)))
+ .put("filledarray.FilledArray",
+ TestCondition.match(TestCondition.runtimesUpTo(Version.V6_0_1)))
// Early art versions incorrectly print doubles.
.put(
"regress_70736958.Test",
- TestCondition.match(
- TestCondition.runtimes(Version.V6_0_1, Version.V5_1_1, Version.V4_4_4)))
+ TestCondition.match(TestCondition.runtimesUpTo(Version.V6_0_1)))
.build();
}
@@ -173,10 +170,11 @@
protected Map<String, TestCondition> getSkip() {
return new ImmutableMap.Builder<String, TestCondition>()
// Test uses runtime methods which are not available on older Art versions.
- .put(
- "regress_70703087.Test",
- TestCondition.match(
- TestCondition.runtimes(Version.V6_0_1, Version.V5_1_1, Version.V4_4_4)))
+ .put("regress_70703087.Test",
+ TestCondition.match(TestCondition.runtimesUpTo(Version.V6_0_1)))
+ // Test uses runtime methods which are not available on older Art versions.
+ .put("loop.UdpServer",
+ TestCondition.match(TestCondition.runtimesUpTo(Version.V4_0_4)))
.build();
}
}
diff --git a/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java b/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java
index c80acd5..126bccf 100644
--- a/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java
@@ -44,6 +44,11 @@
// Smali code contains an empty switch payload.
"sparse-switch",
"regression/33846227"
+ ),
+ Version.V4_0_4, ImmutableList.of(
+ // Smali code contains an empty switch payload.
+ "sparse-switch",
+ "regression/33846227"
)
);
@@ -55,6 +60,12 @@
"type-confusion-regression2", "java.lang.NullPointerException\n",
"type-confusion-regression3", "java.lang.NullPointerException\n",
"merge-blocks-regression", "java.lang.NullPointerException\n"
+ ),
+ Version.V4_0_4, ImmutableMap.of(
+ "bad-codegen", "java.lang.NullPointerException\n",
+ "type-confusion-regression2", "java.lang.NullPointerException\n",
+ "type-confusion-regression3", "java.lang.NullPointerException\n",
+ "merge-blocks-regression", "java.lang.NullPointerException\n"
)
);
@@ -65,6 +76,11 @@
// The invokes are in fact invalid, but the test expects the current Art behavior
// of throwing an IncompatibleClassChange exception. Dalvik fails to verify.
"illegal-invokes"
+ ),
+ Version.V4_0_4, ImmutableList.of(
+ // The invokes are in fact invalid, but the test expects the current Art behavior
+ // of throwing an IncompatibleClassChange exception. Dalvik fails to verify.
+ "illegal-invokes"
)
);
@@ -138,12 +154,11 @@
public void SmaliTest() throws Exception {
Path originalDexFile = Paths.get(SMALI_DIR, directoryName, dexFileName);
String outputPath = temp.getRoot().getCanonicalPath();
- R8.run(
- R8Command.builder()
- .addProgramFiles(originalDexFile)
+ R8Command.Builder builder = R8Command.builder()
.addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()))
- .setOutput(Paths.get(outputPath), OutputMode.DexIndexed)
- .build());
+ .setOutput(Paths.get(outputPath), OutputMode.DexIndexed);
+ ToolHelper.getAppBuilder(builder).addProgramFiles(originalDexFile);
+ R8.run(builder.build());
if (!ToolHelper.artSupported()) {
return;
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
index 0e7d742..cfa2596 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -180,63 +180,88 @@
ImmutableList.of(
"invokepolymorphic-error-due-to-min-sdk", "invokecustom-error-due-to-min-sdk");
- private static Map<DexVm.Version, List<String>> failsOn =
- ImmutableMap.of(
- DexVm.Version.V4_4_4, ImmutableList.of(
- // API not supported
- "paramnames",
- "repeat_annotations_new_api",
- // Dex version not supported
- "invokepolymorphic",
- "invokecustom",
- "invokecustom2",
- "DefaultMethodInAndroidJar25",
- "StaticMethodInAndroidJar25",
- "testMissingInterfaceDesugared2AndroidO",
- "testCallToMissingSuperInterfaceDesugaredAndroidO",
- "testMissingSuperDesugaredAndroidO"
- ),
- DexVm.Version.V5_1_1, ImmutableList.of(
- // API not supported
- "paramnames",
- "repeat_annotations_new_api",
- // Dex version not supported
- "invokepolymorphic",
- "invokecustom",
- "invokecustom2",
- "DefaultMethodInAndroidJar25",
- "StaticMethodInAndroidJar25",
- "testMissingInterfaceDesugared2AndroidO",
- "testCallToMissingSuperInterfaceDesugaredAndroidO",
- "testMissingSuperDesugaredAndroidO"
- ),
- DexVm.Version.V6_0_1, ImmutableList.of(
- // API not supported
- "paramnames",
- "repeat_annotations_new_api",
- // Dex version not supported
- "invokepolymorphic",
- "invokecustom",
- "invokecustom2",
- "DefaultMethodInAndroidJar25",
- "StaticMethodInAndroidJar25",
- "testMissingInterfaceDesugared2AndroidO",
- "testCallToMissingSuperInterfaceDesugaredAndroidO",
- "testMissingSuperDesugaredAndroidO"
- ),
- DexVm.Version.V7_0_0, ImmutableList.of(
- // API not supported
- "paramnames",
- // Dex version not supported
- "invokepolymorphic",
- "invokecustom",
- "invokecustom2",
- "testMissingInterfaceDesugared2AndroidO",
- "testCallToMissingSuperInterfaceDesugaredAndroidO",
- "testMissingSuperDesugaredAndroidO"
- ),
- DexVm.Version.DEFAULT, ImmutableList.of()
- );
+ private static Map<DexVm.Version, List<String>> failsOn;
+
+ static {
+ ImmutableMap.Builder<DexVm.Version, List<String>> builder = ImmutableMap.builder();
+ builder
+ .put(
+ DexVm.Version.V4_0_4, ImmutableList.of(
+ // API not supported
+ "paramnames",
+ "repeat_annotations_new_api",
+ // Dex version not supported
+ "invokepolymorphic",
+ "invokecustom",
+ "invokecustom2",
+ "DefaultMethodInAndroidJar25",
+ "StaticMethodInAndroidJar25",
+ "testMissingInterfaceDesugared2AndroidO",
+ "testCallToMissingSuperInterfaceDesugaredAndroidO",
+ "testMissingSuperDesugaredAndroidO"
+ ))
+ .put(
+ DexVm.Version.V4_4_4, ImmutableList.of(
+ // API not supported
+ "paramnames",
+ "repeat_annotations_new_api",
+ // Dex version not supported
+ "invokepolymorphic",
+ "invokecustom",
+ "invokecustom2",
+ "DefaultMethodInAndroidJar25",
+ "StaticMethodInAndroidJar25",
+ "testMissingInterfaceDesugared2AndroidO",
+ "testCallToMissingSuperInterfaceDesugaredAndroidO",
+ "testMissingSuperDesugaredAndroidO"
+ ))
+ .put(
+ DexVm.Version.V5_1_1, ImmutableList.of(
+ // API not supported
+ "paramnames",
+ "repeat_annotations_new_api",
+ // Dex version not supported
+ "invokepolymorphic",
+ "invokecustom",
+ "invokecustom2",
+ "DefaultMethodInAndroidJar25",
+ "StaticMethodInAndroidJar25",
+ "testMissingInterfaceDesugared2AndroidO",
+ "testCallToMissingSuperInterfaceDesugaredAndroidO",
+ "testMissingSuperDesugaredAndroidO"
+ ))
+ .put(
+ DexVm.Version.V6_0_1, ImmutableList.of(
+ // API not supported
+ "paramnames",
+ "repeat_annotations_new_api",
+ // Dex version not supported
+ "invokepolymorphic",
+ "invokecustom",
+ "invokecustom2",
+ "DefaultMethodInAndroidJar25",
+ "StaticMethodInAndroidJar25",
+ "testMissingInterfaceDesugared2AndroidO",
+ "testCallToMissingSuperInterfaceDesugaredAndroidO",
+ "testMissingSuperDesugaredAndroidO"
+ ))
+ .put(
+ DexVm.Version.V7_0_0, ImmutableList.of(
+ // API not supported
+ "paramnames",
+ // Dex version not supported
+ "invokepolymorphic",
+ "invokecustom",
+ "invokecustom2",
+ "testMissingInterfaceDesugared2AndroidO",
+ "testCallToMissingSuperInterfaceDesugaredAndroidO",
+ "testMissingSuperDesugaredAndroidO"
+ ))
+ .put(
+ DexVm.Version.DEFAULT, ImmutableList.of()
+ );
+ failsOn = builder.build();
+ }
@Rule
public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
index c765f77..bc898bd 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
@@ -173,25 +173,19 @@
"invokepolymorphic-error-due-to-min-sdk", "invokecustom-error-due-to-min-sdk");
private static Map<DexVm.Version, List<String>> failsOn =
- ImmutableMap.of(
- DexVm.Version.V4_4_4, ImmutableList.of(
- // Dex version not supported
- "invokecustom"
- ),
- DexVm.Version.V5_1_1, ImmutableList.of(
- // Dex version not supported
- "invokecustom"
- ),
- DexVm.Version.V6_0_1, ImmutableList.of(
- // Dex version not supported
- "invokecustom"
- ),
- DexVm.Version.V7_0_0, ImmutableList.of(
- // Dex version not supported
- "invokecustom"
- ),
- DexVm.Version.DEFAULT, ImmutableList.of()
- );
+ ImmutableMap.<DexVm.Version, List<String>>builder()
+ // Dex version not supported
+ .put(DexVm.Version.V4_0_4, ImmutableList.of("invokecustom"))
+ // Dex version not supported
+ .put(DexVm.Version.V4_4_4, ImmutableList.of("invokecustom"))
+ // Dex version not supported
+ .put(DexVm.Version.V5_1_1, ImmutableList.of("invokecustom"))
+ // Dex version not supported
+ .put(DexVm.Version.V6_0_1, ImmutableList.of("invokecustom"))
+ // Dex version not supported
+ .put(DexVm.Version.V7_0_0, ImmutableList.of("invokecustom"))
+ .put(DexVm.Version.DEFAULT, ImmutableList.of())
+ .build();
@Rule
public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
diff --git a/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java b/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java
index fb44d6f..281b0da 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java
@@ -171,32 +171,37 @@
private static List<String> minSdkErrorExpected =
ImmutableList.of("varhandle-error-due-to-min-sdk");
- private static Map<DexVm.Version, List<String>> failsOn =
- ImmutableMap.of(
- DexVm.Version.V4_4_4, ImmutableList.of(
- "native-private-interface-methods",
- // Dex version not supported
- "varhandle"
- ),
- DexVm.Version.V5_1_1, ImmutableList.of(
- "native-private-interface-methods",
- // Dex version not supported
- "varhandle"
- ),
- DexVm.Version.V6_0_1, ImmutableList.of(
- "native-private-interface-methods",
- // Dex version not supported
- "varhandle"
- ),
- DexVm.Version.V7_0_0, ImmutableList.of(
- // Dex version not supported
- "varhandle"
- ),
- DexVm.Version.DEFAULT, ImmutableList.of(
- // TODO(mikaelpeltier): Update runtime when the support will be ready
- "varhandle"
- )
- );
+ private static Map<DexVm.Version, List<String>> failsOn;
+
+ static {
+ ImmutableMap.Builder<DexVm.Version, List<String>> builder = ImmutableMap.builder();
+ builder
+ .put(DexVm.Version.V4_0_4, ImmutableList.of(
+ "native-private-interface-methods",// Dex version not supported
+ "varhandle"
+ ))
+ .put(DexVm.Version.V4_4_4, ImmutableList.of(
+ "native-private-interface-methods",// Dex version not supported
+ "varhandle"
+ ))
+ .put(DexVm.Version.V5_1_1, ImmutableList.of(
+ "native-private-interface-methods",// Dex version not supported
+ "varhandle"
+ ))
+ .put(DexVm.Version.V6_0_1, ImmutableList.of("native-private-interface-methods",
+ // Dex version not supported
+ "varhandle"
+ ))
+ .put(DexVm.Version.V7_0_0, ImmutableList.of(
+ // Dex version not supported
+ "varhandle"
+ ))
+ .put(DexVm.Version.DEFAULT, ImmutableList.of(
+ // TODO(mikaelpeltier): Update runtime when the support will be ready
+ "varhandle"
+ ));
+ failsOn = builder.build();
+ }
// Defines methods failing on JVM, specifies the output to be used for comparison.
private static Map<String, String> expectedJvmResult =
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 4412dec..fd238f3 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -277,18 +277,18 @@
}
/**
- * Generate a Proguard configuration for keeping the "public static void main(String[])" method
- * of the specified class.
+ * Generate a Proguard configuration for keeping the "public static void main(String[])" method of
+ * the specified class.
*/
- public String keepMainProguardConfiguration(Class clazz) {
+ public static String keepMainProguardConfiguration(Class clazz) {
return keepMainProguardConfiguration(clazz.getCanonicalName());
}
/**
- * Generate a Proguard configuration for keeping the "public static void main(String[])" method
- * of the specified class.
+ * Generate a Proguard configuration for keeping the "public static void main(String[])" method of
+ * the specified class.
*/
- public String keepMainProguardConfiguration(String clazz) {
+ public static String keepMainProguardConfiguration(String clazz) {
return "-keep public class " + clazz + " {\n"
+ " public static void main(java.lang.String[]);\n"
+ "}\n"
@@ -296,11 +296,11 @@
}
/**
- * Generate a Proguard configuration for keeping the "public static void main(String[])" method
- * of the specified class and specify if -allowaccessmodification and -dontobfuscate are added
- * as well.
+ * Generate a Proguard configuration for keeping the "public static void main(String[])" method of
+ * the specified class and specify if -allowaccessmodification and -dontobfuscate are added as
+ * well.
*/
- public String keepMainProguardConfiguration(
+ public static String keepMainProguardConfiguration(
Class clazz, boolean allowaccessmodification, boolean obfuscate) {
return keepMainProguardConfiguration(clazz)
+ (allowaccessmodification ? "-allowaccessmodification\n" : "")
diff --git a/src/test/java/com/android/tools/r8/TestCondition.java b/src/test/java/com/android/tools/r8/TestCondition.java
index 1aca2bd..4babe9b 100644
--- a/src/test/java/com/android/tools/r8/TestCondition.java
+++ b/src/test/java/com/android/tools/r8/TestCondition.java
@@ -99,6 +99,10 @@
return new RuntimeSet(EnumSet.copyOf(Arrays.asList(runtimes)));
}
+ public static RuntimeSet runtimesUpTo(DexVm.Version upto) {
+ return new RuntimeSet(EnumSet.range(DexVm.Version.first(), upto));
+ }
+
public static TestCondition match(
ToolSet tools,
CompilerSet compilers,
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index cf5e451..b487308 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -11,8 +11,11 @@
import com.android.tools.r8.DeviceRunner.DeviceRunnerConfigurationException;
import com.android.tools.r8.ToolHelper.DexVm.Kind;
import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.errors.DexOverflowException;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.shaking.ProguardConfigurationParser;
import com.android.tools.r8.shaking.ProguardRuleParserException;
@@ -52,6 +55,7 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import joptsimple.internal.Strings;
@@ -86,6 +90,8 @@
private static final String PROGUARD = "third_party/proguard/proguard5.2.1/bin/proguard.sh";
public enum DexVm {
+ ART_4_0_4_TARGET(Version.V4_0_4, Kind.TARGET),
+ ART_4_0_4_HOST(Version.V4_0_4, Kind.HOST),
ART_4_4_4_TARGET(Version.V4_4_4, Kind.TARGET),
ART_4_4_4_HOST(Version.V4_4_4, Kind.HOST),
ART_5_1_1_TARGET(Version.V5_1_1, Kind.TARGET),
@@ -104,6 +110,7 @@
.build();
public enum Version {
+ V4_0_4("4.0.4"),
V4_4_4("4.4.4"),
V5_1_1("5.1.1"),
V6_0_1("6.0.1"),
@@ -131,6 +138,15 @@
}
private String shortName;
+
+ public static Version first() {
+ return V4_0_4;
+ }
+
+ static {
+ // Ensure first is always first.
+ assert Arrays.stream(values()).allMatch(v -> v == first() || v.compareTo(first()) > 0);
+ }
}
public enum Kind {
@@ -371,14 +387,16 @@
.put(DexVm.ART_7_0_0_HOST, "art-7.0.0")
.put(DexVm.ART_6_0_1_HOST, "art-6.0.1")
.put(DexVm.ART_5_1_1_HOST, "art-5.1.1")
- .put(DexVm.ART_4_4_4_HOST, "dalvik").build();
+ .put(DexVm.ART_4_4_4_HOST, "dalvik")
+ .put(DexVm.ART_4_0_4_HOST, "dalvik-4.0.4").build();
private static final Map<DexVm, String> ART_BINARY_VERSIONS =
ImmutableMap.<DexVm, String>builder()
.put(DexVm.ART_DEFAULT, "bin/art")
.put(DexVm.ART_7_0_0_HOST, "bin/art")
.put(DexVm.ART_6_0_1_HOST, "bin/art")
.put(DexVm.ART_5_1_1_HOST, "bin/art")
- .put(DexVm.ART_4_4_4_HOST, "bin/dalvik").build();
+ .put(DexVm.ART_4_4_4_HOST, "bin/dalvik")
+ .put(DexVm.ART_4_0_4_HOST, "bin/dalvik").build();
private static final Map<DexVm, String> ART_BINARY_VERSIONS_X64 =
ImmutableMap.of(
@@ -398,13 +416,19 @@
"core-oj-hostdex.jar",
"apache-xml-hostdex.jar");
- private static final Map<DexVm, List<String>> BOOT_LIBS =
- ImmutableMap.of(
- DexVm.ART_DEFAULT, ART_BOOT_LIBS,
- DexVm.ART_7_0_0_HOST, ART_BOOT_LIBS,
- DexVm.ART_6_0_1_HOST, ART_BOOT_LIBS,
- DexVm.ART_5_1_1_HOST, ART_BOOT_LIBS,
- DexVm.ART_4_4_4_HOST, DALVIK_BOOT_LIBS);
+ private static final Map<DexVm, List<String>> BOOT_LIBS;
+
+ static {
+ ImmutableMap.Builder<DexVm, List<String>> builder = ImmutableMap.builder();
+ builder
+ .put(DexVm.ART_DEFAULT, ART_BOOT_LIBS)
+ .put(DexVm.ART_7_0_0_HOST, ART_BOOT_LIBS)
+ .put(DexVm.ART_6_0_1_HOST, ART_BOOT_LIBS)
+ .put(DexVm.ART_5_1_1_HOST, ART_BOOT_LIBS)
+ .put(DexVm.ART_4_4_4_HOST, DALVIK_BOOT_LIBS)
+ .put(DexVm.ART_4_0_4_HOST, DALVIK_BOOT_LIBS);
+ BOOT_LIBS = builder.build();
+ }
private static final String LIB_PATH = TOOLS + "/linux/art/lib";
private static final String DX = getDxExecutablePath();
@@ -459,9 +483,12 @@
// TODO(mikaelpeltier) Android P does not yet have his android.jar use the O version
minSdkVersion = AndroidApiLevel.O.getLevel();
}
- return String.format(
+ String jar = String.format(
ANDROID_JAR_PATTERN,
minSdkVersion == AndroidApiLevel.getDefault().getLevel() ? DEFAULT_MIN_SDK : minSdkVersion);
+ assert Files.exists(Paths.get(jar))
+ : "Expected android jar to exist for API level " + minSdkVersion;
+ return jar;
}
public static Path getJdwpTestsCfJarPath(int minSdk) {
@@ -557,8 +584,16 @@
return AndroidApiLevel.O.getLevel();
case V7_0_0:
return AndroidApiLevel.N.getLevel();
+ case V6_0_1:
+ return AndroidApiLevel.M.getLevel();
+ case V5_1_1:
+ return AndroidApiLevel.L_MR1.getLevel();
+ case V4_4_4:
+ return AndroidApiLevel.K.getLevel();
+ case V4_0_4:
+ return AndroidApiLevel.I_MR1.getLevel();
default:
- return AndroidApiLevel.getDefault().getLevel();
+ throw new Unreachable("Missing min api level for dex vm " + dexVm);
}
}
@@ -1095,10 +1130,20 @@
return new ProcessResult(p.exitValue(), stdoutReader.getResult(), stderrReader.getResult());
}
+ public static R8Command.Builder addProguardConfigurationConsumer(
+ R8Command.Builder builder, Consumer<ProguardConfiguration.Builder> consumer) {
+ builder.addProguardConfigurationConsumerForTesting(consumer);
+ return builder;
+ }
+
public static AndroidApp getApp(BaseCommand command) {
return command.getInputApp();
}
+ public static AndroidApp.Builder getAppBuilder(BaseCommand.Builder builder) {
+ return builder.getAppBuilder();
+ }
+
public static AndroidApp.Builder builderFromProgramDirectory(Path directory) throws IOException {
AndroidApp.Builder builder = AndroidApp.builder();
Files.walkFileTree(
@@ -1115,4 +1160,16 @@
});
return builder;
}
+
+ public static void writeApplication(DexApplication application, InternalOptions options)
+ throws ExecutionException, DexOverflowException {
+ R8.writeApplication(
+ Executors.newSingleThreadExecutor(),
+ application,
+ null,
+ NamingLens.getIdentityLens(),
+ null,
+ options,
+ null);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/compatproguard/CompatProguardSmaliTestBase.java b/src/test/java/com/android/tools/r8/compatproguard/CompatProguardSmaliTestBase.java
index 7438498..825b434 100644
--- a/src/test/java/com/android/tools/r8/compatproguard/CompatProguardSmaliTestBase.java
+++ b/src/test/java/com/android/tools/r8/compatproguard/CompatProguardSmaliTestBase.java
@@ -17,12 +17,11 @@
DexInspector runCompatProguard(SmaliBuilder builder, List<String> proguardConfigurations)
throws Exception {
Path dexOutputDir = temp.newFolder().toPath();
- R8Command command =
+ R8Command.Builder commandBuilder =
new CompatProguardCommandBuilder(true)
- .addDexProgramData(builder.compile(), Origin.unknown())
.setOutput(dexOutputDir, OutputMode.DexIndexed)
- .addProguardConfiguration(proguardConfigurations, Origin.unknown())
- .build();
- return new DexInspector(ToolHelper.runR8(command));
+ .addProguardConfiguration(proguardConfigurations, Origin.unknown());
+ ToolHelper.getAppBuilder(commandBuilder).addDexProgramData(builder.compile(), Origin.unknown());
+ return new DexInspector(ToolHelper.runR8(commandBuilder.build()));
}
}
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index 0878a30..29c9871 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -799,7 +799,8 @@
command.perform(this);
} catch (TestErrorException e) {
boolean ignoreException = false;
- if (config.isDexRuntime() && ToolHelper.getDexVm().getVersion() == Version.V4_4_4) {
+ if (config.isDexRuntime()
+ && ToolHelper.getDexVm().getVersion().isOlderThanOrEqual(Version.V4_4_4)) {
// Dalvik has flaky synchronization issue on shutdown. The workaround is to ignore
// the exception if and only if we know that it's the final resume command.
if (debuggeeState == null && commandsQueue.isEmpty()) {
diff --git a/src/test/java/com/android/tools/r8/dexmerger/DexFileMergerTests.java b/src/test/java/com/android/tools/r8/dexmerger/DexFileMergerTests.java
index 44d3948..9dfc37b 100644
--- a/src/test/java/com/android/tools/r8/dexmerger/DexFileMergerTests.java
+++ b/src/test/java/com/android/tools/r8/dexmerger/DexFileMergerTests.java
@@ -72,7 +72,7 @@
AndroidApiLevel.N.getLevel(),
MAX_METHOD_COUNT / 2 + 1 + extraMethodCount);
Path appDir = temp.newFolder().toPath().resolve("merger-input.zip");
- assertEquals(programResourcesSize, generatedApp.getDexProgramResources().size());
+ assertEquals(programResourcesSize, generatedApp.getDexProgramResourcesForTesting().size());
generatedApp.write(appDir, OutputMode.DexIndexed);
Path outZip = temp.getRoot().toPath().resolve("out.zip");
diff --git a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
index 92897fa..3cab10a 100644
--- a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
@@ -74,7 +74,7 @@
assertNull(appInfo.lookupDirectTarget(method.method));
assertNotNull(appInfo.lookupStaticTarget(method.method));
- if (ToolHelper.getDexVm().getVersion() == DexVm.Version.V4_4_4) {
+ if (ToolHelper.getDexVm().getVersion().isOlderThanOrEqual(DexVm.Version.V4_4_4)) {
// Dalvik rejects at verification time instead of producing the
// expected IncompatibleClassChangeError.
try {
diff --git a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
index d4745b4..672973f 100644
--- a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
+++ b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
@@ -93,10 +93,12 @@
builder.setMode(mode);
builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
builder.setMinApiLevel(AndroidApiLevel.L.getLevel());
- builder.addProguardConfigurationConsumer(b -> {
- b.setPrintSeeds(false);
- b.setIgnoreWarnings(true);
- });
+ ToolHelper.addProguardConfigurationConsumer(
+ builder,
+ pgConfig -> {
+ pgConfig.setPrintSeeds(false);
+ pgConfig.setIgnoreWarnings(true);
+ });
outputApp = ToolHelper.runR8(builder.build(), optionsConsumer);
} else {
assert compiler == CompilerUnderTest.D8;
@@ -150,7 +152,7 @@
public int applicationSize(AndroidApp app) throws IOException {
int bytes = 0;
try (Closer closer = Closer.create()) {
- for (Resource dex : app.getDexProgramResources()) {
+ for (Resource dex : app.getDexProgramResourcesForTesting()) {
bytes += ByteStreams.toByteArray(closer.register(dex.getStream())).length;
}
}
@@ -168,8 +170,8 @@
app1.writeToDirectory(temp.newFolder("app1").toPath(), OutputMode.Indexed);
app2.writeToDirectory(temp.newFolder("app2").toPath(), OutputMode.Indexed);
}
- List<ProgramResource> files1 = app1.getDexProgramResources();
- List<ProgramResource> files2 = app2.getDexProgramResources();
+ List<ProgramResource> files1 = app1.getDexProgramResourcesForTesting();
+ List<ProgramResource> files2 = app2.getDexProgramResourcesForTesting();
assertEquals(files1.size(), files2.size());
for (int index = 0; index < files1.size(); index++) {
InputStream file1 = closer.register(files1.get(index).getStream());
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java
index 71868d6..f8aa934 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java
@@ -30,7 +30,7 @@
ImmutableList.of());
int bytes = 0;
try (Closer closer = Closer.create()) {
- for (Resource dex : app.getDexProgramResources()) {
+ for (Resource dex : app.getDexProgramResourcesForTesting()) {
bytes += ByteStreams.toByteArray(closer.register(dex.getStream())).length;
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
index 0e97b13..074c704 100644
--- a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir;
-import com.android.tools.r8.R8;
+import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.errors.DexOverflowException;
import com.android.tools.r8.graph.AppInfo;
@@ -15,7 +15,6 @@
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.ValueNumberGenerator;
import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.smali.SmaliBuilder;
import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
@@ -29,7 +28,6 @@
import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executors;
import org.antlr.runtime.RecognitionException;
public class IrInjectionTestBase extends SmaliTestBase {
@@ -123,14 +121,7 @@
private AndroidApp writeDex(DexApplication application, InternalOptions options)
throws DexOverflowException {
try {
- R8.writeApplication(
- Executors.newSingleThreadExecutor(),
- application,
- null,
- NamingLens.getIdentityLens(),
- null,
- options,
- null);
+ ToolHelper.writeApplication(application, options);
options.signalFinishedToProgramConsumer();
return consumers.build();
} catch (ExecutionException e) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/R8StaticInlining.java b/src/test/java/com/android/tools/r8/ir/optimize/R8StaticInlining.java
new file mode 100644
index 0000000..5102878
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/R8StaticInlining.java
@@ -0,0 +1,84 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.OutputMode;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collection;
+import org.junit.Assume;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * This test verifies that semantic of class initialization is preserved when a static method
+ * invocation is inlined.
+ */
+@RunWith(Parameterized.class)
+public class R8StaticInlining extends TestBase {
+
+ // TODO(shertz) add CF output
+ @Parameters(name = "{0}_{1}")
+ public static Collection<String[]> data() {
+ return ImmutableList.of(
+ new String[]{"staticinlining", "staticinlining.Main"}
+ );
+ }
+
+ private final String folder;
+ private final String mainClass;
+
+ public R8StaticInlining(String folder, String mainClass) {
+ this.folder = folder;
+ this.mainClass = mainClass;
+ }
+
+ @Test
+ @Ignore("b/71524812")
+ public void testInliningOfStatic() throws Exception {
+ Assume.assumeTrue(ToolHelper.artSupported());
+
+ Path proguardRules = Paths.get(ToolHelper.EXAMPLES_DIR, folder, "keep-rules.txt");
+ Path jarFile =
+ Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, folder + FileUtils.JAR_EXTENSION);
+
+ // Build with R8
+ AndroidApp.Builder builder = AndroidApp.builder();
+ builder.addProgramFiles(jarFile);
+ AndroidApp app = compileWithR8(builder.build(), proguardRules);
+
+ // Compare original and generated DEX files.
+ String originalDexFile = Paths
+ .get(ToolHelper.EXAMPLES_BUILD_DIR, folder, ToolHelper.DEFAULT_DEX_FILENAME).toString();
+ Path generatedDexFile = temp.getRoot().toPath().resolve("classes.jar");
+ app.writeToZip(generatedDexFile, OutputMode.DexIndexed);
+ String artOutput = ToolHelper
+ .checkArtOutputIdentical(originalDexFile, generatedDexFile.toString(), mainClass,
+ ToolHelper.getDexVm());
+
+ // Compare with Java.
+ ToolHelper.ProcessResult javaResult = ToolHelper.runJava(jarFile, mainClass);
+ if (javaResult.exitCode != 0) {
+ System.out.println(javaResult.stdout);
+ System.err.println(javaResult.stderr);
+ fail("JVM failed for: " + mainClass);
+ }
+ assertEquals("JVM and ART output differ", javaResult.stdout, artOutput);
+ }
+
+}
diff --git a/src/test/java/com/android/tools/r8/jasmin/FillBooleanArrayTruncation.java b/src/test/java/com/android/tools/r8/jasmin/FillBooleanArrayTruncation.java
index a38ee9e..ddf3dbb 100644
--- a/src/test/java/com/android/tools/r8/jasmin/FillBooleanArrayTruncation.java
+++ b/src/test/java/com/android/tools/r8/jasmin/FillBooleanArrayTruncation.java
@@ -31,7 +31,7 @@
private void runTest(JasminBuilder builder, String main) throws Exception {
String javaResult = runOnJava(builder, main);
- if (ToolHelper.getDexVm().getVersion() == DexVm.Version.V4_4_4) {
+ if (ToolHelper.getDexVm().getVersion().isOlderThanOrEqual(DexVm.Version.V4_4_4)) {
// On dalvik the need for truncation is treated as a verification error.
runOnDalvikCheckVerifyError(builder, main);
runOnDalvikDxCheckVerifyError(builder, main);
diff --git a/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java b/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
index cefa888..8e2242d 100644
--- a/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
+++ b/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.CompilationException;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.D8Command;
-import com.android.tools.r8.R8;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.DexAnnotationElement;
@@ -60,7 +59,7 @@
private AndroidApp compileWithR8(Path inputPath, Path outputPath, Path keepRulesPath)
throws IOException, CompilationException, CompilationFailedException {
- return R8.runInternal(
+ return ToolHelper.runR8(
R8Command.builder()
.addProgramFiles(inputPath)
.addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()))
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index aecdf33..122db11 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -431,7 +431,7 @@
MANY_CLASSES, AndroidApiLevel.K.getLevel(), true, MANY_CLASSES_MULTI_DEX_METHODS_PER_CLASS);
generated.write(getManyClassesForceMultiDexAppPath(), OutputMode.Indexed);
// Make sure the generated app indeed has multiple dex files.
- assertTrue(generated.getDexProgramResources().size() > 1);
+ assertTrue(generated.getDexProgramResourcesForTesting().size() > 1);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
index db18703..71ad0ac 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
@@ -80,14 +80,12 @@
String out = temp.getRoot().getCanonicalPath();
// NOTE: It is important to turn off inlining to ensure
// dex inspection of invokes is predictable.
- ToolHelper.runR8(
- R8Command.builder()
- .setOutput(Paths.get(out), OutputMode.DexIndexed)
- .addProgramFiles(programFile)
- .addLibraryFiles(JAR_LIBRARIES)
- .setMinApiLevel(minApiLevel)
- .build(),
- options -> options.inlineAccessors = false);
+ R8Command.Builder builder = R8Command.builder()
+ .setOutput(Paths.get(out), OutputMode.DexIndexed)
+ .addLibraryFiles(JAR_LIBRARIES)
+ .setMinApiLevel(minApiLevel);
+ ToolHelper.getAppBuilder(builder).addProgramFiles(programFile);
+ ToolHelper.runR8(builder.build(), options -> options.inlineAccessors = false);
}
private static boolean coolInvokes(InstructionSubject instruction) {
diff --git a/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java b/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
index 070b6da..4c9bbd1 100644
--- a/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
@@ -76,12 +76,12 @@
}
ProguardMapConsumer mapConsumer = new ProguardMapConsumer();
runR8(
- getCommandForApps(out, flagForObfuscation, NAMING044_JAR)
- .setProguardMapConsumer(mapConsumer)
- .addProguardConfigurationConsumer(
- c -> {
- c.setPrintMapping(true);
- c.setPrintMappingFile(proguardMap);
+ ToolHelper.addProguardConfigurationConsumer(
+ getCommandForApps(out, flagForObfuscation, NAMING044_JAR)
+ .setProguardMapConsumer(mapConsumer),
+ pgConfig -> {
+ pgConfig.setPrintMapping(true);
+ pgConfig.setPrintMappingFile(proguardMap);
})
.build());
@@ -92,13 +92,13 @@
Path instrOut = temp.newFolder("instr").toPath();
Path flag = Paths.get(ToolHelper.EXAMPLES_DIR, "applymapping044", "keep-rules.txt");
- AndroidApp instrApp = runR8(
- getCommandForInstrumentation(instrOut, flag, NAMING044_JAR, APPLYMAPPING044_JAR)
- .addProguardConfigurationConsumer(c -> {
- c.setApplyMappingFile(proguardMap);
- })
- .setMinification(false)
- .build());
+ AndroidApp instrApp =
+ runR8(
+ ToolHelper.addProguardConfigurationConsumer(
+ getCommandForInstrumentation(instrOut, flag, NAMING044_JAR, APPLYMAPPING044_JAR)
+ .setMinification(false),
+ pgConfig -> pgConfig.setApplyMappingFile(proguardMap))
+ .build());
DexInspector inspector = new DexInspector(instrApp);
MethodSubject main = inspector.clazz("applymapping044.Main").method(DexInspector.MAIN);
@@ -181,14 +181,15 @@
// keep rules to reserve D and E, along with a proguard map.
Path flag = Paths.get(ToolHelper.EXAMPLES_DIR, "naming001", "keep-rules-105.txt");
Path proguardMap = out.resolve(MAPPING);
- AndroidApp outputApp = runR8(
- getCommandForApps(out, flag, NAMING001_JAR)
- .addProguardConfigurationConsumer(c -> {
- c.setPrintMapping(true);
- c.setPrintMappingFile(proguardMap);
- })
- .setMinification(false)
- .build());
+ AndroidApp outputApp =
+ runR8(
+ ToolHelper.addProguardConfigurationConsumer(
+ getCommandForApps(out, flag, NAMING001_JAR).setMinification(false),
+ pgConfig -> {
+ pgConfig.setPrintMapping(true);
+ pgConfig.setPrintMappingFile(proguardMap);
+ })
+ .build());
// Make sure the given proguard map is indeed applied.
DexInspector inspector = new DexInspector(outputApp);
diff --git a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
index 9442559..cca27cf 100644
--- a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
@@ -64,19 +64,18 @@
@Before
public void generateR8ProcessedApp() throws Exception {
Path out = temp.getRoot().toPath();
- R8Command command =
- R8Command.builder()
- .setOutput(out, OutputMode.DexIndexed)
- .addProgramFiles(Paths.get(appFileName))
- .addProguardConfigurationFiles(ListUtils.map(keepRulesFiles, Paths::get))
- .addProguardConfigurationConsumer(
- builder -> {
- builder.setPrintMapping(true);
- builder.setPrintMappingFile(out.resolve(ToolHelper.DEFAULT_PROGUARD_MAP_FILE));
+ R8Command.Builder builder =
+ ToolHelper.addProguardConfigurationConsumer(
+ R8Command.builder(),
+ pgConfig -> {
+ pgConfig.setPrintMapping(true);
+ pgConfig.setPrintMappingFile(out.resolve(ToolHelper.DEFAULT_PROGUARD_MAP_FILE));
})
- .addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()))
- .build();
- ToolHelper.runR8(command);
+ .setOutput(out, OutputMode.DexIndexed)
+ .addProguardConfigurationFiles(ListUtils.map(keepRulesFiles, Paths::get))
+ .addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()));
+ ToolHelper.getAppBuilder(builder).addProgramFiles(Paths.get(appFileName));
+ ToolHelper.runR8(builder.build());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java b/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java
index 34f2b76..d5bb4bb 100644
--- a/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java
+++ b/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java
@@ -32,18 +32,19 @@
Path outjar = outdir.resolve("r8_compiled.jar");
Path proguardMapPath = outdir.resolve("proguard.map");
ToolHelper.runR8(
- R8Command.builder()
+ ToolHelper.addProguardConfigurationConsumer(
+ R8Command.builder(),
+ pgConfig -> {
+ pgConfig.setRenameSourceFileAttribute(TEST_FILE);
+ pgConfig.addKeepAttributePatterns(
+ ImmutableList.of("SourceFile", "LineNumberTable"));
+ })
.addProgramFiles(DEBUGGEE_JAR)
.setMinApiLevel(minSdk)
.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdk)))
.setMode(CompilationMode.DEBUG)
.setOutput(outjar, OutputMode.DexIndexed)
.setProguardMapOutput(proguardMapPath)
- .addProguardConfigurationConsumer(
- pg -> {
- pg.setRenameSourceFileAttribute(TEST_FILE);
- pg.addKeepAttributePatterns(ImmutableList.of("SourceFile", "LineNumberTable"));
- })
.build());
config = new DexDebugTestConfig(outjar);
config.setProguardMap(proguardMapPath);
diff --git a/src/test/java/com/android/tools/r8/rewrite/staticvalues/inlibraries/StaticLibraryValuesChangeTest.java b/src/test/java/com/android/tools/r8/rewrite/staticvalues/inlibraries/StaticLibraryValuesChangeTest.java
index 7d963b8..8439bbf 100644
--- a/src/test/java/com/android/tools/r8/rewrite/staticvalues/inlibraries/StaticLibraryValuesChangeTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/staticvalues/inlibraries/StaticLibraryValuesChangeTest.java
@@ -40,7 +40,8 @@
*/
// TODO(66944616): Can we make this work on Dalvik as well?
- Assume.assumeTrue("Skipping on 4.4.4", ToolHelper.getDexVm().getVersion() != Version.V4_4_4);
+ Assume.assumeTrue("Skipping on VM versions < 4.4.4",
+ ToolHelper.getDexVm().getVersion().isNewerThan(Version.V4_4_4));
// Build the second version of LibraryClass
JasminBuilder compileTimeLibrary = new JasminBuilder();
diff --git a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
index dafd45e..3446242 100644
--- a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
@@ -66,14 +66,15 @@
public void runR8andGetPrintUsage() throws Exception {
Path out = temp.getRoot().toPath();
R8Command command =
- R8Command.builder()
+ ToolHelper.addProguardConfigurationConsumer(
+ R8Command.builder(),
+ pgConfig -> {
+ pgConfig.setPrintUsage(true);
+ pgConfig.setPrintUsageFile(out.resolve(test + PRINT_USAGE_FILE_SUFFIX));
+ })
.setOutput(out, OutputMode.DexIndexed)
.addProgramFiles(Paths.get(programFile))
.addProguardConfigurationFiles(ListUtils.map(keepRulesFiles, Paths::get))
- .addProguardConfigurationConsumer(builder -> {
- builder.setPrintUsage(true);
- builder.setPrintUsageFile(out.resolve(test + PRINT_USAGE_FILE_SUFFIX));
- })
.addLibraryFiles(Paths.get(ANDROID_JAR))
.build();
ToolHelper.runR8(command, options -> {
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
index b57a0d8..6d78fd8 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
@@ -44,12 +44,11 @@
Path originalDex = Paths.get(EXAMPLES_BUILD_DIR, test, "classes.dex");
Path keepRules = Paths.get(EXAMPLES_DIR, test, "keep-rules.txt");
Path ignoreWarnings = Paths.get(VALID_PROGUARD_DIR, "ignorewarnings.flags");
- R8.run(
- R8Command.builder()
- .addProgramFiles(originalDex)
- .setOutput(out, OutputMode.DexIndexed)
- .addProguardConfigurationFiles(keepRules, ignoreWarnings)
- .build());
+ R8Command.Builder builder = R8Command.builder()
+ .setOutput(out, OutputMode.DexIndexed)
+ .addProguardConfigurationFiles(keepRules, ignoreWarnings);
+ ToolHelper.getAppBuilder(builder).addProgramFiles(originalDex);
+ R8.run(builder.build());
}
@Test(expected = CompilationFailedException.class)
@@ -67,11 +66,11 @@
}
}
};
- R8.run(R8Command.builder(handler)
- .addProgramFiles(originalDex)
+ R8Command.Builder builder = R8Command.builder(handler)
.setOutput(out, OutputMode.DexIndexed)
- .addProguardConfigurationFiles(keepRules)
- .build());
+ .addProguardConfigurationFiles(keepRules);
+ ToolHelper.getAppBuilder(builder).addProgramFiles(originalDex);
+ R8.run(builder.build());
}
@Test
@@ -89,16 +88,13 @@
mapping.println("-printmapping mapping.txt");
}
- ToolHelper.runR8(
- R8Command.builder()
- .addProgramFiles(originalDex)
- .setOutput(out, OutputMode.DexIndexed)
- .addProguardConfigurationFiles(keepRules, printMapping)
- .build(),
- options -> {
- // Turn off inlining, as we want the mapping that is printed to be stable.
- options.inlineAccessors = false;
- });
+ R8Command.Builder builder = R8Command.builder()
+ .setOutput(out, OutputMode.DexIndexed)
+ .addProguardConfigurationFiles(keepRules, printMapping);
+ ToolHelper.getAppBuilder(builder).addProgramFiles(originalDex);
+ // Turn off inlining, as we want the mapping that is printed to be stable.
+ ToolHelper.runR8(builder.build(), options -> options.inlineAccessors = false);
+
Path outputmapping = out.resolve("mapping.txt");
String actualMapping;
actualMapping = new String(Files.readAllBytes(outputmapping), StandardCharsets.UTF_8);
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
index 2f5805a..1023667 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
@@ -130,23 +130,20 @@
Path out = temp.getRoot().toPath();
boolean inline = programFile.contains("inlining");
- R8Command command =
- R8Command.builder()
- .setOutput(out, OutputMode.DexIndexed)
- .addProgramFiles(Paths.get(programFile))
- .addProguardConfigurationFiles(ListUtils.map(keepRulesFiles, Paths::get))
- .addProguardConfigurationConsumer(
- builder -> {
- builder.setPrintMapping(true);
- builder.setPrintMappingFile(out.resolve(ToolHelper.DEFAULT_PROGUARD_MAP_FILE));
- builder.setOverloadAggressively(minify == MinifyMode.AGGRESSIVE);
- builder.setObfuscating(minify.isMinify());
+ R8Command.Builder builder =
+ ToolHelper.addProguardConfigurationConsumer(
+ R8Command.builder(),
+ pgConfig -> {
+ pgConfig.setPrintMapping(true);
+ pgConfig.setPrintMappingFile(out.resolve(ToolHelper.DEFAULT_PROGUARD_MAP_FILE));
+ pgConfig.setOverloadAggressively(minify == MinifyMode.AGGRESSIVE);
+ pgConfig.setObfuscating(minify.isMinify());
})
- .addLibraryFiles(JAR_LIBRARIES)
- .build();
- ToolHelper.runR8(command, options -> {
- options.inlineAccessors = inline;
- });
+ .setOutput(out, OutputMode.DexIndexed)
+ .addProguardConfigurationFiles(ListUtils.map(keepRulesFiles, Paths::get))
+ .addLibraryFiles(JAR_LIBRARIES);
+ ToolHelper.getAppBuilder(builder).addProgramFiles(Paths.get(programFile));
+ ToolHelper.runR8(builder.build(), options -> options.inlineAccessors = inline);
}
public static void shaking1HasNoClassUnused(DexInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
index ceb14cc..0b6d16b 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
@@ -96,16 +96,15 @@
Consumer<InternalOptions> optionsConsumer) {
try {
Path dexOutputDir = temp.newFolder().toPath();
- R8Command command =
- R8Command.builder()
- .addDexProgramData(builder.compile(), EmbeddedOrigin.INSTANCE)
+ R8Command.Builder command =
+ ToolHelper.addProguardConfigurationConsumer(R8Command.builder(), pgConsumer)
.setOutput(dexOutputDir, OutputMode.DexIndexed)
.setMode(CompilationMode.DEBUG)
.addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()))
- .addProguardConfiguration(proguardConfigurations, Origin.unknown())
- .addProguardConfigurationConsumer(pgConsumer)
- .build();
- ToolHelper.runR8WithFullResult(command, optionsConsumer);
+ .addProguardConfiguration(proguardConfigurations, Origin.unknown());
+ ToolHelper.getAppBuilder(command)
+ .addDexProgramData(builder.compile(), EmbeddedOrigin.INSTANCE);
+ ToolHelper.runR8WithFullResult(command.build(), optionsConsumer);
return dexOutputDir.resolve("classes.dex");
} catch (CompilationException | IOException | RecognitionException | ExecutionException
| CompilationFailedException e) {
@@ -189,8 +188,8 @@
protected int getNumberOfProgramClasses(AndroidApp application) {
try {
- return getNumberOfClassesForResources(application.getClassProgramResources())
- + getNumberOfClassesForResources(application.getDexProgramResources());
+ return getNumberOfClassesForResources(application.getClassProgramResourcesForTesting())
+ + getNumberOfClassesForResources(application.getDexProgramResourcesForTesting());
} catch (IOException e) {
return -1;
}
diff --git a/src/test/java/com/android/tools/r8/utils/GenerateMainDexListCommandTest.java b/src/test/java/com/android/tools/r8/utils/GenerateMainDexListCommandTest.java
index 64e7a5b..1c6a050 100644
--- a/src/test/java/com/android/tools/r8/utils/GenerateMainDexListCommandTest.java
+++ b/src/test/java/com/android/tools/r8/utils/GenerateMainDexListCommandTest.java
@@ -45,8 +45,8 @@
}
private void verifyEmptyCommand(GenerateMainDexListCommand command) throws IOException {
- assertEquals(0, ToolHelper.getApp(command).getDexProgramResources().size());
- assertEquals(0, ToolHelper.getApp(command).getClassProgramResources().size());
+ assertEquals(0, ToolHelper.getApp(command).getDexProgramResourcesForTesting().size());
+ assertEquals(0, ToolHelper.getApp(command).getClassProgramResourcesForTesting().size());
assertFalse(ToolHelper.getApp(command).hasMainDexListResources());
}
diff --git a/tests/api_usage_sample.jar b/tests/api_usage_sample.jar
index 59eb757..f258759 100644
--- a/tests/api_usage_sample.jar
+++ b/tests/api_usage_sample.jar
Binary files differ
diff --git a/tests/d8_api_usage_sample.jar b/tests/d8_api_usage_sample.jar
new file mode 100644
index 0000000..7a1949a
--- /dev/null
+++ b/tests/d8_api_usage_sample.jar
Binary files differ
diff --git a/tests/r8_api_usage_sample.jar b/tests/r8_api_usage_sample.jar
new file mode 100644
index 0000000..5c66037
--- /dev/null
+++ b/tests/r8_api_usage_sample.jar
Binary files differ
diff --git a/third_party/android_jar/lib-v22.tar.gz.sha1 b/third_party/android_jar/lib-v22.tar.gz.sha1
new file mode 100644
index 0000000..9993d73
--- /dev/null
+++ b/third_party/android_jar/lib-v22.tar.gz.sha1
@@ -0,0 +1 @@
+1b29ed8eff486944080c2e75d1835f88ead804ef
\ No newline at end of file
diff --git a/third_party/android_jar/lib-v23.tar.gz.sha1 b/third_party/android_jar/lib-v23.tar.gz.sha1
new file mode 100644
index 0000000..e3ce8c7
--- /dev/null
+++ b/third_party/android_jar/lib-v23.tar.gz.sha1
@@ -0,0 +1 @@
+961bf14c97ab2880998b9f6c6b74f9e459e474fe
\ No newline at end of file
diff --git a/tools/test.py b/tools/test.py
index 8c47219..58fc4eb 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -17,7 +17,7 @@
import notify
-ALL_ART_VMS = ["default", "7.0.0", "6.0.1", "5.1.1", "4.4.4"]
+ALL_ART_VMS = ["default", "7.0.0", "6.0.1", "5.1.1", "4.4.4", "4.0.4"]
BUCKET = 'r8-test-results'
def ParseOptions():