Merge "Resolve single vs object in IRBuilder."
diff --git a/build.gradle b/build.gradle
index cd831cc..f379bbf 100644
--- a/build.gradle
+++ b/build.gradle
@@ -111,7 +111,12 @@
}
apiUsageSample {
java {
- srcDirs = ['src/test/apiUsageSample']
+ srcDirs = ['src/test/apiUsageSample', 'src/main/java']
+ include 'com/android/tools/apiusagesample/*.java'
+ include 'com/android/tools/r8/BaseCompilerCommandParser.java'
+ include 'com/android/tools/r8/D8CommandParser.java'
+ include 'com/android/tools/r8/R8CommandParser.java'
+ include 'com/android/tools/r8/utils/FlagFile.java'
}
}
debugTestResources {
diff --git a/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java b/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java
index e17acd0..b3d59fb 100644
--- a/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java
+++ b/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java
@@ -35,6 +35,7 @@
* <p>The descriptor index is built eagerly upon creating the provider and subsequent requests for
* resources in the descriptor set will then force the read of zip entry contents.
*/
+@Keep
public class ArchiveClassFileProvider implements ClassFileResourceProvider, Closeable {
private final Origin origin;
private final ZipFile zipFile;
diff --git a/src/main/java/com/android/tools/r8/ArchiveProgramResourceProvider.java b/src/main/java/com/android/tools/r8/ArchiveProgramResourceProvider.java
index b3a4bfb..baac9bc 100644
--- a/src/main/java/com/android/tools/r8/ArchiveProgramResourceProvider.java
+++ b/src/main/java/com/android/tools/r8/ArchiveProgramResourceProvider.java
@@ -29,8 +29,10 @@
import java.util.zip.ZipFile;
/** Provider for archives of program resources. */
+@KeepForSubclassing
public class ArchiveProgramResourceProvider implements ProgramResourceProvider {
+ @KeepForSubclassing
public interface ZipFileSupplier {
ZipFile open() throws IOException;
}
diff --git a/src/main/java/com/android/tools/r8/BaseCommand.java b/src/main/java/com/android/tools/r8/BaseCommand.java
index fbc6ca3..8828248 100644
--- a/src/main/java/com/android/tools/r8/BaseCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCommand.java
@@ -28,6 +28,7 @@
* <p>For concrete builders, see for example {@link D8Command.Builder} and {@link
* R8Command.Builder}.
*/
+@Keep
public abstract class BaseCommand {
private final boolean printHelp;
@@ -101,6 +102,7 @@
* @param <B> Concrete builder extending this base, e.g., {@link R8Command.Builder} or {@link
* D8Command.Builder}.
*/
+ @Keep
public abstract static class Builder<C extends BaseCommand, B extends Builder<C, B>> {
private final Reporter reporter;
@@ -304,6 +306,20 @@
return self();
}
+ /** Signal an error. */
+ public void error(Diagnostic diagnostic) {
+ reporter.error(diagnostic);
+ }
+
+ /**
+ * Signal an error and throw {@link AbortException}.
+ *
+ * @throws AbortException always.
+ */
+ public RuntimeException fatalError(Diagnostic diagnostic) {
+ return reporter.fatalError(diagnostic);
+ }
+
// Internal helper for compat tools to make them ignore DEX code in input archives.
void setIgnoreDexInArchive(boolean value) {
guard(() -> app.setIgnoreDexInArchive(value));
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index 799d834..5980a0c 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -4,13 +4,11 @@
package com.android.tools.r8;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.StringDiagnostic;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
@@ -23,6 +21,7 @@
* <p>For concrete builders, see for example {@link D8Command.Builder} and {@link
* R8Command.Builder}.
*/
+@Keep
public abstract class BaseCompilerCommand extends BaseCommand {
private final CompilationMode mode;
@@ -106,6 +105,7 @@
* @param <B> Concrete builder extending this base, e.g., {@link R8Command.Builder} or {@link
* D8Command.Builder}.
*/
+ @Keep
public abstract static class Builder<C extends BaseCompilerCommand, B extends Builder<C, B>>
extends BaseCommand.Builder<C, B> {
@@ -341,31 +341,4 @@
}
}
- static <C extends BaseCompilerCommand, B extends BaseCompilerCommand.Builder<C, B>>
- boolean parseMinApi(
- BaseCompilerCommand.Builder<C, B> builder,
- String minApiString,
- boolean hasDefinedApiLevel,
- Origin origin) {
- if (hasDefinedApiLevel) {
- builder.getReporter().error(new StringDiagnostic(
- "Cannot set multiple --min-api options", origin));
- return false;
- }
- int minApi;
- try {
- minApi = Integer.valueOf(minApiString);
- } catch (NumberFormatException e) {
- builder.getReporter().error(new StringDiagnostic(
- "Invalid argument to --min-api: " + minApiString, origin));
- return false;
- }
- if (minApi < 1) {
- builder.getReporter().error(new StringDiagnostic(
- "Invalid argument to --min-api: " + minApiString, origin));
- return false;
- }
- builder.setMinApiLevel(minApi);
- return true;
- }
}
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
new file mode 100644
index 0000000..054db58
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
@@ -0,0 +1,25 @@
+// 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.origin.Origin;
+import com.android.tools.r8.utils.StringDiagnostic;
+
+public class BaseCompilerCommandParser {
+
+ static void parseMinApi(BaseCompilerCommand.Builder builder, String minApiString, Origin origin) {
+ int minApi;
+ try {
+ minApi = Integer.valueOf(minApiString);
+ } catch (NumberFormatException e) {
+ builder.error(new StringDiagnostic("Invalid argument to --min-api: " + minApiString, origin));
+ return;
+ }
+ if (minApi < 1) {
+ builder.error(new StringDiagnostic("Invalid argument to --min-api: " + minApiString, origin));
+ return;
+ }
+ builder.setMinApiLevel(minApi);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ClassFileConsumer.java b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
index 6c9a286..3bab0ab 100644
--- a/src/main/java/com/android/tools/r8/ClassFileConsumer.java
+++ b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
@@ -26,6 +26,7 @@
*
* <p>This consumer can only be provided to R8.
*/
+@KeepForSubclassing
public interface ClassFileConsumer extends ProgramConsumer {
/**
@@ -49,6 +50,7 @@
}
/** Forwarding consumer to delegate to an optional existing consumer. */
+ @Keep
class ForwardingConsumer implements ClassFileConsumer {
private static final ClassFileConsumer EMPTY_CONSUMER = new ForwardingConsumer(null);
@@ -80,6 +82,7 @@
}
/** Consumer to write program resources to an output. */
+ @Keep
class ArchiveConsumer extends ForwardingConsumer
implements DataResourceConsumer, InternalProgramOutputPathConsumer {
private final OutputBuilder outputBuilder;
@@ -163,6 +166,7 @@
}
/** Directory consumer to write program resources to a directory. */
+ @Keep
class DirectoryConsumer extends ForwardingConsumer implements InternalProgramOutputPathConsumer {
private final OutputBuilder outputBuilder;
protected final boolean consumeDataResouces;
diff --git a/src/main/java/com/android/tools/r8/ClassFileResourceProvider.java b/src/main/java/com/android/tools/r8/ClassFileResourceProvider.java
index 4c71d88..db0a9f8 100644
--- a/src/main/java/com/android/tools/r8/ClassFileResourceProvider.java
+++ b/src/main/java/com/android/tools/r8/ClassFileResourceProvider.java
@@ -12,6 +12,7 @@
* only created on-demand when they are needed by the compiler. If never needed, the resource will
* never be loaded.
*/
+@KeepForSubclassing
public interface ClassFileResourceProvider {
/**
diff --git a/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java b/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
index a6923ba..89a33c1 100644
--- a/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
+++ b/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
@@ -6,6 +6,8 @@
import java.nio.file.Path;
+// This class is used by the Android Studio Gradle plugin and is thus part of the R8 API.
+@Keep
public class CompatProguardCommandBuilder extends R8Command.Builder {
public CompatProguardCommandBuilder() {
this(true);
diff --git a/src/main/java/com/android/tools/r8/CompilationFailedException.java b/src/main/java/com/android/tools/r8/CompilationFailedException.java
index 561ad0c..bcc4ad1 100644
--- a/src/main/java/com/android/tools/r8/CompilationFailedException.java
+++ b/src/main/java/com/android/tools/r8/CompilationFailedException.java
@@ -7,6 +7,7 @@
* Exception thrown when compilation failed to complete because of errors previously reported
* through {@link com.android.tools.r8.DiagnosticsHandler}.
*/
+@Keep
public class CompilationFailedException extends Exception {
public CompilationFailedException() {
diff --git a/src/main/java/com/android/tools/r8/CompilationMode.java b/src/main/java/com/android/tools/r8/CompilationMode.java
index f7aa4f0..fb02504 100644
--- a/src/main/java/com/android/tools/r8/CompilationMode.java
+++ b/src/main/java/com/android/tools/r8/CompilationMode.java
@@ -4,6 +4,7 @@
package com.android.tools.r8;
/** Compilation mode. */
+@Keep
public enum CompilationMode {
/** Preserves debugging information during compilation, eg, line-numbers and locals. */
DEBUG,
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 06cf0bb..f4d378a 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -52,6 +52,7 @@
* them to DEX bytecode (compiling from Java bytecode for such inputs and merging for DEX inputs),
* and then writes the result to the directory or zip archive specified by {@code outputPath}.
*/
+@Keep
public final class D8 {
private D8() {}
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 6f956db..8c6c02b 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -3,18 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
-import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.FlagFile;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.StringDiagnostic;
-import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collection;
@@ -31,7 +26,8 @@
* .build();
* </pre>
*/
-public class D8Command extends BaseCompilerCommand {
+@Keep
+public final class D8Command extends BaseCompilerCommand {
private static class ClasspathInputOrigin extends InputFileOrigin {
@@ -45,6 +41,7 @@
*
* <p>A builder is obtained by calling {@link D8Command#builder}.
*/
+ @Keep
public static class Builder extends BaseCompilerCommand.Builder<D8Command, Builder> {
private boolean intermediate = false;
@@ -150,24 +147,7 @@
}
}
- static final String USAGE_MESSAGE = String.join("\n", ImmutableList.of(
- "Usage: d8 [options] <input-files>",
- " where <input-files> are any combination of dex, class, zip, jar, or apk files",
- " and options are:",
- " --debug # Compile with debugging information (default).",
- " --release # Compile without debugging information.",
- " --output <file> # Output result in <outfile>.",
- " # <file> must be an existing directory or a zip file.",
- " --lib <file> # Add <file> as a library resource.",
- " --classpath <file> # Add <file> as a classpath resource.",
- " --min-api # Minimum Android API level compatibility",
- " --intermediate # Compile an intermediate result intended for later",
- " # merging.",
- " --file-per-class # Produce a separate dex file per input class",
- " --no-desugaring # Force disable desugaring.",
- " --main-dex-list <file> # List of classes to place in the primary dex file.",
- " --version # Print the version of d8.",
- " --help # Print this message."));
+ static final String USAGE_MESSAGE = D8CommandParser.USAGE_MESSAGE;
private boolean intermediate = false;
@@ -194,7 +174,7 @@
* @return D8 command builder with state set up according to parsed command line.
*/
public static Builder parse(String[] args, Origin origin) {
- return parse(args, origin, builder());
+ return D8CommandParser.parse(args, origin);
}
/**
@@ -208,87 +188,7 @@
* @return D8 command builder with state set up according to parsed command line.
*/
public static Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
- return parse(args, origin, builder(handler));
- }
-
- private static Builder parse(String[] args, Origin origin, Builder builder) {
- CompilationMode compilationMode = null;
- Path outputPath = null;
- OutputMode outputMode = null;
- boolean hasDefinedApiLevel = false;
- String[] expandedArgs = FlagFile.expandFlagFiles(args, builder.getReporter());
- try {
- for (int i = 0; i < expandedArgs.length; i++) {
- String arg = expandedArgs[i].trim();
- if (arg.length() == 0) {
- continue;
- } else if (arg.equals("--help")) {
- builder.setPrintHelp(true);
- } else if (arg.equals("--version")) {
- builder.setPrintVersion(true);
- } else if (arg.equals("--debug")) {
- if (compilationMode == CompilationMode.RELEASE) {
- builder.getReporter().error(new StringDiagnostic(
- "Cannot compile in both --debug and --release mode.",
- origin));
- continue;
- }
- compilationMode = CompilationMode.DEBUG;
- } else if (arg.equals("--release")) {
- if (compilationMode == CompilationMode.DEBUG) {
- builder.getReporter().error(new StringDiagnostic(
- "Cannot compile in both --debug and --release mode.",
- origin));
- continue;
- }
- compilationMode = CompilationMode.RELEASE;
- } else if (arg.equals("--file-per-class")) {
- outputMode = OutputMode.DexFilePerClassFile;
- } else if (arg.equals("--output")) {
- String output = expandedArgs[++i];
- if (outputPath != null) {
- builder.getReporter().error(new StringDiagnostic(
- "Cannot output both to '" + outputPath.toString() + "' and '" + output + "'",
- origin));
- continue;
- }
- outputPath = Paths.get(output);
- } else if (arg.equals("--lib")) {
- builder.addLibraryFiles(Paths.get(expandedArgs[++i]));
- } else if (arg.equals("--classpath")) {
- builder.addClasspathFiles(Paths.get(expandedArgs[++i]));
- } else if (arg.equals("--main-dex-list")) {
- builder.addMainDexListFiles(Paths.get(expandedArgs[++i]));
- } else if (arg.equals("--optimize-multidex-for-linearalloc")) {
- builder.setOptimizeMultidexForLinearAlloc(true);
- } else if (arg.equals("--min-api")) {
- hasDefinedApiLevel = parseMinApi(builder, expandedArgs[++i], hasDefinedApiLevel, origin);
- } else if (arg.equals("--intermediate")) {
- builder.setIntermediate(true);
- } else if (arg.equals("--no-desugaring")) {
- builder.setDisableDesugaring(true);
- } else {
- if (arg.startsWith("--")) {
- builder.getReporter().error(new StringDiagnostic("Unknown option: " + arg,
- origin));
- continue;
- }
- builder.addProgramFiles(Paths.get(arg));
- }
- }
- if (compilationMode != null) {
- builder.setMode(compilationMode);
- }
- if (outputMode == null) {
- outputMode = OutputMode.DexIndexed;
- }
- if (outputPath == null) {
- outputPath = Paths.get(".");
- }
- return builder.setOutput(outputPath, outputMode);
- } catch (CompilationError e) {
- throw builder.getReporter().fatalError(e);
- }
+ return D8CommandParser.parse(args, origin, handler);
}
private D8Command(
diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java
new file mode 100644
index 0000000..9e5d36a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
@@ -0,0 +1,157 @@
+// 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.errors.CompilationError;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.FlagFile;
+import com.android.tools.r8.utils.StringDiagnostic;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+
+public class D8CommandParser extends BaseCompilerCommandParser {
+
+ public static void main(String[] args) throws CompilationFailedException {
+ D8Command command = parse(args, Origin.root()).build();
+ if (command.isPrintHelp()) {
+ System.out.println(USAGE_MESSAGE);
+ System.exit(1);
+ }
+ D8.run(command);
+ }
+
+ static final String USAGE_MESSAGE =
+ String.join(
+ "\n",
+ Arrays.asList(
+ "Usage: d8 [options] <input-files>",
+ " where <input-files> are any combination of dex, class, zip, jar, or apk files",
+ " and options are:",
+ " --debug # Compile with debugging information (default).",
+ " --release # Compile without debugging information.",
+ " --output <file> # Output result in <outfile>.",
+ " # <file> must be an existing directory or a zip file.",
+ " --lib <file> # Add <file> as a library resource.",
+ " --classpath <file> # Add <file> as a classpath resource.",
+ " --min-api # Minimum Android API level compatibility",
+ " --intermediate # Compile an intermediate result intended for later",
+ " # merging.",
+ " --file-per-class # Produce a separate dex file per input class",
+ " --no-desugaring # Force disable desugaring.",
+ " --main-dex-list <file> # List of classes to place in the primary dex file.",
+ " --version # Print the version of d8.",
+ " --help # Print this message."));
+
+ /**
+ * Parse the D8 command-line.
+ *
+ * <p>Parsing will set the supplied options or their default value if they have any.
+ *
+ * @param args Command-line arguments array.
+ * @param origin Origin description of the command-line arguments.
+ * @return D8 command builder with state set up according to parsed command line.
+ */
+ public static D8Command.Builder parse(String[] args, Origin origin) {
+ return parse(args, origin, D8Command.builder());
+ }
+
+ /**
+ * Parse the D8 command-line.
+ *
+ * <p>Parsing will set the supplied options or their default value if they have any.
+ *
+ * @param args Command-line arguments array.
+ * @param origin Origin description of the command-line arguments.
+ * @param handler Custom defined diagnostics handler.
+ * @return D8 command builder with state set up according to parsed command line.
+ */
+ public static D8Command.Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
+ return parse(args, origin, D8Command.builder(handler));
+ }
+
+ private static D8Command.Builder parse(String[] args, Origin origin, D8Command.Builder builder) {
+ CompilationMode compilationMode = null;
+ Path outputPath = null;
+ OutputMode outputMode = null;
+ boolean hasDefinedApiLevel = false;
+ String[] expandedArgs = FlagFile.expandFlagFiles(args, builder);
+ try {
+ for (int i = 0; i < expandedArgs.length; i++) {
+ String arg = expandedArgs[i].trim();
+ if (arg.length() == 0) {
+ continue;
+ } else if (arg.equals("--help")) {
+ builder.setPrintHelp(true);
+ } else if (arg.equals("--version")) {
+ builder.setPrintVersion(true);
+ } else if (arg.equals("--debug")) {
+ if (compilationMode == CompilationMode.RELEASE) {
+ builder.error(
+ new StringDiagnostic("Cannot compile in both --debug and --release mode.", origin));
+ continue;
+ }
+ compilationMode = CompilationMode.DEBUG;
+ } else if (arg.equals("--release")) {
+ if (compilationMode == CompilationMode.DEBUG) {
+ builder.error(
+ new StringDiagnostic("Cannot compile in both --debug and --release mode.", origin));
+ continue;
+ }
+ compilationMode = CompilationMode.RELEASE;
+ } else if (arg.equals("--file-per-class")) {
+ outputMode = OutputMode.DexFilePerClassFile;
+ } else if (arg.equals("--output")) {
+ String output = expandedArgs[++i];
+ if (outputPath != null) {
+ builder.error(
+ new StringDiagnostic(
+ "Cannot output both to '" + outputPath.toString() + "' and '" + output + "'",
+ origin));
+ continue;
+ }
+ outputPath = Paths.get(output);
+ } else if (arg.equals("--lib")) {
+ builder.addLibraryFiles(Paths.get(expandedArgs[++i]));
+ } else if (arg.equals("--classpath")) {
+ builder.addClasspathFiles(Paths.get(expandedArgs[++i]));
+ } else if (arg.equals("--main-dex-list")) {
+ builder.addMainDexListFiles(Paths.get(expandedArgs[++i]));
+ } else if (arg.equals("--optimize-multidex-for-linearalloc")) {
+ builder.setOptimizeMultidexForLinearAlloc(true);
+ } else if (arg.equals("--min-api")) {
+ String minApiString = expandedArgs[++i];
+ if (hasDefinedApiLevel) {
+ builder.error(new StringDiagnostic("Cannot set multiple --min-api options", origin));
+ } else {
+ parseMinApi(builder, minApiString, origin);
+ hasDefinedApiLevel = true;
+ }
+ } else if (arg.equals("--intermediate")) {
+ builder.setIntermediate(true);
+ } else if (arg.equals("--no-desugaring")) {
+ builder.setDisableDesugaring(true);
+ } else {
+ if (arg.startsWith("--")) {
+ builder.error(new StringDiagnostic("Unknown option: " + arg, origin));
+ continue;
+ }
+ builder.addProgramFiles(Paths.get(arg));
+ }
+ }
+ if (compilationMode != null) {
+ builder.setMode(compilationMode);
+ }
+ if (outputMode == null) {
+ outputMode = OutputMode.DexIndexed;
+ }
+ if (outputPath == null) {
+ outputPath = Paths.get(".");
+ }
+ return builder.setOutput(outputPath, outputMode);
+ } catch (CompilationError e) {
+ throw builder.fatalError(e);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/DataDirectoryResource.java b/src/main/java/com/android/tools/r8/DataDirectoryResource.java
index 2f421f7..3e860e3 100644
--- a/src/main/java/com/android/tools/r8/DataDirectoryResource.java
+++ b/src/main/java/com/android/tools/r8/DataDirectoryResource.java
@@ -12,6 +12,7 @@
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
+@Keep
public interface DataDirectoryResource extends DataResource {
static DataDirectoryResource fromFile(Path dir, Path file) {
diff --git a/src/main/java/com/android/tools/r8/DataEntryResource.java b/src/main/java/com/android/tools/r8/DataEntryResource.java
index 189d5eb..aa9c73f 100644
--- a/src/main/java/com/android/tools/r8/DataEntryResource.java
+++ b/src/main/java/com/android/tools/r8/DataEntryResource.java
@@ -15,6 +15,7 @@
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
+@Keep
public interface DataEntryResource extends DataResource {
/** Get the bytes of the data entry resource. */
diff --git a/src/main/java/com/android/tools/r8/DataResource.java b/src/main/java/com/android/tools/r8/DataResource.java
index 4609c97..eac8b6c 100644
--- a/src/main/java/com/android/tools/r8/DataResource.java
+++ b/src/main/java/com/android/tools/r8/DataResource.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+@Keep
public interface DataResource extends Resource {
char SEPARATOR = '/';
diff --git a/src/main/java/com/android/tools/r8/DataResourceConsumer.java b/src/main/java/com/android/tools/r8/DataResourceConsumer.java
index 215ca59..e306b9b 100644
--- a/src/main/java/com/android/tools/r8/DataResourceConsumer.java
+++ b/src/main/java/com/android/tools/r8/DataResourceConsumer.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+@KeepForSubclassing
public interface DataResourceConsumer {
void accept(DataDirectoryResource directory, DiagnosticsHandler diagnosticsHandler);
diff --git a/src/main/java/com/android/tools/r8/DataResourceProvider.java b/src/main/java/com/android/tools/r8/DataResourceProvider.java
index e851f0f..5e0a24f 100644
--- a/src/main/java/com/android/tools/r8/DataResourceProvider.java
+++ b/src/main/java/com/android/tools/r8/DataResourceProvider.java
@@ -3,8 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+@KeepForSubclassing
public interface DataResourceProvider {
+ @KeepForSubclassing
interface Visitor {
void visit(DataDirectoryResource directory);
void visit(DataEntryResource file);
diff --git a/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java b/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
index f1c0fac..311a3dd 100644
--- a/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
+++ b/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
@@ -29,6 +29,7 @@
*
* <p>This consumer receives DEX file content for each Java class-file input.
*/
+@KeepForSubclassing
public interface DexFilePerClassFileConsumer extends ProgramConsumer {
/**
@@ -57,6 +58,7 @@
}
/** Forwarding consumer to delegate to an optional existing consumer. */
+ @Keep
class ForwardingConsumer implements DexFilePerClassFileConsumer {
private static final DexFilePerClassFileConsumer EMPTY_CONSUMER = new ForwardingConsumer(null);
@@ -92,6 +94,7 @@
}
/** Consumer to write program resources to an output. */
+ @Keep
class ArchiveConsumer extends ForwardingConsumer
implements DataResourceConsumer, InternalProgramOutputPathConsumer {
private final OutputBuilder outputBuilder;
@@ -181,6 +184,7 @@
}
/** Directory consumer to write program resources to a directory. */
+ @Keep
class DirectoryConsumer extends ForwardingConsumer
implements DataResourceConsumer, InternalProgramOutputPathConsumer {
private final OutputBuilder outputBuilder;
diff --git a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
index 3bbab64..e802103 100644
--- a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
+++ b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
@@ -31,6 +31,7 @@
* <p>This consumer receives DEX file content using standard indexed-multidex for programs larger
* than a single DEX file. This is the default consumer for DEX programs.
*/
+@KeepForSubclassing
public interface DexIndexedConsumer extends ProgramConsumer {
/**
@@ -58,6 +59,7 @@
}
/** Forwarding consumer to delegate to an optional existing consumer. */
+ @Keep
class ForwardingConsumer implements DexIndexedConsumer {
private static final DexIndexedConsumer EMPTY_CONSUMER = new ForwardingConsumer(null);
@@ -100,6 +102,7 @@
}
/** Consumer to write program resources to an output. */
+ @Keep
class ArchiveConsumer extends ForwardingConsumer
implements DataResourceConsumer, InternalProgramOutputPathConsumer {
protected final OutputBuilder outputBuilder;
@@ -181,6 +184,7 @@
}
}
+ @Keep
class DirectoryConsumer extends ForwardingConsumer
implements DataResourceConsumer, InternalProgramOutputPathConsumer {
private final Path directory;
diff --git a/src/main/java/com/android/tools/r8/DexSplitterHelper.java b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
index 2d52ec9..3e299f2 100644
--- a/src/main/java/com/android/tools/r8/DexSplitterHelper.java
+++ b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
@@ -31,7 +31,8 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
-public class DexSplitterHelper {
+@Keep
+public final class DexSplitterHelper {
public static void run(
D8Command command, FeatureClassMapping featureClassMapping, String output, String proguardMap)
diff --git a/src/main/java/com/android/tools/r8/Diagnostic.java b/src/main/java/com/android/tools/r8/Diagnostic.java
index 7896251..3184d54 100644
--- a/src/main/java/com/android/tools/r8/Diagnostic.java
+++ b/src/main/java/com/android/tools/r8/Diagnostic.java
@@ -9,6 +9,7 @@
/**
* Interface for all diagnostic message produced by D8 and R8.
*/
+@Keep
public interface Diagnostic {
/**
diff --git a/src/main/java/com/android/tools/r8/DiagnosticsHandler.java b/src/main/java/com/android/tools/r8/DiagnosticsHandler.java
index 8212835..74a9a96 100644
--- a/src/main/java/com/android/tools/r8/DiagnosticsHandler.java
+++ b/src/main/java/com/android/tools/r8/DiagnosticsHandler.java
@@ -10,6 +10,7 @@
*
* <p>During compilation the warning and info methods will be called.
*/
+@Keep
public interface DiagnosticsHandler {
/**
diff --git a/src/main/java/com/android/tools/r8/DirectoryClassFileProvider.java b/src/main/java/com/android/tools/r8/DirectoryClassFileProvider.java
index 5d5f751..50aff3f 100644
--- a/src/main/java/com/android/tools/r8/DirectoryClassFileProvider.java
+++ b/src/main/java/com/android/tools/r8/DirectoryClassFileProvider.java
@@ -19,6 +19,7 @@
* Lazy resource provider returning class file resources based
* on filesystem directory content.
*/
+@Keep
public final class DirectoryClassFileProvider implements ClassFileResourceProvider {
private final Path root;
diff --git a/src/main/java/com/android/tools/r8/Keep.java b/src/main/java/com/android/tools/r8/Keep.java
new file mode 100644
index 0000000..13ed8c1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/Keep.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 com.android.tools.r8;
+
+@Keep
+public @interface Keep {
+
+}
diff --git a/src/main/java/com/android/tools/r8/KeepForSubclassing.java b/src/main/java/com/android/tools/r8/KeepForSubclassing.java
new file mode 100644
index 0000000..6e57114
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/KeepForSubclassing.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 com.android.tools.r8;
+
+@Keep
+public @interface KeepForSubclassing {
+
+}
diff --git a/src/main/java/com/android/tools/r8/OutputMode.java b/src/main/java/com/android/tools/r8/OutputMode.java
index 0da34b7..bc8a8d8 100644
--- a/src/main/java/com/android/tools/r8/OutputMode.java
+++ b/src/main/java/com/android/tools/r8/OutputMode.java
@@ -4,6 +4,7 @@
package com.android.tools.r8;
/** Enumeration of the possible output formats of compilation. */
+@Keep
public enum OutputMode {
/** Produce DEX files using standard indexed-multidex for programs larger that a single file. */
diff --git a/src/main/java/com/android/tools/r8/ProgramConsumer.java b/src/main/java/com/android/tools/r8/ProgramConsumer.java
index 08d7d7c..b3143a0 100644
--- a/src/main/java/com/android/tools/r8/ProgramConsumer.java
+++ b/src/main/java/com/android/tools/r8/ProgramConsumer.java
@@ -6,6 +6,7 @@
/**
* Base for all program consumers to allow abstracting which concrete consumer is provided to D8/R8.
*/
+@KeepForSubclassing
public interface ProgramConsumer {
/**
diff --git a/src/main/java/com/android/tools/r8/ProgramResource.java b/src/main/java/com/android/tools/r8/ProgramResource.java
index cabeabc..ffeb558 100644
--- a/src/main/java/com/android/tools/r8/ProgramResource.java
+++ b/src/main/java/com/android/tools/r8/ProgramResource.java
@@ -20,9 +20,11 @@
* A resource may optionally include a set describing the class descriptors for each type that is
* defined by the resource.
*/
+@KeepForSubclassing
public interface ProgramResource extends Resource {
/** Type of program-format kinds. */
+ @Keep
enum Kind {
/** Format-kind for Java class-file resources. */
CF,
@@ -66,6 +68,7 @@
Set<String> getClassDescriptors();
/** File-based program resource. */
+ @Keep
class FileResource implements ProgramResource {
private final Origin origin;
private final Kind kind;
@@ -105,6 +108,7 @@
}
/** Byte-content based program resource. */
+ @Keep
class ByteResource implements ProgramResource {
private final Origin origin;
private final Kind kind;
diff --git a/src/main/java/com/android/tools/r8/ProgramResourceProvider.java b/src/main/java/com/android/tools/r8/ProgramResourceProvider.java
index 76d340c..d4b8a87 100644
--- a/src/main/java/com/android/tools/r8/ProgramResourceProvider.java
+++ b/src/main/java/com/android/tools/r8/ProgramResourceProvider.java
@@ -6,6 +6,7 @@
import java.util.Collection;
/** Program resource provider. */
+@KeepForSubclassing
public interface ProgramResourceProvider {
Collection<ProgramResource> getProgramResources() throws ResourceException;
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 5d29d90..077e220 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -103,6 +103,7 @@
* 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}.
*/
+@Keep
public class R8 {
private final Timing timing = new Timing("R8");
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 573f6df..86ff5a5 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -18,7 +18,6 @@
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.FlagFile;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
import com.android.tools.r8.utils.Reporter;
@@ -46,13 +45,15 @@
* .build();
* </pre>
*/
-public class R8Command extends BaseCompilerCommand {
+@Keep
+public final class R8Command extends BaseCompilerCommand {
/**
* Builder for constructing a R8Command.
*
* <p>A builder is obtained by calling {@link R8Command#builder}.
*/
+ @Keep
public static class Builder extends BaseCompilerCommand.Builder<R8Command, Builder> {
private final List<ProguardConfigurationSource> mainDexRules = new ArrayList<>();
@@ -421,38 +422,7 @@
}
}
- // Internal state to verify parsing properties not enforced by the builder.
- private static class ParseState {
- CompilationMode mode = null;
- OutputMode outputMode = null;
- Path outputPath = null;
- boolean hasDefinedApiLevel = false;
- }
-
- static final String USAGE_MESSAGE = String.join("\n", ImmutableList.of(
- "Usage: r8 [options] <input-files>",
- " where <input-files> are any combination of dex, class, zip, jar, or apk files",
- " and options are:",
- " --release # Compile without debugging information (default).",
- " --debug # Compile with debugging information.",
- // TODO(b/65390962): Add help for output-mode flags once the CF backend is complete.
- //" --dex # Compile program to DEX file format (default).",
- //" --classfile # Compile program to Java classfile format.",
- " --output <file> # Output result in <file>.",
- " # <file> must be an existing directory or a zip file.",
- " --lib <file> # Add <file> as a library resource.",
- " --min-api # Minimum Android API level compatibility.",
- " --pg-conf <file> # Proguard configuration <file>.",
- " --pg-map-output <file> # Output the resulting name and line mapping to <file>.",
- " --no-tree-shaking # Force disable tree shaking of unreachable classes.",
- " --no-minification # Force disable minification of names.",
- " --no-desugaring # Force disable desugaring.",
- " --main-dex-rules <file> # Proguard keep rules for classes to place in the",
- " # primary dex file.",
- " --main-dex-list <file> # List of classes to place in the primary dex file.",
- " --main-dex-list-output <file> # Output the full main-dex list in <file>.",
- " --version # Print the version of r8.",
- " --help # Print this message."));
+ static final String USAGE_MESSAGE = R8CommandParser.USAGE_MESSAGE;
private final ImmutableList<ProguardConfigurationRule> mainDexKeepRules;
private final StringConsumer mainDexListConsumer;
@@ -488,7 +458,7 @@
* @return R8 command builder with state set up according to parsed command line.
*/
public static Builder parse(String[] args, Origin origin) {
- return parse(args, origin, builder());
+ return R8CommandParser.parse(args, origin);
}
/**
@@ -502,102 +472,7 @@
* @return R8 command builder with state set up according to parsed command line.
*/
public static Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
- return parse(args, origin, builder(handler));
- }
-
- private static Builder parse(String[] args, Origin origin, Builder builder) {
- ParseState state = new ParseState();
- parse(args, origin, builder, state);
- if (state.mode != null) {
- builder.setMode(state.mode);
- }
- Path outputPath = state.outputPath != null ? state.outputPath : Paths.get(".");
- OutputMode outputMode = state.outputMode != null ? state.outputMode : OutputMode.DexIndexed;
- builder.setOutput(outputPath, outputMode);
- return builder;
- }
-
- private static ParseState parse(
- String[] args,
- Origin argsOrigin,
- Builder builder,
- ParseState state) {
- String[] expandedArgs = FlagFile.expandFlagFiles(args, builder.getReporter());
- for (int i = 0; i < expandedArgs.length; i++) {
- String arg = expandedArgs[i].trim();
- if (arg.length() == 0) {
- continue;
- } else if (arg.equals("--help")) {
- builder.setPrintHelp(true);
- } else if (arg.equals("--version")) {
- builder.setPrintVersion(true);
- } else if (arg.equals("--debug")) {
- if (state.mode == CompilationMode.RELEASE) {
- builder.getReporter().error(new StringDiagnostic(
- "Cannot compile in both --debug and --release mode.", argsOrigin));
- }
- state.mode = CompilationMode.DEBUG;
- } else if (arg.equals("--release")) {
- if (state.mode == CompilationMode.DEBUG) {
- builder.getReporter().error(new StringDiagnostic(
- "Cannot compile in both --debug and --release mode.", argsOrigin));
- }
- state.mode = CompilationMode.RELEASE;
- } else if (arg.equals("--dex")) {
- if (state.outputMode == OutputMode.ClassFile) {
- builder.getReporter().error(new StringDiagnostic(
- "Cannot compile in both --dex and --classfile output mode.", argsOrigin));
- }
- state.outputMode = OutputMode.DexIndexed;
- } else if (arg.equals("--classfile")) {
- if (state.outputMode == OutputMode.DexIndexed) {
- builder.getReporter().error(new StringDiagnostic(
- "Cannot compile in both --dex and --classfile output mode.", argsOrigin));
- }
- state.outputMode = OutputMode.ClassFile;
- } else if (arg.equals("--output")) {
- String outputPath = expandedArgs[++i];
- if (state.outputPath != null) {
- builder.getReporter().error(new StringDiagnostic(
- "Cannot output both to '"
- + state.outputPath.toString()
- + "' and '"
- + outputPath
- + "'",
- argsOrigin));
- }
- state.outputPath = Paths.get(outputPath);
- } else if (arg.equals("--lib")) {
- builder.addLibraryFiles(Paths.get(expandedArgs[++i]));
- } else if (arg.equals("--min-api")) {
- state.hasDefinedApiLevel =
- parseMinApi(builder, expandedArgs[++i], state.hasDefinedApiLevel, argsOrigin);
- } else if (arg.equals("--no-tree-shaking")) {
- builder.setDisableTreeShaking(true);
- } else if (arg.equals("--no-minification")) {
- builder.setDisableMinification(true);
- } else if (arg.equals("--no-desugaring")) {
- builder.setDisableDesugaring(true);
- } else if (arg.equals("--main-dex-rules")) {
- builder.addMainDexRulesFiles(Paths.get(expandedArgs[++i]));
- } else if (arg.equals("--main-dex-list")) {
- builder.addMainDexListFiles(Paths.get(expandedArgs[++i]));
- } else if (arg.equals("--main-dex-list-output")) {
- builder.setMainDexListOutputPath(Paths.get(expandedArgs[++i]));
- } else if (arg.equals("--optimize-multidex-for-linearalloc")) {
- builder.setOptimizeMultidexForLinearAlloc(true);
- } else if (arg.equals("--pg-conf")) {
- builder.addProguardConfigurationFiles(Paths.get(expandedArgs[++i]));
- } else if (arg.equals("--pg-map-output")) {
- builder.setProguardMapOutputPath(Paths.get(expandedArgs[++i]));
- } else {
- if (arg.startsWith("--")) {
- builder.getReporter().error(new StringDiagnostic("Unknown option: " + arg, argsOrigin));
- }
- builder.addProgramFiles(Paths.get(arg));
- }
- }
- return state;
+ return R8CommandParser.parse(args, origin, handler);
}
private R8Command(
diff --git a/src/main/java/com/android/tools/r8/R8CommandParser.java b/src/main/java/com/android/tools/r8/R8CommandParser.java
new file mode 100644
index 0000000..3d33037
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/R8CommandParser.java
@@ -0,0 +1,186 @@
+// 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.origin.Origin;
+import com.android.tools.r8.utils.FlagFile;
+import com.android.tools.r8.utils.StringDiagnostic;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+
+public class R8CommandParser extends BaseCompilerCommandParser {
+
+ public static void main(String[] args) throws CompilationFailedException {
+ R8Command command = parse(args, Origin.root()).build();
+ if (command.isPrintHelp()) {
+ System.out.println(USAGE_MESSAGE);
+ System.exit(1);
+ }
+ R8.run(command);
+ }
+
+ // Internal state to verify parsing properties not enforced by the builder.
+ private static class ParseState {
+ CompilationMode mode = null;
+ OutputMode outputMode = null;
+ Path outputPath = null;
+ boolean hasDefinedApiLevel = false;
+ }
+
+ static final String USAGE_MESSAGE =
+ String.join(
+ "\n",
+ Arrays.asList(
+ "Usage: r8 [options] <input-files>",
+ " where <input-files> are any combination of dex, class, zip, jar, or apk files",
+ " and options are:",
+ " --release # Compile without debugging information (default).",
+ " --debug # Compile with debugging information.",
+ // TODO(b/65390962): Add help for output-mode flags once the CF backend is complete.
+ // " --dex # Compile program to DEX file format (default).",
+ // " --classfile # Compile program to Java classfile format.",
+ " --output <file> # Output result in <file>.",
+ " # <file> must be an existing directory or a zip file.",
+ " --lib <file> # Add <file> as a library resource.",
+ " --min-api # Minimum Android API level compatibility.",
+ " --pg-conf <file> # Proguard configuration <file>.",
+ " --pg-map-output <file> # Output the resulting name and line mapping to <file>.",
+ " --no-tree-shaking # Force disable tree shaking of unreachable classes.",
+ " --no-minification # Force disable minification of names.",
+ " --no-desugaring # Force disable desugaring.",
+ " --main-dex-rules <file> # Proguard keep rules for classes to place in the",
+ " # primary dex file.",
+ " --main-dex-list <file> # List of classes to place in the primary dex file.",
+ " --main-dex-list-output <file> # Output the full main-dex list in <file>.",
+ " --version # Print the version of r8.",
+ " --help # Print this message."));
+ /**
+ * Parse the R8 command-line.
+ *
+ * <p>Parsing will set the supplied options or their default value if they have any.
+ *
+ * @param args Command-line arguments array.
+ * @param origin Origin description of the command-line arguments.
+ * @return R8 command builder with state set up according to parsed command line.
+ */
+ public static R8Command.Builder parse(String[] args, Origin origin) {
+ return new R8CommandParser().parse(args, origin, R8Command.builder());
+ }
+
+ /**
+ * Parse the R8 command-line.
+ *
+ * <p>Parsing will set the supplied options or their default value if they have any.
+ *
+ * @param args Command-line arguments array.
+ * @param origin Origin description of the command-line arguments.
+ * @param handler Custom defined diagnostics handler.
+ * @return R8 command builder with state set up according to parsed command line.
+ */
+ public static R8Command.Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
+ return new R8CommandParser().parse(args, origin, R8Command.builder(handler));
+ }
+
+ private R8Command.Builder parse(String[] args, Origin origin, R8Command.Builder builder) {
+ ParseState state = new ParseState();
+ parse(args, origin, builder, state);
+ if (state.mode != null) {
+ builder.setMode(state.mode);
+ }
+ Path outputPath = state.outputPath != null ? state.outputPath : Paths.get(".");
+ OutputMode outputMode = state.outputMode != null ? state.outputMode : OutputMode.DexIndexed;
+ builder.setOutput(outputPath, outputMode);
+ return builder;
+ }
+
+ private void parse(
+ String[] args, Origin argsOrigin, R8Command.Builder builder, ParseState state) {
+ String[] expandedArgs = FlagFile.expandFlagFiles(args, builder);
+ for (int i = 0; i < expandedArgs.length; i++) {
+ String arg = expandedArgs[i].trim();
+ if (arg.length() == 0) {
+ continue;
+ } else if (arg.equals("--help")) {
+ builder.setPrintHelp(true);
+ } else if (arg.equals("--version")) {
+ builder.setPrintVersion(true);
+ } else if (arg.equals("--debug")) {
+ if (state.mode == CompilationMode.RELEASE) {
+ builder.error(
+ new StringDiagnostic(
+ "Cannot compile in both --debug and --release mode.", argsOrigin));
+ }
+ state.mode = CompilationMode.DEBUG;
+ } else if (arg.equals("--release")) {
+ if (state.mode == CompilationMode.DEBUG) {
+ builder.error(
+ new StringDiagnostic(
+ "Cannot compile in both --debug and --release mode.", argsOrigin));
+ }
+ state.mode = CompilationMode.RELEASE;
+ } else if (arg.equals("--dex")) {
+ if (state.outputMode == OutputMode.ClassFile) {
+ builder.error(
+ new StringDiagnostic(
+ "Cannot compile in both --dex and --classfile output mode.", argsOrigin));
+ }
+ state.outputMode = OutputMode.DexIndexed;
+ } else if (arg.equals("--classfile")) {
+ if (state.outputMode == OutputMode.DexIndexed) {
+ builder.error(
+ new StringDiagnostic(
+ "Cannot compile in both --dex and --classfile output mode.", argsOrigin));
+ }
+ state.outputMode = OutputMode.ClassFile;
+ } else if (arg.equals("--output")) {
+ String outputPath = expandedArgs[++i];
+ if (state.outputPath != null) {
+ builder.error(
+ new StringDiagnostic(
+ "Cannot output both to '"
+ + state.outputPath.toString()
+ + "' and '"
+ + outputPath
+ + "'",
+ argsOrigin));
+ }
+ state.outputPath = Paths.get(outputPath);
+ } else if (arg.equals("--lib")) {
+ builder.addLibraryFiles(Paths.get(expandedArgs[++i]));
+ } else if (arg.equals("--min-api")) {
+ String minApiString = expandedArgs[++i];
+ if (state.hasDefinedApiLevel) {
+ builder.error(new StringDiagnostic("Cannot set multiple --min-api options", argsOrigin));
+ } else {
+ parseMinApi(builder, minApiString, argsOrigin);
+ state.hasDefinedApiLevel = true;
+ }
+ } else if (arg.equals("--no-tree-shaking")) {
+ builder.setDisableTreeShaking(true);
+ } else if (arg.equals("--no-minification")) {
+ builder.setDisableMinification(true);
+ } else if (arg.equals("--no-desugaring")) {
+ builder.setDisableDesugaring(true);
+ } else if (arg.equals("--main-dex-rules")) {
+ builder.addMainDexRulesFiles(Paths.get(expandedArgs[++i]));
+ } else if (arg.equals("--main-dex-list")) {
+ builder.addMainDexListFiles(Paths.get(expandedArgs[++i]));
+ } else if (arg.equals("--main-dex-list-output")) {
+ builder.setMainDexListOutputPath(Paths.get(expandedArgs[++i]));
+ } else if (arg.equals("--optimize-multidex-for-linearalloc")) {
+ builder.setOptimizeMultidexForLinearAlloc(true);
+ } else if (arg.equals("--pg-conf")) {
+ builder.addProguardConfigurationFiles(Paths.get(expandedArgs[++i]));
+ } else if (arg.equals("--pg-map-output")) {
+ builder.setProguardMapOutputPath(Paths.get(expandedArgs[++i]));
+ } else {
+ if (arg.startsWith("--")) {
+ builder.error(new StringDiagnostic("Unknown option: " + arg, argsOrigin));
+ }
+ builder.addProgramFiles(Paths.get(arg));
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/Resource.java b/src/main/java/com/android/tools/r8/Resource.java
index b64fe01..50c50cc 100644
--- a/src/main/java/com/android/tools/r8/Resource.java
+++ b/src/main/java/com/android/tools/r8/Resource.java
@@ -19,6 +19,7 @@
* The D8/R8 compilers uses default implementations for various file-system resources, but the
* client is free to provide their own.
*/
+@KeepForSubclassing
public interface Resource {
/**
* Get the origin of the resource.
diff --git a/src/main/java/com/android/tools/r8/ResourceException.java b/src/main/java/com/android/tools/r8/ResourceException.java
index 3af7efa..2726b92 100644
--- a/src/main/java/com/android/tools/r8/ResourceException.java
+++ b/src/main/java/com/android/tools/r8/ResourceException.java
@@ -11,6 +11,7 @@
* For example, this is the expected exception that must be thrown if a resource fails to produce
* its content. See {@link ProgramResource#getByteStream()} and {@link StringResource#getString()}.
*/
+@Keep
public class ResourceException extends Exception {
private final Origin origin;
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index 7daef42..6cbad63 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -481,13 +481,14 @@
return result;
}
- public boolean canTriggerStaticInitializer(DexType clazz) {
+ public boolean canTriggerStaticInitializer(DexType type) {
Set<DexType> knownInterfaces = Sets.newIdentityHashSet();
// Process superclass chain.
+ DexType clazz = type;
while (clazz != null && clazz != dexItemFactory.objectType) {
DexClass definition = definitionFor(clazz);
- if (definition == null || definition.hasClassInitializer()) {
+ if (canTriggerStaticInitializer(definition)) {
return true; // Assume it *may* trigger if we didn't find the definition.
}
knownInterfaces.addAll(Arrays.asList(definition.interfaces.values));
@@ -499,10 +500,10 @@
while (!queue.isEmpty()) {
DexType iface = queue.remove();
DexClass definition = definitionFor(iface);
- if (definition == null || definition.hasClassInitializer()) {
+ if (canTriggerStaticInitializer(definition)) {
return true; // Assume it *may* trigger if we didn't find the definition.
}
- if (!definition.accessFlags.isInterface()) {
+ if (!definition.isInterface()) {
throw new Unreachable(iface.toSourceString() + " is expected to be an interface");
}
@@ -515,6 +516,10 @@
return false;
}
+ private static boolean canTriggerStaticInitializer(DexClass clazz) {
+ return clazz == null || clazz.hasClassInitializer();
+ }
+
public interface ResolutionResult {
DexEncodedMethod asResultOfResolve();
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index d178e4a..02d2898 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -224,6 +224,34 @@
}
}
+ /**
+ * Collect all interfaces that this type directly or indirectly implements.
+ * @param appInfo where the definition of a certain {@link DexType} is looked up.
+ * @return a set of interfaces of {@link DexType}.
+ */
+ public Set<DexType> implementedInterfaces(AppInfo appInfo) {
+ Set<DexType> interfaces = Sets.newIdentityHashSet();
+ implementedInterfaces(appInfo, interfaces);
+ return interfaces;
+ }
+
+ private void implementedInterfaces(AppInfo appInfo, Set<DexType> interfaces) {
+ DexClass dexClass = appInfo.definitionFor(this);
+ // Loop to traverse the super type hierarchy of the current type.
+ while (dexClass != null) {
+ if (dexClass.isInterface()) {
+ interfaces.add(dexClass.type);
+ }
+ for (DexType itf : dexClass.interfaces.values) {
+ itf.implementedInterfaces(appInfo, interfaces);
+ }
+ if (dexClass.superType == null) {
+ break;
+ }
+ dexClass = appInfo.definitionFor(dexClass.superType);
+ }
+ }
+
public boolean isSamePackage(DexType other) {
return getPackageDescriptor().equals(other.getPackageDescriptor());
}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index ef247a0..df30c18 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -813,8 +813,8 @@
if (overlappingMoveExceptionIntervals) {
for (LiveIntervals intervals : moveExceptionIntervals) {
if (intervals.getUses().size() > 1) {
- LiveIntervalsUse secondUse = intervals.getUses().stream().skip(1).findFirst().get();
- LiveIntervals split = intervals.splitBefore(secondUse.getPosition());
+ LiveIntervals split =
+ intervals.splitBefore(intervals.getFirstUse() + INSTRUCTION_NUMBER_DELTA);
unhandled.add(split);
}
}
@@ -1354,6 +1354,12 @@
if (!hasDedicatedMoveExceptionRegister()) {
return false;
}
+ // If there are that many move exception intervals we don't spent the time
+ // going through them all. In that case it is unlikely that we can reuse the move exception
+ // register in any case.
+ if (moveExceptionIntervals.size() > 1000) {
+ return true;
+ }
for (LiveIntervals moveExceptionInterval : moveExceptionIntervals) {
if (intervals.anySplitOverlaps(moveExceptionInterval)) {
return true;
diff --git a/src/main/java/com/android/tools/r8/origin/ArchiveEntryOrigin.java b/src/main/java/com/android/tools/r8/origin/ArchiveEntryOrigin.java
index 0a6c84a..37ae662 100644
--- a/src/main/java/com/android/tools/r8/origin/ArchiveEntryOrigin.java
+++ b/src/main/java/com/android/tools/r8/origin/ArchiveEntryOrigin.java
@@ -4,9 +4,12 @@
package com.android.tools.r8.origin;
+import com.android.tools.r8.Keep;
+
/**
* Origin representing an entry in an archive.
*/
+@Keep
public class ArchiveEntryOrigin extends Origin {
final String entryName;
diff --git a/src/main/java/com/android/tools/r8/origin/Origin.java b/src/main/java/com/android/tools/r8/origin/Origin.java
index c0bc2ec..3a8aa97 100644
--- a/src/main/java/com/android/tools/r8/origin/Origin.java
+++ b/src/main/java/com/android/tools/r8/origin/Origin.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.origin;
+import com.android.tools.r8.KeepForSubclassing;
import java.util.ArrayList;
import java.util.List;
@@ -22,6 +23,7 @@
* Origin.unknown()}. The former is the parent of any file path, while the latter is an unknown
* origin (e.g., for generated resources of raw bytes).
*/
+@KeepForSubclassing
public abstract class Origin implements Comparable<Origin> {
private static final Origin ROOT =
diff --git a/src/main/java/com/android/tools/r8/origin/PathOrigin.java b/src/main/java/com/android/tools/r8/origin/PathOrigin.java
index 95ee46f..0ff8080 100644
--- a/src/main/java/com/android/tools/r8/origin/PathOrigin.java
+++ b/src/main/java/com/android/tools/r8/origin/PathOrigin.java
@@ -4,11 +4,13 @@
package com.android.tools.r8.origin;
+import com.android.tools.r8.Keep;
import java.nio.file.Path;
/**
* Path component in an origin description.
*/
+@Keep
public class PathOrigin extends Origin {
private final Path path;
diff --git a/src/main/java/com/android/tools/r8/position/BinaryPosition.java b/src/main/java/com/android/tools/r8/position/BinaryPosition.java
deleted file mode 100644
index 5eb2da2..0000000
--- a/src/main/java/com/android/tools/r8/position/BinaryPosition.java
+++ /dev/null
@@ -1,49 +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.position;
-
-public class BinaryPosition implements Position {
-
- /**
- * Byte offset from start of the resource.
- */
- private final long offset;
-
- public BinaryPosition(long offset) {
- assert offset >= 0;
- this.offset = offset;
- }
-
- public long getOffset() {
- return offset;
- }
-
- @Override
- public String toString() {
- return "Index : " + offset;
- }
-
- @Override
- public int hashCode() {
- return Long.hashCode(offset);
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == this) {
- return true;
- }
- if (o != null && o.getClass().equals(BinaryPosition.class)) {
- BinaryPosition other = (BinaryPosition) o;
- return offset == other.offset;
- }
- return false;
- }
-
- @Override
- public String getDescription() {
- return "Offset " + offset;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/position/Position.java b/src/main/java/com/android/tools/r8/position/Position.java
index 902fad5..618fd51 100644
--- a/src/main/java/com/android/tools/r8/position/Position.java
+++ b/src/main/java/com/android/tools/r8/position/Position.java
@@ -4,10 +4,13 @@
package com.android.tools.r8.position;
+import com.android.tools.r8.Keep;
+
/**
* Represent a position in a resource, it can for example be line in a text file of the byte offset
* in a binary stream.
*/
+@Keep
public interface Position {
/**
diff --git a/src/main/java/com/android/tools/r8/position/TextPosition.java b/src/main/java/com/android/tools/r8/position/TextPosition.java
index 9136fa7..0dd4090 100644
--- a/src/main/java/com/android/tools/r8/position/TextPosition.java
+++ b/src/main/java/com/android/tools/r8/position/TextPosition.java
@@ -3,10 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.position;
+import com.android.tools.r8.Keep;
+
/**
* A {@link Position} in a text file determined by line and column.
* Line and column numbers start at 1.
*/
+@Keep
public class TextPosition implements Position {
/**
diff --git a/src/main/java/com/android/tools/r8/position/TextRange.java b/src/main/java/com/android/tools/r8/position/TextRange.java
index 5f35aad..0e642d8 100644
--- a/src/main/java/com/android/tools/r8/position/TextRange.java
+++ b/src/main/java/com/android/tools/r8/position/TextRange.java
@@ -4,6 +4,9 @@
package com.android.tools.r8.position;
+import com.android.tools.r8.Keep;
+
+@Keep
public class TextRange implements Position {
private final TextPosition start;
private final TextPosition end;
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 19fe18e..085d9b3 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -785,6 +785,9 @@
// Mark the type live here, so that the class exists at runtime. Note that this also marks all
// supertypes as live, so even if the field is actually on a supertype, its class will be live.
markTypeAsLive(field.clazz);
+ if (field.type.isClassType()) {
+ markTypeAsLive(field.type);
+ }
// Find the actual field.
DexEncodedField encodedField = appInfo.resolveFieldOn(field.clazz, field);
if (encodedField == null) {
@@ -814,6 +817,9 @@
private void markInstanceFieldAsLive(DexEncodedField field, KeepReason reason) {
assert field != null;
markTypeAsLive(field.field.clazz);
+ if (field.field.type.isClassType()) {
+ markTypeAsLive(field.field.type);
+ }
if (Log.ENABLED) {
Log.verbose(getClass(), "Adding instance field `%s` to live set.", field.field);
}
diff --git a/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java b/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
index 873013e..8e84935 100644
--- a/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
+++ b/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.utils;
import com.android.tools.r8.ArchiveClassFileProvider;
+import com.android.tools.r8.Keep;
import com.android.tools.r8.dexsplitter.DexSplitter;
import com.android.tools.r8.dexsplitter.DexSplitter.FeatureJar;
import com.android.tools.r8.origin.PathOrigin;
@@ -38,6 +39,7 @@
* <p>Note that this format does not allow specifying inter-module dependencies, this is simply a
* placement tool.
*/
+@Keep
public class FeatureClassMapping {
HashMap<String, String> parsedRules = new HashMap<>(); // Already parsed rules.
@@ -198,6 +200,7 @@
"Invalid mappings specification: " + error + "\n in file " + mappingFile + ":" + line);
}
+ @Keep
public static class FeatureMappingException extends Exception {
FeatureMappingException(String message) {
super(message);
diff --git a/src/main/java/com/android/tools/r8/utils/FlagFile.java b/src/main/java/com/android/tools/r8/utils/FlagFile.java
index 4791217..5f5003f 100644
--- a/src/main/java/com/android/tools/r8/utils/FlagFile.java
+++ b/src/main/java/com/android/tools/r8/utils/FlagFile.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.utils;
+import com.android.tools.r8.BaseCommand;
import com.android.tools.r8.origin.Origin;
import java.io.IOException;
import java.nio.file.Files;
@@ -28,7 +29,7 @@
}
}
- public static String[] expandFlagFiles(String[] args, Reporter reporter) {
+ public static String[] expandFlagFiles(String[] args, BaseCommand.Builder builder) {
List<String> flags = new ArrayList<>(args.length);
for (String arg : args) {
if (arg.startsWith("@")) {
@@ -37,7 +38,7 @@
flags.addAll(Files.readAllLines(flagFilePath));
} catch (IOException e) {
Origin origin = new FlagFileOrigin(flagFilePath);
- reporter.error(new ExceptionDiagnostic(e, origin));
+ builder.error(new ExceptionDiagnostic(e, origin));
}
} else {
flags.add(arg);
diff --git a/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java b/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java
index b7cd855..06ab8a9 100644
--- a/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java
@@ -4,9 +4,11 @@
package com.android.tools.r8.utils;
import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.Keep;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.Position;
+@Keep
public class StringDiagnostic implements Diagnostic {
private final Origin origin;
diff --git a/src/main/keep.txt b/src/main/keep.txt
new file mode 100644
index 0000000..2d74e47
--- /dev/null
+++ b/src/main/keep.txt
@@ -0,0 +1,6 @@
+# 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 @com.android.tools.r8.Keep class * { public *; }
+-keep @com.android.tools.r8.KeepForSubclassing class * { public *; protected *; }
diff --git a/src/test/examples/catchhandleroverlap/CatchHandlerOverlap.java b/src/test/examples/catchhandleroverlap/CatchHandlerOverlap.java
new file mode 100644
index 0000000..7a4950c
--- /dev/null
+++ b/src/test/examples/catchhandleroverlap/CatchHandlerOverlap.java
@@ -0,0 +1,33 @@
+// 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 catchhandleroverlap;
+
+public class CatchHandlerOverlap {
+ private static void f() throws Exception {
+ throw new Exception("f");
+ }
+
+ private static void g() throws Exception {
+ throw new Exception("g");
+ }
+
+ private static void h(int i1, int i2, int i3, int i4, int i5, int i6, int i7, int i8, int i9,
+ int i10, int i11, int i12, int i13, int i14, int i15, int i16, int i17) {
+ System.out.println(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 + i10 + i11 +
+ i12 + i13 + i14 + i15 + i16 + i17);
+ try {
+ f();
+ } catch (Exception e0) {
+ try {
+ g();
+ } catch (Exception e1) {
+ System.out.println(e0.getMessage() + " " + e1.getMessage());
+ }
+ }
+ }
+
+ public static void main(String[] args) {
+ h(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17);
+ }
+}
diff --git a/src/test/examples/multidex005/ref-list-4-r8.txt b/src/test/examples/multidex005/ref-list-4-r8.txt
new file mode 100644
index 0000000..ea7b0f7
--- /dev/null
+++ b/src/test/examples/multidex005/ref-list-4-r8.txt
@@ -0,0 +1,8 @@
+Lmultidex005/DirectlyReferenced;
+Lmultidex005/FieldReference;
+Lmultidex005/Interface1;
+Lmultidex005/Interface2;
+Lmultidex005/Interface3;
+Lmultidex005/SuperClass;
+Lmultidex005/SuperInterface;
+Lmultidex005/SuperSuperClass;
\ No newline at end of file
diff --git a/src/test/examples/multidex005/ref-list-4.txt b/src/test/examples/multidex005/ref-list-4.txt
index ea7b0f7..7733fba 100644
--- a/src/test/examples/multidex005/ref-list-4.txt
+++ b/src/test/examples/multidex005/ref-list-4.txt
@@ -1,5 +1,8 @@
Lmultidex005/DirectlyReferenced;
Lmultidex005/FieldReference;
+Lmultidex005/IndirectlyReferenced;
+Lmultidex005/IndirectlyReferencedInterface;
+Lmultidex005/IndirectlyReferencedSuperClass;
Lmultidex005/Interface1;
Lmultidex005/Interface2;
Lmultidex005/Interface3;
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index 2ac50a6..d8b886c 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -32,6 +32,7 @@
"arrayaccess.ArrayAccess",
"barray.BArray",
"bridge.BridgeMethod",
+ "catchhandleroverlap.CatchHandlerOverlap",
"cse.CommonSubexpressionElimination",
"constants.Constants",
"controlflow.ControlFlow",
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 45ec280..68d87c1 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -86,6 +86,7 @@
public static final String DEFAULT_PROGUARD_MAP_FILE = "proguard.map";
public static final String JAVA_8_RUNTIME = "third_party/openjdk/openjdk-rt-1.8/rt.jar";
+ public static final String KT_RUNTIME = "third_party/kotlin/kotlinc/lib/kotlin-runtime.jar";
private static final String ANDROID_JAR_PATTERN = "third_party/android_jar/lib-v%d/android.jar";
private static final AndroidApiLevel DEFAULT_MIN_SDK = AndroidApiLevel.I;
@@ -539,9 +540,16 @@
String jar = String.format(
ANDROID_JAR_PATTERN,
(apiLevel == AndroidApiLevel.getDefault() ? DEFAULT_MIN_SDK : apiLevel).getLevel());
- assert Files.exists(Paths.get(jar))
+ Path path = Paths.get(jar);
+ assert Files.exists(path)
: "Expected android jar to exist for API level " + apiLevel;
- return Paths.get(jar);
+ return path;
+ }
+
+ public static Path getKotlinRuntimeJar() {
+ Path path = Paths.get(KT_RUNTIME);
+ assert Files.exists(path) : "Expected kotlin runtime jar";
+ return path;
}
public static Path getJdwpTestsCfJarPath(AndroidApiLevel minSdk) {
diff --git a/src/test/java/com/android/tools/r8/graph/DexTypeTest.java b/src/test/java/com/android/tools/r8/graph/DexTypeTest.java
new file mode 100644
index 0000000..b32284a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/DexTypeTest.java
@@ -0,0 +1,161 @@
+// 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.graph;
+
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
+import java.util.Set;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class DexTypeTest {
+
+ private static DexItemFactory factory;
+ private static AppInfoWithSubtyping appInfo;
+
+ @BeforeClass
+ public static void makeAppInfo() throws Exception {
+ InternalOptions options = new InternalOptions();
+ DexApplication application =
+ new ApplicationReader(
+ AndroidApp.builder()
+ .addLibraryFiles(ToolHelper.getDefaultAndroidJar())
+ .addLibraryFiles(ToolHelper.getKotlinRuntimeJar())
+ .build(),
+ options,
+ new Timing(DexType.class.getName()))
+ .read()
+ .toDirect();
+ factory = options.itemFactory;
+ appInfo = new AppInfoWithSubtyping(application);
+ }
+
+ @Test
+ public void implementedInterfaces_collections_java() {
+ DexType serializable = factory.createType("Ljava/io/Serializable;");
+ DexType iterable = factory.createType("Ljava/lang/Iterable;");
+ // interface Collection extends Iterable
+ DexType collection = factory.createType("Ljava/util/Collection;");
+ // interface List extends Collection
+ DexType list = factory.createType("Ljava/util/List;");
+ // interface Queue extends Collection
+ DexType queue = factory.createType("Ljava/util/Queue;");
+ // interface Deque extends Queue
+ DexType deque = factory.createType("Ljava/util/Deque;");
+
+ // class ArrayList implements List
+ DexType arrayList = factory.createType("Ljava/util/ArrayList;");
+ Set<DexType> interfaces = arrayList.implementedInterfaces(appInfo);
+ assertThat(interfaces, hasItems(serializable));
+ assertThat(interfaces, hasItems(iterable));
+ assertThat(interfaces, hasItems(collection));
+ assertThat(interfaces, hasItems(list));
+ assertThat(interfaces, not(hasItems(queue)));
+
+ // class LinkedList implements List, Deque
+ DexType linkedList = factory.createType("Ljava/util/LinkedList;");
+ interfaces = linkedList.implementedInterfaces(appInfo);
+ assertThat(interfaces, hasItems(serializable));
+ assertThat(interfaces, hasItems(iterable));
+ assertThat(interfaces, hasItems(collection));
+ assertThat(interfaces, hasItems(list));
+ assertThat(interfaces, hasItems(deque));
+ assertThat(interfaces, hasItems(queue));
+ }
+
+ @Test
+ public void implementedInterfaces_collections_kotlin() {
+ DexType iterable = factory.createType("Ljava/lang/Iterable;");
+ // interface Collection extends Iterable
+ DexType collection = factory.createType("Ljava/util/Collection;");
+ // interface List extends Collection
+ DexType list = factory.createType("Ljava/util/List;");
+ // interface Set extends Collection
+ DexType set = factory.createType("Ljava/util/Set;");
+
+ DexType ktAbsList = factory.createType("Lkotlin/collections/AbstractList;");
+ DexType ktAbsSet = factory.createType("Lkotlin/collections/AbstractSet;");
+
+ Set<DexType> interfaces = ktAbsList.implementedInterfaces(appInfo);
+ assertThat(interfaces, hasItems(iterable));
+ assertThat(interfaces, hasItems(collection));
+ assertThat(interfaces, hasItems(list));
+ assertThat(interfaces, not(hasItems(set)));
+
+ interfaces = ktAbsSet.implementedInterfaces(appInfo);
+ assertThat(interfaces, hasItems(iterable));
+ assertThat(interfaces, hasItems(collection));
+ assertThat(interfaces, hasItems(set));
+ assertThat(interfaces, not(hasItems(list)));
+ }
+
+ @Test
+ public void implementedInterfaces_reflect_java() {
+ DexType serializable = factory.createType("Ljava/io/Serializable;");
+ DexType annotatedElement = factory.createType("Ljava/lang/reflect/AnnotatedElement;");
+ // interface GenericDeclaration extends AnnotatedElement
+ DexType genericDeclaration = factory.createType("Ljava/lang/reflect/GenericDeclaration;");
+ DexType type = factory.createType("Ljava/lang/reflect/Type;");
+ DexType pType = factory.createType("Ljava/lang/reflect/ParameterizedType;");
+ DexType klass = factory.createType("Ljava/lang/Class;");
+
+ Set<DexType> interfaces = klass.implementedInterfaces(appInfo);
+ assertThat(interfaces, hasItems(serializable));
+ assertThat(interfaces, hasItems(annotatedElement));
+ assertThat(interfaces, hasItems(genericDeclaration));
+ assertThat(interfaces, hasItems(type));
+ assertThat(interfaces, not(hasItems(pType)));
+ }
+
+ @Test
+ public void implementedInterfaces_reflect_kotlin() {
+ DexType kCallable = factory.createType("Lkotlin/reflect/KCallable;");
+ // interface KProperty : KFunction
+ DexType kFunction = factory.createType("Lkotlin/reflect/KFunction;");
+ // interface KProperty : KCallable
+ DexType kProperty = factory.createType("Lkotlin/reflect/KProperty;");
+ // interface KMutableProperty : KProperty
+ DexType kMutableProperty = factory.createType("Lkotlin/reflect/KMutableProperty;");
+ // interface KMutableProperty0 : KProperty, KMutableProperty
+ DexType kMutableProperty0 = factory.createType("Lkotlin/reflect/KMutableProperty0;");
+ // class MutablePropertyReference0 : KMutableProperty0
+ DexType mutableReference0 =
+ factory.createType("Lkotlin/jvm/internal/MutablePropertyReference0;");
+
+ Set<DexType> interfaces = mutableReference0.implementedInterfaces(appInfo);
+ assertThat(interfaces, hasItems(kCallable));
+ assertThat(interfaces, hasItems(kProperty));
+ assertThat(interfaces, hasItems(kMutableProperty));
+ assertThat(interfaces, hasItems(kMutableProperty0));
+ assertThat(interfaces, not(hasItems(kFunction)));
+ }
+
+ @Test
+ public void implementedInterfaces_lambda_kotlin() {
+ DexType function = factory.createType("Lkotlin/Function;");
+ DexType functionBase = factory.createType("Lkotlin/jvm/internal/FunctionBase;");
+ // class Lambda : Function, FunctionBase
+ DexType lambda = factory.createType("Lkotlin/jvm/internal/Lambda;");
+ // interface Function0 : Function
+ DexType function0 = factory.createType("Lkotlin/jvm/functions/Function0;");
+
+ Set<DexType> interfaces = lambda.implementedInterfaces(appInfo);
+ assertThat(interfaces, not(hasItems(lambda)));
+ assertThat(interfaces, hasItems(function));
+ assertThat(interfaces, hasItems(functionBase));
+
+ interfaces = function0.implementedInterfaces(appInfo);
+ assertThat(interfaces, hasItems(function0));
+ assertThat(interfaces, hasItems(function));
+ assertThat(interfaces, not(hasItems(functionBase)));
+ }
+
+}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
index 43b4c0f..374d58f 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.GenerateMainDexListCommand;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ir.desugar.LambdaRewriter;
import com.android.tools.r8.utils.AndroidApiLevel;
@@ -30,19 +31,15 @@
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.junit.Assert;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-public class MainDexTracingTest {
+public class MainDexTracingTest extends TestBase {
private static final String EXAMPLE_BUILD_DIR = ToolHelper.EXAMPLES_BUILD_DIR;
private static final String EXAMPLE_O_BUILD_DIR = ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR;
private static final String EXAMPLE_SRC_DIR = ToolHelper.EXAMPLES_DIR;
private static final String EXAMPLE_O_SRC_DIR = ToolHelper.EXAMPLES_ANDROID_O_DIR;
- @Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
-
@Test
public void traceMainDexList001_whyareyoukeeping() throws Throwable {
PrintStream stdout = System.out;
@@ -54,6 +51,7 @@
EXAMPLE_BUILD_DIR,
Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules-whyareyoukeeping.txt"),
Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-1.txt"),
+ Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-1.txt"),
AndroidApiLevel.I);
String output = new String(baos.toByteArray(), Charset.defaultCharset());
Assert.assertTrue(output.contains("is live because referenced in keep rule:"));
@@ -68,6 +66,7 @@
EXAMPLE_BUILD_DIR,
Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules.txt"),
Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-1.txt"),
+ Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-1.txt"),
AndroidApiLevel.I);
}
@@ -79,6 +78,7 @@
EXAMPLE_BUILD_DIR,
Paths.get(EXAMPLE_SRC_DIR, "multidex001", "main-dex-rules-2.txt"),
Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-2.txt"),
+ Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-2.txt"),
AndroidApiLevel.I);
}
@@ -90,6 +90,7 @@
EXAMPLE_BUILD_DIR,
Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules.txt"),
Paths.get(EXAMPLE_SRC_DIR, "multidex002", "ref-list-1.txt"),
+ Paths.get(EXAMPLE_SRC_DIR, "multidex002", "ref-list-1.txt"),
AndroidApiLevel.I);
}
@@ -101,6 +102,7 @@
EXAMPLE_BUILD_DIR,
Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules.txt"),
Paths.get(EXAMPLE_SRC_DIR, "multidex003", "ref-list-1.txt"),
+ Paths.get(EXAMPLE_SRC_DIR, "multidex003", "ref-list-1.txt"),
AndroidApiLevel.I);
}
@@ -112,6 +114,7 @@
EXAMPLE_O_BUILD_DIR,
Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules.txt"),
Paths.get(EXAMPLE_O_SRC_DIR, "multidex004", "ref-list-1.txt"),
+ Paths.get(EXAMPLE_O_SRC_DIR, "multidex004", "ref-list-1.txt"),
AndroidApiLevel.I);
}
@@ -132,7 +135,14 @@
@Test
public void traceMainDexList005_4() throws Throwable {
- doTest5(4);
+ doTest(
+ "traceMainDexList005",
+ "multidex005",
+ EXAMPLE_BUILD_DIR,
+ Paths.get(EXAMPLE_SRC_DIR, "multidex005", "main-dex-rules-4.txt"),
+ Paths.get(EXAMPLE_SRC_DIR, "multidex005", "ref-list-4-r8.txt"),
+ Paths.get(EXAMPLE_SRC_DIR, "multidex005", "ref-list-4.txt"),
+ AndroidApiLevel.I);
}
@Test
@@ -158,6 +168,7 @@
EXAMPLE_BUILD_DIR,
Paths.get(EXAMPLE_SRC_DIR, "multidex006", "main-dex-rules-1.txt"),
Paths.get(EXAMPLE_SRC_DIR, "multidex006", "ref-list-1.txt"),
+ Paths.get(EXAMPLE_SRC_DIR, "multidex006", "ref-list-1.txt"),
AndroidApiLevel.I);
}
@@ -168,6 +179,7 @@
EXAMPLE_BUILD_DIR,
Paths.get(EXAMPLE_SRC_DIR, "multidex005", "main-dex-rules-" + variant + ".txt"),
Paths.get(EXAMPLE_SRC_DIR, "multidex005", "ref-list-" + variant + ".txt"),
+ Paths.get(EXAMPLE_SRC_DIR, "multidex005", "ref-list-" + variant + ".txt"),
AndroidApiLevel.I);
}
@@ -176,6 +188,7 @@
String packageName,
String buildDir,
Path mainDexRules,
+ Path expectedR8MainDexList,
Path expectedMainDexList,
AndroidApiLevel minSdk)
throws Throwable {
@@ -184,6 +197,7 @@
packageName,
buildDir,
mainDexRules,
+ expectedR8MainDexList,
expectedMainDexList,
minSdk,
(options) -> {
@@ -196,6 +210,7 @@
String packageName,
String buildDir,
Path mainDexRules,
+ Path expectedR8MainDexList,
Path expectedMainDexList,
AndroidApiLevel minSdk,
Consumer<InternalOptions> optionsConsumer)
@@ -259,17 +274,22 @@
.map(this::mainDexStringToDescriptor)
.sorted()
.collect(Collectors.toList());
- // Check that both generated lists are the same as the reference list, except for lambda
+ // Check that generated lists are the same as the reference list, except for lambda
// classes which are only produced when running R8.
+ String[] r8RefList = new String(Files.readAllBytes(
+ expectedR8MainDexList), StandardCharsets.UTF_8).split("\n");
+ for (int i = 0; i < r8RefList.length; i++) {
+ String reference = r8RefList[i].trim();
+ if (r8MainDexList.size() <= i) {
+ Assert.fail("R8 main dex list is missing '" + reference + "'");
+ }
+ checkSameMainDexEntry(reference, r8MainDexList.get(i));
+ }
String[] refList = new String(Files.readAllBytes(
expectedMainDexList), StandardCharsets.UTF_8).split("\n");
int nonLambdaOffset = 0;
for (int i = 0; i < refList.length; i++) {
String reference = refList[i].trim();
- if (r8MainDexList.size() <= i) {
- Assert.fail("R8 main dex list is missing '" + reference + "'");
- }
- checkSameMainDexEntry(reference, r8MainDexList.get(i));
// The main dex list generator does not do any lambda desugaring.
if (!isLambda(reference)) {
if (mainDexGeneratorMainDexList.size() <= i - nonLambdaOffset) {
diff --git a/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java b/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java
index 9e0429b..8f00f8d 100644
--- a/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java
@@ -21,22 +21,15 @@
import com.android.tools.r8.utils.DexInspector.ClassSubject;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(VmTestRunner.class)
public class FieldTypeTest extends TestBase {
- @Ignore("b/78788577")
- @Test
- public void test_brokenTypeHierarchy() throws Exception {
- JasminBuilder jasminBuilder = new JasminBuilder();
- // interface Itf
- ClassBuilder itf = jasminBuilder.addInterface("Itf");
- MethodSignature foo = itf.addAbstractMethod("foo", ImmutableList.of(), "V");
- // class Impl /* implements Itf */
- ClassBuilder impl = jasminBuilder.addClass("Impl");
+ private ClassBuilder addImplementor(
+ JasminBuilder jasminBuilder, String name, String superName, String... interfaces) {
+ ClassBuilder impl = jasminBuilder.addClass(name, superName, interfaces);
impl.addDefaultConstructor();
impl.addVirtualMethod("foo", ImmutableList.of(), "V",
".limit locals 2",
@@ -50,15 +43,43 @@
".limit stack 2",
"ldc \"" + impl.name + "\"",
"areturn");
+ return impl;
+ }
+
+ @Test
+ public void test_brokenTypeHierarchy() throws Exception {
+ JasminBuilder jasminBuilder = new JasminBuilder();
+ // interface Itf1
+ ClassBuilder itf1 = jasminBuilder.addInterface("Itf1");
+ MethodSignature foo1 = itf1.addAbstractMethod("foo", ImmutableList.of(), "V");
+ // class Impl1 /* implements Itf1 */
+ ClassBuilder impl1 = addImplementor(jasminBuilder, "Impl1", "java/lang/Object");
+
+ // Another interface and implementer with a correct relation.
+ ClassBuilder itf2 = jasminBuilder.addInterface("Itf2");
+ MethodSignature foo2 = itf2.addAbstractMethod("foo", ImmutableList.of(), "V");
+ ClassBuilder impl2 = addImplementor(jasminBuilder, "Impl2", "java/lang/Object", itf2.name);
+
ClassBuilder client = jasminBuilder.addClass("Client");
- FieldSignature obj = client.addStaticFinalField("obj", itf.getDescriptor(), null);
+ client.setAccess("final");
+ client.addDefaultConstructor();
+ FieldSignature a = client.addStaticFinalField("a", "Ljava/lang/Object;", null);
+ FieldSignature obj1 = client.addField("private static", "obj1", itf1.getDescriptor(), null);
+ FieldSignature obj2 = client.addStaticFinalField("obj2", itf2.getDescriptor(), null);
client.addClassInitializer(
".limit locals 1",
".limit stack 2",
- "new " + impl.name,
+ "aconst_null",
+ "putstatic " + client.name + "/" + a.name + " " + "Ljava/lang/Object;",
+ "new " + impl1.name,
"dup",
- "invokespecial " + impl.name + "/<init>()V",
- "putstatic " + client.name + "/" + obj.name + " " + itf.getDescriptor(),
+ "invokespecial " + impl1.name + "/<init>()V",
+ // Unused, i.e., not read, field, yet still remained in the output.
+ "putstatic " + client.name + "/" + obj1.name + " " + itf1.getDescriptor(),
+ "new " + impl2.name,
+ "dup",
+ "invokespecial " + impl2.name + "/<init>()V",
+ "putstatic " + client.name + "/" + obj2.name + " " + itf2.getDescriptor(),
"return"
);
@@ -67,8 +88,8 @@
".limit locals 2",
".limit stack 2",
"getstatic java/lang/System/out Ljava/io/PrintStream;",
- "getstatic " + client.name + "/" + obj.name + " " + itf.getDescriptor(),
/*
+ "getstatic " + client.name + "/" + obj1.name + " " + itf1.getDescriptor(),
"astore_0",
"aload_0",
// java.lang.IncompatibleClassChangeError:
@@ -76,19 +97,24 @@
"invokeinterface " + itf.name + "/" + foo.name + "()V 1",
"aload_0",
*/
+ "getstatic " + client.name + "/" + obj2.name + " " + itf2.getDescriptor(),
"invokevirtual java/io/PrintStream/print(Ljava/lang/Object;)V",
"return"
);
final String mainClassName = mainClass.name;
- String proguardConfig = keepMainProguardConfiguration(mainClass.name, false, false);
+ String proguardConfig =
+ keepMainProguardConfiguration(mainClass.name, false, false)
+ // AGP default is to not turn optimizations on, which disables MemberValuePropagation,
+ // resulting in the problematic putstatic being remained.
+ + "-dontoptimize\n";
// Run input program on java.
Path outputDirectory = temp.newFolder().toPath();
jasminBuilder.writeClassFiles(outputDirectory);
ProcessResult javaResult = ToolHelper.runJava(outputDirectory, mainClassName);
assertEquals(0, javaResult.exitCode);
- assertThat(javaResult.stdout, containsString(impl.name));
+ assertThat(javaResult.stdout, containsString(impl2.name));
AndroidApp processedApp = compileWithR8(jasminBuilder.build(), proguardConfig,
// Disable inlining to avoid the (short) tested method from being inlined and then removed.
@@ -97,12 +123,12 @@
// Run processed (output) program on ART
ProcessResult artResult = runOnArtRaw(processedApp, mainClassName);
assertEquals(0, artResult.exitCode);
- assertThat(artResult.stdout, containsString(impl.name));
+ assertThat(artResult.stdout, containsString(impl2.name));
assertEquals(-1, artResult.stderr.indexOf("DoFieldPut"));
DexInspector inspector = new DexInspector(processedApp);
- ClassSubject itfSubject = inspector.clazz(itf.name);
- assertThat(itfSubject, isPresent());
+ ClassSubject itf1Subject = inspector.clazz(itf1.name);
+ assertThat(itf1Subject, isPresent());
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
index d578815..cae3b06 100644
--- a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
@@ -94,12 +94,13 @@
Result result = runTest(mainClass, proguardConfig);
- // Without includedescriptorclasses return type argument type and field type are removed.
+ // Without includedescriptorclasses return type and argument type are removed.
result.assertKept(ClassWithNativeMethods.class);
result.assertRemoved(NativeArgumentType.class);
result.assertRemoved(NativeReturnType.class);
- result.assertRemoved(InstanceFieldType.class);
- result.assertRemoved(StaticFieldType.class);
+ // Field type is not removed due to the concern about the broken type hierarchy.
+ result.assertRenamed(InstanceFieldType.class);
+ result.assertRenamed(StaticFieldType.class);
}
}
diff --git a/tests/d8_api_usage_sample.jar b/tests/d8_api_usage_sample.jar
index f901b9d..7d503a5 100644
--- a/tests/d8_api_usage_sample.jar
+++ 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
index f901b9d..7d503a5 100644
--- a/tests/r8_api_usage_sample.jar
+++ b/tests/r8_api_usage_sample.jar
Binary files differ
diff --git a/tools/build_r8lib.py b/tools/build_r8lib.py
new file mode 100755
index 0000000..abaa619
--- /dev/null
+++ b/tools/build_r8lib.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+# 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.
+
+'''
+Build r8lib.jar using src/main/keep.txt and test that d8_api_usage_sample.jar
+works with the minified R8.
+'''
+
+import argparse
+import os
+import subprocess
+import toolhelper
+import utils
+
+parser = argparse.ArgumentParser(description=__doc__.strip(),
+ formatter_class=argparse.RawTextHelpFormatter)
+
+SAMPLE_JAR = os.path.join(utils.REPO_ROOT, 'tests/d8_api_usage_sample.jar')
+KEEP_RULES = os.path.join(utils.REPO_ROOT, 'src/main/keep.txt')
+R8LIB_JAR = os.path.join(utils.LIBS, 'r8lib.jar')
+R8LIB_MAP_FILE = os.path.join(utils.LIBS, 'r8lib-map.txt')
+
+API_LEVEL = 26
+ANDROID_JAR = 'third_party/android_jar/lib-v%s/android.jar' % API_LEVEL
+
+
+def build_r8lib():
+ toolhelper.run(
+ 'r8',
+ ('--release',
+ '--classfile',
+ '--lib', utils.RT_JAR,
+ utils.R8_JAR,
+ '--output', R8LIB_JAR,
+ '--pg-conf', KEEP_RULES,
+ '--pg-map-output', R8LIB_MAP_FILE))
+
+
+def test_d8sample():
+ with utils.TempDir() as path:
+ args = ['java', '-cp', '%s:%s' % (SAMPLE_JAR, R8LIB_JAR),
+ 'com.android.tools.apiusagesample.D8ApiUsageSample',
+ '--output', path,
+ '--min-api', str(API_LEVEL),
+ '--lib', ANDROID_JAR,
+ '--classpath', utils.R8_JAR,
+ '--main-dex-list', '/dev/null',
+ os.path.join(utils.BUILD, 'test/examples/hello.jar')]
+ utils.PrintCmd(args)
+ subprocess.check_call(args)
+
+
+def test_r8command():
+ with utils.TempDir() as path:
+ # SAMPLE_JAR and R8LIB_JAR should not have any classes in common, since e.g.
+ # R8CommandParser should have been minified in R8LIB_JAR.
+ # Just in case R8CommandParser is also present in R8LIB_JAR, we put
+ # SAMPLE_JAR first on the classpath to use its version of R8CommandParser.
+ args = ['java', '-cp', '%s:%s' % (SAMPLE_JAR, R8LIB_JAR),
+ 'com.android.tools.r8.R8CommandParser',
+ '--output', path + "/output.zip",
+ '--min-api', str(API_LEVEL),
+ '--lib', ANDROID_JAR,
+ '--main-dex-list', '/dev/null',
+ os.path.join(utils.BUILD, 'test/examples/hello.jar')]
+ utils.PrintCmd(args)
+ subprocess.check_call(args)
+
+
+def test_r8cfcommand():
+ with utils.TempDir() as path:
+ # SAMPLE_JAR and R8LIB_JAR should not have any classes in common, since e.g.
+ # R8CommandParser should have been minified in R8LIB_JAR.
+ # Just in case R8CommandParser is also present in R8LIB_JAR, we put
+ # SAMPLE_JAR first on the classpath to use its version of R8CommandParser.
+ args = ['java', '-cp', '%s:%s' % (SAMPLE_JAR, R8LIB_JAR),
+ 'com.android.tools.r8.R8CommandParser',
+ '--classfile',
+ '--output', path + "/output.jar",
+ '--lib', utils.RT_JAR,
+ os.path.join(utils.BUILD, 'test/examples/hello.jar')]
+ utils.PrintCmd(args)
+ subprocess.check_call(args)
+
+
+def main():
+ # Handle --help
+ parser.parse_args()
+
+ build_r8lib()
+ test_d8sample()
+ test_r8command()
+ test_r8cfcommand()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/minify_tool.py b/tools/minify_tool.py
index 787c1e1..8f440dd 100755
--- a/tools/minify_tool.py
+++ b/tools/minify_tool.py
@@ -27,7 +27,6 @@
MANIFEST_PATH = 'META-INF/MANIFEST.MF'
MANIFEST = 'Manifest-Version: 1.0\nMain-Class: %s\n\n'
MANIFEST_PATTERN = r'Main-Class:\s*(\S+)'
-RT = os.path.join(utils.REPO_ROOT, 'third_party/openjdk/openjdk-rt-1.8/rt.jar')
parser = argparse.ArgumentParser(description=__doc__.strip(),
formatter_class=argparse.RawTextHelpFormatter)
@@ -38,7 +37,7 @@
'-o', '--output-jar',
help='Path to output JAR (default: build/libs/<MainClass>-min.jar)')
parser.add_argument(
- '-l', '--lib', default=RT,
+ '-l', '--lib', default=utils.RT_JAR,
help='Path to rt.jar to use instead of OpenJDK 1.8')
parser.add_argument(
'-m', '--mainclass',
@@ -85,8 +84,8 @@
'No --mainclass specified and no Main-Class in input JAR manifest.')
return mo.group(1)
-def minify_tool(mainclass=None, input_jar=utils.R8_JAR, output_jar=None, lib=RT,
- debug=True, build=True, benchmark_name=None):
+def minify_tool(mainclass=None, input_jar=utils.R8_JAR, output_jar=None,
+ lib=utils.RT_JAR, debug=True, build=True, benchmark_name=None):
if output_jar is None:
output_jar = generate_output_name(input_jar, mainclass)
with utils.TempDir() as path:
diff --git a/tools/utils.py b/tools/utils.py
index 9f6959f..3aab572 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -35,6 +35,7 @@
COMPATPROGUARD_JAR = os.path.join(LIBS, 'compatproguard.jar')
MAVEN_ZIP = os.path.join(LIBS, 'r8.zip')
GENERATED_LICENSE = os.path.join(GENERATED_LICENSE_DIR, 'LICENSE')
+RT_JAR = os.path.join(REPO_ROOT, 'third_party/openjdk/openjdk-rt-1.8/rt.jar')
def PrintCmd(s):
if type(s) is list: