Merge "Mark obsolete when DexEncodedMethod instance became obsolete."
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index b57ff61..6ae611c 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -235,11 +235,16 @@
* @param outputMode Mode in which to write the output.
*/
public B setOutput(Path outputPath, OutputMode outputMode) {
+ return setOutput(outputPath, outputMode, false);
+ }
+
+ // This is only public in R8Command.
+ protected B setOutput(Path outputPath, OutputMode outputMode, boolean includeDataResources) {
assert outputPath != null;
assert outputMode != null;
this.outputPath = outputPath;
this.outputMode = outputMode;
- programConsumer = createProgramOutputConsumer(outputPath, outputMode, false);
+ programConsumer = createProgramOutputConsumer(outputPath, outputMode, includeDataResources);
return self();
}
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 5195a82..c53f367 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -239,6 +239,47 @@
return self();
}
+ /**
+ * Set the output path-and-mode.
+ *
+ * <p>Setting the output path-and-mode will override any previous set consumer or any previous
+ * output path-and-mode, and implicitly sets the appropriate program consumer to write the
+ * output.
+ *
+ * <p>By default data resources from the input will be included in the output. (see {@link
+ * #setOutput(Path, OutputMode, boolean) for details}
+ *
+ * @param outputPath Path to write the output to. Must be an archive or and existing directory.
+ * @param outputMode Mode in which to write the output.
+ */
+ @Override
+ public Builder setOutput(Path outputPath, OutputMode outputMode) {
+ setOutput(outputPath, outputMode, true);
+ return self();
+ }
+
+ /**
+ * Set the output path-and-mode and control if data resources are included.
+ *
+ * <p>In addition to setting the output path-and-mode (see {@link #setOutput(Path, OutputMode)})
+ * this can control if data resources should be included or not.
+ *
+ * <p>Data resources are non Java classfile items in the input.
+ *
+ * <p>If data resources are not included they are ignored in the input and will not produce
+ * anything in the output. If data resources are included they are processed according to the
+ * configuration and written to the output.
+ *
+ * @param outputPath Path to write the output to. Must be an archive or and existing directory.
+ * @param outputMode Mode in which to write the output.
+ * @param includeDataResources If data resources from the input should be included in the
+ * output.
+ */
+ @Override
+ public Builder setOutput(Path outputPath, OutputMode outputMode, boolean includeDataResources) {
+ return super.setOutput(outputPath, outputMode, includeDataResources);
+ }
+
@Override
public Builder addProgramResourceProvider(ProgramResourceProvider programProvider) {
return super.addProgramResourceProvider(
@@ -250,7 +291,7 @@
Path path,
OutputMode mode,
boolean consumeDataResources) {
- return super.createProgramOutputConsumer(path, mode, false);
+ return super.createProgramOutputConsumer(path, mode, consumeDataResources);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/R8CommandParser.java b/src/main/java/com/android/tools/r8/R8CommandParser.java
index 3d33037..580b068 100644
--- a/src/main/java/com/android/tools/r8/R8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/R8CommandParser.java
@@ -27,6 +27,7 @@
OutputMode outputMode = null;
Path outputPath = null;
boolean hasDefinedApiLevel = false;
+ private boolean includeDataResources = true;
}
static final String USAGE_MESSAGE =
@@ -49,6 +50,7 @@
" --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-data-resources # Ignore all data resources.",
" --no-desugaring # Force disable desugaring.",
" --main-dex-rules <file> # Proguard keep rules for classes to place in the",
" # primary dex file.",
@@ -91,7 +93,7 @@
}
Path outputPath = state.outputPath != null ? state.outputPath : Paths.get(".");
OutputMode outputMode = state.outputMode != null ? state.outputMode : OutputMode.DexIndexed;
- builder.setOutput(outputPath, outputMode);
+ builder.setOutput(outputPath, outputMode, state.includeDataResources);
return builder;
}
@@ -175,6 +177,8 @@
builder.addProguardConfigurationFiles(Paths.get(expandedArgs[++i]));
} else if (arg.equals("--pg-map-output")) {
builder.setProguardMapOutputPath(Paths.get(expandedArgs[++i]));
+ } else if (arg.equals("--no-data-resources")) {
+ state.includeDataResources = false;
} else {
if (arg.startsWith("--")) {
builder.error(new StringDiagnostic("Unknown option: " + arg, argsOrigin));
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 dad3120..ec15120 100644
--- a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
+++ b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
@@ -34,6 +34,7 @@
public final String output;
public final int minApi;
public final boolean forceProguardCompatibility;
+ public final boolean includeDataResources;
public final boolean multiDex;
public final String mainDexList;
public final List<String> proguardConfig;
@@ -48,12 +49,14 @@
int minApi,
boolean multiDex,
boolean forceProguardCompatibility,
+ boolean includeDataResources,
String mainDexList,
boolean printHelpAndExit,
boolean verticalClassMerging) {
this.output = output;
this.minApi = minApi;
this.forceProguardCompatibility = forceProguardCompatibility;
+ this.includeDataResources = includeDataResources;
this.multiDex = multiDex;
this.mainDexList = mainDexList;
this.proguardConfig = proguardConfig;
@@ -65,6 +68,7 @@
String output = null;
int minApi = 1;
boolean forceProguardCompatibility = false;
+ boolean includeDataResources = true;
boolean multiDex = false;
String mainDexList = null;
boolean printHelpAndExit = false;
@@ -87,6 +91,8 @@
minApi = Integer.valueOf(args[++i]);
} else if (arg.equals("--force-proguard-compatibility")) {
forceProguardCompatibility = true;
+ } else if (arg.equals("--no-data-resources")) {
+ includeDataResources = false;
} else if (arg.equals("--output")) {
output = args[++i];
} else if (arg.equals("--multi-dex")) {
@@ -129,6 +135,7 @@
minApi,
multiDex,
forceProguardCompatibility,
+ includeDataResources,
mainDexList,
printHelpAndExit,
verticalClassMerging);
@@ -142,6 +149,8 @@
System.out.println("--multi-dex : ignored (provided for compatibility)");
System.out.println("--no-locals : ignored (provided for compatibility)");
System.out.println("--core-library : ignored (provided for compatibility)");
+ System.out.println("--force-proguard-compatibility : Proguard compatibility mode");
+ System.out.println("--no-data-resources : ignore all data resources");
}
}
@@ -170,7 +179,7 @@
new CompatProguardCommandBuilder(
options.forceProguardCompatibility, options.enableVerticalClassMerging);
builder
- .setOutput(Paths.get(options.output), OutputMode.DexIndexed)
+ .setOutput(Paths.get(options.output), OutputMode.DexIndexed, options.includeDataResources)
.addProguardConfiguration(options.proguardConfig, CommandLineOrigin.INSTANCE)
.setMinApiLevel(options.minApi);
if (options.mainDexList != null) {
diff --git a/src/main/java/com/android/tools/r8/naming/NamingState.java b/src/main/java/com/android/tools/r8/naming/NamingState.java
index fc00d91..6b64017 100644
--- a/src/main/java/com/android/tools/r8/naming/NamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/NamingState.java
@@ -22,7 +22,7 @@
class NamingState<ProtoType extends CachedHashValueDexItem, KeyType> {
private final NamingState<ProtoType, KeyType> parent;
- private final Map<KeyType, InternalState<ProtoType>> usedNames = new HashMap<>();
+ private final Map<KeyType, InternalState> usedNames = new HashMap<>();
private final DexItemFactory itemFactory;
private final ImmutableList<String> dictionary;
private final Function<ProtoType, KeyType> keyTransform;
@@ -53,25 +53,25 @@
return new NamingState<>(this, itemFactory, dictionary, keyTransform, useUniqueMemberNames);
}
- private InternalState<ProtoType> findInternalStateFor(ProtoType proto) {
+ private InternalState findInternalStateFor(ProtoType proto) {
KeyType key = keyTransform.apply(proto);
- InternalState<ProtoType> result = usedNames.get(key);
+ InternalState result = usedNames.get(key);
if (result == null && parent != null) {
result = parent.findInternalStateFor(proto);
}
return result;
}
- private InternalState<ProtoType> getOrCreateInternalStateFor(ProtoType proto) {
+ private InternalState getOrCreateInternalStateFor(ProtoType proto) {
// TODO(herhut): Maybe allocate these sparsely and search via state chain.
KeyType key = keyTransform.apply(proto);
- InternalState<ProtoType> result = usedNames.get(key);
+ InternalState result = usedNames.get(key);
if (result == null) {
if (parent != null) {
- InternalState<ProtoType> parentState = parent.getOrCreateInternalStateFor(proto);
+ InternalState parentState = parent.getOrCreateInternalStateFor(proto);
result = parentState.createChild();
} else {
- result = new InternalState<>(itemFactory, null, dictionary);
+ result = new InternalState(itemFactory, null, dictionary);
}
usedNames.put(key, result);
}
@@ -79,7 +79,7 @@
}
private DexString getAssignedNameFor(DexString name, ProtoType proto) {
- InternalState<ProtoType> state = findInternalStateFor(proto);
+ InternalState state = findInternalStateFor(proto);
if (state == null) {
return null;
}
@@ -89,19 +89,19 @@
public DexString assignNewNameFor(DexString original, ProtoType proto, boolean markAsUsed) {
DexString result = getAssignedNameFor(original, proto);
if (result == null) {
- InternalState<ProtoType> state = getOrCreateInternalStateFor(proto);
+ InternalState state = getOrCreateInternalStateFor(proto);
result = state.getNameFor(original, proto, markAsUsed);
}
return result;
}
public void reserveName(DexString name, ProtoType proto) {
- InternalState<ProtoType> state = getOrCreateInternalStateFor(proto);
+ InternalState state = getOrCreateInternalStateFor(proto);
state.reserveName(name);
}
public boolean isReserved(DexString name, ProtoType proto) {
- InternalState<ProtoType> state = findInternalStateFor(proto);
+ InternalState state = findInternalStateFor(proto);
if (state == null) {
return false;
}
@@ -109,7 +109,7 @@
}
public boolean isAvailable(DexString original, ProtoType proto, DexString candidate) {
- InternalState<ProtoType> state = findInternalStateFor(proto);
+ InternalState state = findInternalStateFor(proto);
if (state == null) {
return true;
}
@@ -118,25 +118,25 @@
}
public void addRenaming(DexString original, ProtoType proto, DexString newName) {
- InternalState<ProtoType> state = getOrCreateInternalStateFor(proto);
+ InternalState state = getOrCreateInternalStateFor(proto);
state.addRenaming(original, proto, newName);
}
- private class InternalState<InternalProtoType extends CachedHashValueDexItem> {
+ private class InternalState {
private static final int INITIAL_NAME_COUNT = 1;
private final char[] EMPTY_CHAR_ARRARY = new char[0];
protected final DexItemFactory itemFactory;
- private final InternalState<InternalProtoType> parentInternalState;
+ private final InternalState parentInternalState;
private Set<DexString> reservedNames = null;
- private Table<DexString, InternalProtoType, DexString> renamings = null;
+ private Table<DexString, ProtoType, DexString> renamings = null;
private int nameCount;
private final Iterator<String> dictionaryIterator;
private InternalState(
DexItemFactory itemFactory,
- InternalState<InternalProtoType> parentInternalState,
+ InternalState parentInternalState,
Iterator<String> dictionaryIterator) {
this.itemFactory = itemFactory;
this.parentInternalState = parentInternalState;
@@ -146,9 +146,7 @@
}
private InternalState(
- DexItemFactory itemFactory,
- InternalState<InternalProtoType> parentInternalState,
- List<String> dictionary) {
+ DexItemFactory itemFactory, InternalState parentInternalState, List<String> dictionary) {
this(itemFactory, parentInternalState, dictionary.iterator());
}
@@ -163,8 +161,8 @@
&& (parentInternalState == null || parentInternalState.isAvailable(name));
}
- InternalState<InternalProtoType> createChild() {
- return new InternalState<>(itemFactory, this, dictionaryIterator);
+ InternalState createChild() {
+ return new InternalState(itemFactory, this, dictionaryIterator);
}
void reserveName(DexString name) {
@@ -174,11 +172,11 @@
reservedNames.add(name);
}
- DexString getAssignedNameFor(DexString original, InternalProtoType proto) {
+ DexString getAssignedNameFor(DexString original, ProtoType proto) {
DexString result = null;
if (renamings != null) {
if (useUniqueMemberNames) {
- Map<InternalProtoType, DexString> row = renamings.row(original);
+ Map<ProtoType, DexString> row = renamings.row(original);
if (row != null) {
// Either not renamed yet (0) or renamed (1). If renamed, return the same renamed name
// so that other members with the same name can be renamed to the same renamed name.
@@ -196,7 +194,7 @@
return result;
}
- DexString getNameFor(DexString original, InternalProtoType proto, boolean markAsUsed) {
+ DexString getNameFor(DexString original, ProtoType proto, boolean markAsUsed) {
DexString name = getAssignedNameFor(original, proto);
if (name != null) {
return name;
@@ -210,7 +208,7 @@
return name;
}
- void addRenaming(DexString original, InternalProtoType proto, DexString newName) {
+ void addRenaming(DexString original, ProtoType proto, DexString newName) {
if (renamings == null) {
renamings = HashBasedTable.create();
}
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java
index 16bbae9..0f0bc2f 100644
--- a/src/test/java/com/android/tools/r8/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.dex.Marker.Tool;
import com.android.tools.r8.origin.EmbeddedOrigin;
import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.ZipUtils;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.lang.reflect.Method;
@@ -22,10 +23,13 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@@ -463,6 +467,116 @@
assertEquals(0, new ZipFile(emptyZip.toFile(), StandardCharsets.UTF_8).size());
}
+ private Path writeZipWithDataResource(String name) throws Exception {
+ Path dataResourceZip = temp.newFolder().toPath().resolve(name);
+ try (ZipOutputStream out =
+ new ZipOutputStream(
+ Files.newOutputStream(
+ dataResourceZip,
+ StandardOpenOption.CREATE,
+ StandardOpenOption.TRUNCATE_EXISTING))) {
+ // Write a directory entry and a normal entry.
+ ZipUtils.writeToZipStream(out, "org/", new byte[] {}, ZipEntry.STORED);
+ ZipUtils.writeToZipStream(
+ out, "org/resource.txt", "Hello world!".getBytes(), ZipEntry.STORED);
+ }
+ return dataResourceZip;
+ }
+
+ @Test
+ public void defaultResourceProcessing() throws Exception {
+ Path dataResourceZip = writeZipWithDataResource("dataResource.zip");
+ Path outputZip = temp.getRoot().toPath().resolve("output.zip");
+ R8.run(
+ R8Command.builder()
+ .addProgramFiles(dataResourceZip)
+ .setOutput(outputZip, OutputMode.ClassFile)
+ .build());
+ assertTrue(Files.exists(outputZip));
+ assertEquals(2, new ZipFile(outputZip.toFile(), StandardCharsets.UTF_8).size());
+ }
+
+ public void runCustomResourceProcessing(boolean includeDataResources, int expectedZipEntries)
+ throws Exception {
+ Path dataResourceZip = writeZipWithDataResource("dataResource.zip");
+ Path outputZip = temp.newFolder().toPath().resolve("output.zip");
+ R8.run(
+ R8Command.builder()
+ .addProgramFiles(dataResourceZip)
+ .setOutput(outputZip, OutputMode.ClassFile, includeDataResources)
+ .build());
+ assertTrue(Files.exists(outputZip));
+ assertEquals(
+ expectedZipEntries, new ZipFile(outputZip.toFile(), StandardCharsets.UTF_8).size());
+ }
+
+ private Path simpleProguardConfiguration() throws Exception {
+ Path proguardConfiguration = temp.newFile("printseedsandprintusage.txt").toPath();
+ FileUtils.writeTextFile(proguardConfiguration, ImmutableList.of("-keep class A { *; }"));
+ return proguardConfiguration;
+ }
+
+ @Test
+ public void noTreeShakingOption() throws Throwable {
+ // Default "keep all" rule implies no tree shaking.
+ assertFalse(parse().getEnableTreeShaking());
+ assertFalse(parse("--no-tree-shaking").getEnableTreeShaking());
+
+ // With a Proguard configuration --no-tree-shaking takes effect.
+ String proguardConfiguration = simpleProguardConfiguration().toAbsolutePath().toString();
+ assertTrue(parse("--pg-conf", proguardConfiguration).getEnableTreeShaking());
+ assertFalse(
+ parse("--no-tree-shaking", "--pg-conf", proguardConfiguration).getEnableTreeShaking());
+ }
+
+ @Test
+ public void noMinificationOption() throws Throwable {
+ // Default "keep all" rule implies no tree minification.
+ assertFalse(parse().getEnableMinification());
+ assertFalse(parse("--no-minification").getEnableMinification());
+
+ // With a Proguard configuration --no-tree-shaking takes effect.
+ String proguardConfiguration = simpleProguardConfiguration().toAbsolutePath().toString();
+ assertTrue(parse("--pg-conf", proguardConfiguration).getEnableMinification());
+ assertFalse(
+ parse("--no-minification", "--pg-conf", proguardConfiguration).getEnableMinification());
+ }
+
+ @Test
+ public void defaultDataResourcesOption() throws Throwable {
+ Path dataResourceZip = writeZipWithDataResource("dataResource.zip");
+ Path outputZip = temp.newFolder().toPath().resolve("output.zip");
+
+ R8.run(
+ parse(
+ dataResourceZip.toAbsolutePath().toString(),
+ "--output",
+ outputZip.toAbsolutePath().toString()));
+ assertTrue(Files.exists(outputZip));
+ assertEquals(2, new ZipFile(outputZip.toFile(), StandardCharsets.UTF_8).size());
+ }
+
+ @Test
+ public void noDataResourcesOption() throws Throwable {
+ Path dataResourceZip = writeZipWithDataResource("dataResource.zip");
+ Path outputZip = temp.newFolder().toPath().resolve("output.zip");
+
+ R8.run(
+ parse(
+ "--no-data-resources",
+ dataResourceZip.toAbsolutePath().toString(),
+ "--output",
+ outputZip.toAbsolutePath().toString()));
+ assertTrue(Files.exists(outputZip));
+ assertEquals(0, new ZipFile(outputZip.toFile(), StandardCharsets.UTF_8).size());
+ }
+
+ @Test
+ public void customResourceProcessing() throws Exception {
+ runCustomResourceProcessing(true, 2);
+ runCustomResourceProcessing(false, 0);
+ }
+
private R8Command parse(String... args) throws CompilationFailedException {
return R8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
}
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index f3ada7d..22bb5e5 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.SmaliWriter;
@@ -581,13 +582,8 @@
*/
protected String runOnJava(Class mainClass) throws Exception {
ProcessResult result = ToolHelper.runJava(mainClass);
- if (result.exitCode != 0) {
- System.out.println("Std out:");
- System.out.println(result.stdout);
- System.out.println("Std err:");
- System.out.println(result.stderr);
- assertEquals(0, result.exitCode);
- }
+ ToolHelper.failOnProcessFailure(result);
+ ToolHelper.failOnVerificationErrors(result);
return result.stdout;
}
@@ -603,9 +599,17 @@
}
/** Run application on Java with the specified main class and provided arguments. */
+ protected String runOnJava(AndroidApp app, String mainClass, String... args) throws IOException {
+ return runOnJava(app, mainClass, Arrays.asList(args));
+ }
+
+ /** Run application on Java with the specified main class and provided arguments. */
protected String runOnJava(AndroidApp app, String mainClass, List<String> args)
throws IOException {
- return runOnJavaRaw(app, mainClass, args).stdout;
+ ProcessResult result = runOnJavaRaw(app, mainClass, args);
+ ToolHelper.failOnProcessFailure(result);
+ ToolHelper.failOnVerificationErrors(result);
+ return result.stdout;
}
protected ProcessResult runOnJavaRawNoVerify(String main, byte[]... classes) throws IOException {
@@ -647,6 +651,18 @@
return ToolHelper.runJavaNoVerify(out, mainClass, args.toArray(new String[0]));
}
+ /** Run application on Art or Java with the specified main class. */
+ protected String runOnVM(AndroidApp app, String mainClass, Backend backend) throws IOException {
+ switch (backend) {
+ case CF:
+ return runOnJava(app, mainClass);
+ case DEX:
+ return runOnArt(app, mainClass);
+ default:
+ throw new Unreachable("Unexpected backend: " + backend);
+ }
+ }
+
private String extractClassName(byte[] ccc) {
class ClassNameExtractor extends ClassVisitor {
private String className;
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 76edce7..e34282d 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -1296,13 +1296,13 @@
return runArtNoVerificationErrorsRaw(files, mainClass, extras, version).stdout;
}
- private static void failOnProcessFailure(ProcessResult result) {
+ protected static void failOnProcessFailure(ProcessResult result) {
if (result.exitCode != 0) {
- fail("Unexpected art failure: '" + result.stderr + "'\n" + result.stdout);
+ fail("Unexpected failure: '" + result.stderr + "'\n" + result.stdout);
}
}
- private static void failOnVerificationErrors(ProcessResult result) {
+ protected static void failOnVerificationErrors(ProcessResult result) {
if (result.stderr.contains("Verification error")) {
fail("Verification error: \n" + result.stderr);
}
diff --git a/src/test/java/com/android/tools/r8/compatproguard/CompatProguardTest.java b/src/test/java/com/android/tools/r8/compatproguard/CompatProguardTest.java
index 8da93a5..cf95846 100644
--- a/src/test/java/com/android/tools/r8/compatproguard/CompatProguardTest.java
+++ b/src/test/java/com/android/tools/r8/compatproguard/CompatProguardTest.java
@@ -5,6 +5,9 @@
package com.android.tools.r8.compatproguard;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.compatproguard.CompatProguard.CompatProguardOptions;
import org.junit.Test;
@@ -16,9 +19,20 @@
}
@Test
+ public void testDefaultDataResources() throws Exception {
+ CompatProguardOptions options = parseArgs();
+ assertNull(options.output);
+ assertEquals(1, options.minApi);
+ assertFalse(options.forceProguardCompatibility);
+ assertTrue(options.includeDataResources);
+ assertFalse(options.multiDex);
+ assertNull(options.mainDexList);
+ assertEquals(0, options.proguardConfig.size());
+ }
+
+ @Test
public void testShortLine() throws Exception {
- CompatProguardOptions options;
- options = parseArgs("-");
+ CompatProguardOptions options = parseArgs("-");
assertEquals(1, options.proguardConfig.size());
}
@@ -65,18 +79,20 @@
@Test
public void testInclude() throws Exception {
- CompatProguardOptions options;
-
- options = parseArgs("-include --my-include-file.txt");
+ CompatProguardOptions options = parseArgs("-include --my-include-file.txt");
assertEquals(1, options.proguardConfig.size());
assertEquals("-include --my-include-file.txt", options.proguardConfig.get(0));
}
@Test
public void testNoLocalsOption() throws Exception {
- CompatProguardOptions options;
-
- options = parseArgs("--no-locals");
+ CompatProguardOptions options = parseArgs("--no-locals");
assertEquals(0, options.proguardConfig.size());
}
+
+ @Test
+ public void testNoDataResources() throws Exception {
+ CompatProguardOptions options = parseArgs("--no-data-resources");
+ assertFalse(options.includeDataResources);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
index 2b73eaa..3674108 100644
--- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
@@ -7,6 +7,7 @@
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.AsmTestBase;
+import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.VmTestRunner;
@@ -20,7 +21,6 @@
import com.android.tools.r8.desugaring.interfacemethods.default2.TestMainDefault2;
import com.android.tools.r8.desugaring.interfacemethods.static0.TestMainStatic0;
import com.android.tools.r8.desugaring.interfacemethods.static1.TestMainStatic1;
-import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.utils.AndroidApiLevel;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@@ -139,7 +139,7 @@
ToolHelper.getClassAsBytes(TestMainDefault0.class));
}
- @Test(expected = CompilationError.class)
+ @Test(expected = CompilationFailedException.class)
@IgnoreForRangeOfVmVersions(from = Version.V7_0_0, to = Version.DEFAULT) // No desugaring
public void testInvokeDefault1() throws Exception {
ensureSameOutput(
diff --git a/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeTest.java b/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeTest.java
new file mode 100644
index 0000000..93f6215
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeTest.java
@@ -0,0 +1,113 @@
+// 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.naming;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.StringConsumer.FileConsumer;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.jasmin.JasminBuilder;
+import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.io.File;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class CovariantReturnTypeTest extends TestBase {
+
+ @Ignore("b/115613850")
+ @Test
+ public void test() throws Exception {
+ for (int i = 0; i < 3; i++) {
+ JasminBuilder appBuilder = new JasminBuilder();
+ ClassBuilder classBuilder = appBuilder.addClass("package.TestClass");
+
+ classBuilder.addVirtualMethod(
+ "method1",
+ "Ljava/lang/Object;",
+ ".limit stack 1",
+ ".limit locals 1",
+ "aconst_null",
+ "areturn");
+ classBuilder.addVirtualMethod(
+ "method1", "Lpackage/A;", ".limit stack 1", ".limit locals 1", "aconst_null", "areturn");
+ classBuilder.addVirtualMethod(
+ "method1", "Lpackage/B;", ".limit stack 1", ".limit locals 1", "aconst_null", "areturn");
+
+ classBuilder.addVirtualMethod(
+ "method2", "Lpackage/A;", ".limit stack 1", ".limit locals 1", "aconst_null", "areturn");
+ classBuilder.addVirtualMethod(
+ "method2",
+ "Ljava/lang/Object;",
+ ".limit stack 1",
+ ".limit locals 1",
+ "aconst_null",
+ "areturn");
+ classBuilder.addVirtualMethod(
+ "method2", "Lpackage/B;", ".limit stack 1", ".limit locals 1", "aconst_null", "areturn");
+
+ classBuilder.addVirtualMethod(
+ "method3", "Lpackage/A;", ".limit stack 1", ".limit locals 1", "aconst_null", "areturn");
+ classBuilder.addVirtualMethod(
+ "method3", "Lpackage/B;", ".limit stack 1", ".limit locals 1", "aconst_null", "areturn");
+ classBuilder.addVirtualMethod(
+ "method3",
+ "Ljava/lang/Object;",
+ ".limit stack 1",
+ ".limit locals 1",
+ "aconst_null",
+ "areturn");
+
+ appBuilder.addInterface("package.A");
+ appBuilder.addInterface("package.B");
+
+ Path proguardMapPath = File.createTempFile("mapping", ".txt", temp.getRoot()).toPath();
+ AndroidApp output =
+ compileWithR8(
+ appBuilder.build(),
+ keepMainProguardConfiguration("package.TestClass"),
+ options -> {
+ options.enableTreeShaking = false;
+ options.proguardMapConsumer = new FileConsumer(proguardMapPath);
+ });
+
+ CodeInspector inspector = new CodeInspector(output, proguardMapPath);
+ ClassSubject clazz = inspector.clazz("package.TestClass");
+ assertThat(clazz, isPresent());
+
+ Map<String, Set<MethodSubject>> methodSubjectsByName = new HashMap<>();
+ for (String name : ImmutableList.of("method1", "method2", "method3")) {
+ methodSubjectsByName.put(
+ name,
+ ImmutableSet.of(
+ clazz.method("java.lang.Object", name, ImmutableList.of()),
+ clazz.method("package.A", name, ImmutableList.of()),
+ clazz.method("package.B", name, ImmutableList.of())));
+ }
+
+ Set<String> minifiedMethodNames = new HashSet<>();
+ for (Set<MethodSubject> methodSubjects : methodSubjectsByName.values()) {
+ for (MethodSubject methodSubject : methodSubjects) {
+ assertThat(methodSubject, isRenamed());
+ minifiedMethodNames.add(methodSubject.getFinalName());
+ }
+ }
+ assertEquals(3, minifiedMethodNames.size());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/b114554345/B114554345.java b/src/test/java/com/android/tools/r8/naming/b114554345/B114554345.java
new file mode 100644
index 0000000..edf77b0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/b114554345/B114554345.java
@@ -0,0 +1,136 @@
+// 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.naming.b114554345;
+
+import static junit.framework.TestCase.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApp;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class B114554345 extends TestBase {
+
+ private final Backend backend;
+
+ @Parameters(name = "mode:{0}")
+ public static Collection<Object[]> data() {
+ List<Object[]> parameters = new ArrayList<>();
+ for (Backend backend : Backend.values()) {
+ parameters.add(new Object[] {backend});
+ }
+ return parameters;
+ }
+
+ public B114554345(Backend backend) {
+ this.backend = backend;
+ }
+
+ @Ignore("b/114554345")
+ @Test
+ public void test() throws Exception {
+ AndroidApp input =
+ AndroidApp.builder()
+ .addProgramFiles(
+ ToolHelper.getClassFilesForTestDirectory(
+ ToolHelper.getPackageDirectoryForTestPackage(this.getClass().getPackage()),
+ path -> !path.toString().contains("B114554345")))
+ .build();
+ AndroidApp output =
+ compileWithR8(
+ input,
+ keepMainProguardConfiguration(TestDriver.class),
+ options -> options.enableInlining = false,
+ backend);
+ String mainClass = TestDriver.class.getName();
+ assertEquals(runOnJava(TestDriver.class), runOnVM(output, mainClass, backend));
+ }
+}
+
+// Interface and two implementations.
+
+interface Interface {
+ Interface method();
+}
+
+class InterfaceImpl implements Interface {
+
+ @Override
+ public InterfaceImpl method() {
+ System.out.println("In InterfaceImpl.method()");
+ return this;
+ }
+}
+
+class OtherInterfaceImpl extends InterfaceImpl {
+
+ @Override
+ public OtherInterfaceImpl method() {
+ System.out.println("In OtherInterfaceImpl.method()");
+ return this;
+ }
+}
+
+// Sub-interface and three implementations.
+
+interface SubInterface extends Interface {
+ SubInterface method();
+}
+
+class SubInterfaceImpl implements SubInterface {
+
+ @Override
+ public SubInterfaceImpl method() {
+ System.out.println("In SubInterfaceImpl.method()");
+ return this;
+ }
+}
+
+class OtherSubInterfaceImpl implements SubInterface {
+
+ @Override
+ public OtherSubInterfaceImpl method() {
+ System.out.println("In OtherSubInterfaceImpl.method()");
+ return this;
+ }
+}
+
+class YetAnotherSubInterfaceImpl extends InterfaceImpl implements SubInterface {
+
+ @Override
+ public YetAnotherSubInterfaceImpl method() {
+ System.out.println("In YetAnotherSubInterfaceImpl.method()");
+ return this;
+ }
+}
+
+class TestDriver {
+
+ public static void main(String[] args) {
+ foo(new InterfaceImpl());
+ foo(new OtherInterfaceImpl());
+ foo(new SubInterfaceImpl());
+ foo(new YetAnotherSubInterfaceImpl());
+ bar(new SubInterfaceImpl());
+ bar(new OtherSubInterfaceImpl());
+ bar(new YetAnotherSubInterfaceImpl());
+ }
+
+ private static void foo(Interface obj) {
+ obj.method();
+ }
+
+ private static void bar(SubInterface obj) {
+ obj.method();
+ }
+}