Merge "Improve information in GenericSignatureFormatError"
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 250f2e9..1db187e 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -17,7 +17,6 @@
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.CfgPrinter;
import com.android.tools.r8.utils.ExceptionUtils;
-import com.android.tools.r8.utils.FlagFile;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
@@ -94,9 +93,8 @@
});
}
- private static void run(String[] args) throws CompilationFailedException, IOException {
- String[] expandedArgs = FlagFile.expandFlagFiles(args);
- D8Command command = D8Command.parse(expandedArgs, CommandLineOrigin.INSTANCE).build();
+ private static void run(String[] args) throws CompilationFailedException {
+ D8Command command = D8Command.parse(args, CommandLineOrigin.INSTANCE).build();
if (command.isPrintHelp()) {
System.out.println(USAGE_MESSAGE);
return;
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 4c65134..6f956db 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -7,6 +7,7 @@
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;
@@ -215,9 +216,10 @@
Path outputPath = null;
OutputMode outputMode = null;
boolean hasDefinedApiLevel = false;
+ String[] expandedArgs = FlagFile.expandFlagFiles(args, builder.getReporter());
try {
- for (int i = 0; i < args.length; i++) {
- String arg = args[i].trim();
+ for (int i = 0; i < expandedArgs.length; i++) {
+ String arg = expandedArgs[i].trim();
if (arg.length() == 0) {
continue;
} else if (arg.equals("--help")) {
@@ -243,7 +245,7 @@
} else if (arg.equals("--file-per-class")) {
outputMode = OutputMode.DexFilePerClassFile;
} else if (arg.equals("--output")) {
- String output = args[++i];
+ String output = expandedArgs[++i];
if (outputPath != null) {
builder.getReporter().error(new StringDiagnostic(
"Cannot output both to '" + outputPath.toString() + "' and '" + output + "'",
@@ -252,15 +254,15 @@
}
outputPath = Paths.get(output);
} else if (arg.equals("--lib")) {
- builder.addLibraryFiles(Paths.get(args[++i]));
+ builder.addLibraryFiles(Paths.get(expandedArgs[++i]));
} else if (arg.equals("--classpath")) {
- builder.addClasspathFiles(Paths.get(args[++i]));
+ builder.addClasspathFiles(Paths.get(expandedArgs[++i]));
} else if (arg.equals("--main-dex-list")) {
- builder.addMainDexListFiles(Paths.get(args[++i]));
+ 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, args[++i], hasDefinedApiLevel, origin);
+ hasDefinedApiLevel = parseMinApi(builder, expandedArgs[++i], hasDefinedApiLevel, origin);
} else if (arg.equals("--intermediate")) {
builder.setIntermediate(true);
} else if (arg.equals("--no-desugaring")) {
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 8e1722e..16c9de8 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -51,7 +51,6 @@
import com.android.tools.r8.utils.CfgPrinter;
import com.android.tools.r8.utils.ExceptionUtils;
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.LineNumberOptimizer;
@@ -534,9 +533,8 @@
}
}
- private static void run(String[] args) throws CompilationFailedException, IOException {
- String[] expandedArgs = FlagFile.expandFlagFiles(args);
- R8Command command = R8Command.parse(expandedArgs, CommandLineOrigin.INSTANCE).build();
+ private static void run(String[] args) throws CompilationFailedException {
+ R8Command command = R8Command.parse(args, CommandLineOrigin.INSTANCE).build();
if (command.isPrintHelp()) {
System.out.println(USAGE_MESSAGE);
return;
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index ced17d4..4fc0590 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -18,6 +18,7 @@
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;
@@ -513,8 +514,9 @@
Origin argsOrigin,
Builder builder,
ParseState state) {
- for (int i = 0; i < args.length; i++) {
- String arg = args[i].trim();
+ 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")) {
@@ -546,7 +548,7 @@
}
state.outputMode = OutputMode.ClassFile;
} else if (arg.equals("--output")) {
- String outputPath = args[++i];
+ String outputPath = expandedArgs[++i];
if (state.outputPath != null) {
builder.getReporter().error(new StringDiagnostic(
"Cannot output both to '"
@@ -558,10 +560,10 @@
}
state.outputPath = Paths.get(outputPath);
} else if (arg.equals("--lib")) {
- builder.addLibraryFiles(Paths.get(args[++i]));
+ builder.addLibraryFiles(Paths.get(expandedArgs[++i]));
} else if (arg.equals("--min-api")) {
state.hasDefinedApiLevel =
- parseMinApi(builder, args[++i], state.hasDefinedApiLevel, argsOrigin);
+ parseMinApi(builder, expandedArgs[++i], state.hasDefinedApiLevel, argsOrigin);
} else if (arg.equals("--no-tree-shaking")) {
builder.setDisableTreeShaking(true);
} else if (arg.equals("--no-minification")) {
@@ -569,17 +571,17 @@
} else if (arg.equals("--no-desugaring")) {
builder.setDisableDesugaring(true);
} else if (arg.equals("--main-dex-rules")) {
- builder.addMainDexRulesFiles(Paths.get(args[++i]));
+ builder.addMainDexRulesFiles(Paths.get(expandedArgs[++i]));
} else if (arg.equals("--main-dex-list")) {
- builder.addMainDexListFiles(Paths.get(args[++i]));
+ builder.addMainDexListFiles(Paths.get(expandedArgs[++i]));
} else if (arg.equals("--main-dex-list-output")) {
- builder.setMainDexListOutputPath(Paths.get(args[++i]));
+ 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(args[++i]));
+ builder.addProguardConfigurationFiles(Paths.get(expandedArgs[++i]));
} else if (arg.equals("--pg-map-output")) {
- builder.setProguardMapOutputPath(Paths.get(args[++i]));
+ builder.setProguardMapOutputPath(Paths.get(expandedArgs[++i]));
} else {
if (arg.startsWith("--")) {
builder.getReporter().error(new StringDiagnostic("Unknown option: " + arg, argsOrigin));
diff --git a/src/main/java/com/android/tools/r8/graph/AccessFlags.java b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
index 324362a..626e5cc 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
@@ -68,6 +68,25 @@
return flags;
}
+ public boolean isMoreVisibleThan(AccessFlags other) {
+ return visibilityOrdinal() > other.visibilityOrdinal();
+ }
+
+ private int visibilityOrdinal() {
+ // public > protected > package > private
+ if (isPublic()) {
+ return 3;
+ }
+ if (isProtected()) {
+ return 2;
+ }
+ if (isPrivate()) {
+ return 0;
+ }
+ // Package-private
+ return 1;
+ }
+
public boolean isPublic() {
return isSet(Constants.ACC_PUBLIC);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java b/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java
index e4e2ed7..610bfd3 100644
--- a/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java
@@ -52,7 +52,7 @@
List<DexEncodedMethod> methods = null;
for (int i = 0; i < virtualMethods.length; i++) {
DexEncodedMethod method = virtualMethods[i];
- if (scope.addMethod(method.method) || !method.accessFlags.isAbstract()) {
+ if (scope.addMethodIfMoreVisible(method) || !method.accessFlags.isAbstract()) {
if (methods != null) {
methods.add(method);
}
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 27b2da7..d718026 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -739,7 +739,6 @@
transitionNonAbstractMethodsToLiveAndShadow(
reachableMethods.getItems(), instantiatedType, seen.newNestedScope());
}
- seen = seen.newNestedScope();
for (DexType subInterface : clazz.interfaces.values) {
transitionDefaultMethodsForInstantiatedClass(subInterface, instantiatedType, seen);
}
@@ -748,7 +747,7 @@
private void transitionNonAbstractMethodsToLiveAndShadow(Iterable<DexEncodedMethod> reachable,
DexType instantiatedType, ScopedDexMethodSet seen) {
for (DexEncodedMethod encodedMethod : reachable) {
- if (seen.addMethod(encodedMethod.method)) {
+ if (seen.addMethod(encodedMethod)) {
// Abstract methods do shadow implementations but they cannot be live, as they have no
// code.
if (!encodedMethod.accessFlags.isAbstract()) {
diff --git a/src/main/java/com/android/tools/r8/shaking/ScopedDexMethodSet.java b/src/main/java/com/android/tools/r8/shaking/ScopedDexMethodSet.java
index 39f07ee..3587cf4 100644
--- a/src/main/java/com/android/tools/r8/shaking/ScopedDexMethodSet.java
+++ b/src/main/java/com/android/tools/r8/shaking/ScopedDexMethodSet.java
@@ -3,19 +3,20 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.google.common.base.Equivalence;
import com.google.common.base.Equivalence.Wrapper;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.HashMap;
+import java.util.Map;
class ScopedDexMethodSet {
private static final Equivalence<DexMethod> METHOD_EQUIVALENCE = MethodSignatureEquivalence.get();
private final ScopedDexMethodSet parent;
- private final Set<Wrapper<DexMethod>> items = new HashSet<>();
+ private final Map<Wrapper<DexMethod>, DexEncodedMethod> items = new HashMap<>();
public ScopedDexMethodSet() {
this(null);
@@ -29,14 +30,32 @@
return new ScopedDexMethodSet(this);
}
- private boolean contains(Wrapper<DexMethod> item) {
- return items.contains(item)
- || ((parent != null) && parent.contains(item));
+ private DexEncodedMethod lookup(Wrapper<DexMethod> item) {
+ DexEncodedMethod ownMethod = items.get(item);
+ return ownMethod != null ? ownMethod : (parent != null ? parent.lookup(item) : null);
}
- public boolean addMethod(DexMethod method) {
- Wrapper<DexMethod> wrapped = METHOD_EQUIVALENCE.wrap(method);
- return !contains(wrapped) && items.add(wrapped);
+ private boolean contains(Wrapper<DexMethod> item) {
+ return lookup(item) != null;
+ }
+
+ public boolean addMethod(DexEncodedMethod method) {
+ Wrapper<DexMethod> wrapped = METHOD_EQUIVALENCE.wrap(method.method);
+ if (contains(wrapped)) {
+ return false;
+ }
+ items.put(wrapped, method);
+ return true;
+ }
+
+ public boolean addMethodIfMoreVisible(DexEncodedMethod method) {
+ Wrapper<DexMethod> wrapped = METHOD_EQUIVALENCE.wrap(method.method);
+ DexEncodedMethod existing = lookup(wrapped);
+ if (existing == null || method.accessFlags.isMoreVisibleThan(existing.accessFlags)) {
+ items.put(wrapped, method);
+ return true;
+ }
+ return false;
}
public ScopedDexMethodSet getParent() {
diff --git a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
index 577632a..9c7f525 100644
--- a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
@@ -74,7 +74,7 @@
}
public interface MainAction {
- void run() throws CompilationFailedException, IOException;
+ void run() throws CompilationFailedException;
}
public static void withMainProgramHandler(MainAction action) {
@@ -84,7 +84,7 @@
// Detail of the errors were already reported
System.err.println("Compilation failed");
System.exit(STATUS_ERROR);
- } catch (RuntimeException | IOException e) {
+ } catch (RuntimeException e) {
System.err.println("Compilation failed with an internal error.");
Throwable cause = e.getCause() == null ? e : e.getCause();
cause.printStackTrace();
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 2354066..4791217 100644
--- a/src/main/java/com/android/tools/r8/utils/FlagFile.java
+++ b/src/main/java/com/android/tools/r8/utils/FlagFile.java
@@ -4,18 +4,41 @@
package com.android.tools.r8.utils;
+import com.android.tools.r8.origin.Origin;
import java.io.IOException;
import java.nio.file.Files;
+import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
public class FlagFile {
- public static String[] expandFlagFiles(String[] args) throws IOException {
+
+ private static class FlagFileOrigin extends Origin {
+ private final Path path;
+
+ protected FlagFileOrigin(Path path) {
+ super(Origin.root());
+ this.path = path;
+ }
+
+ @Override
+ public String part() {
+ return "flag file argument: '@" + path + "'";
+ }
+ }
+
+ public static String[] expandFlagFiles(String[] args, Reporter reporter) {
List<String> flags = new ArrayList<>(args.length);
for (String arg : args) {
if (arg.startsWith("@")) {
- flags.addAll(Files.readAllLines(Paths.get(arg.substring(1))));
+ Path flagFilePath = Paths.get(arg.substring(1));
+ try {
+ flags.addAll(Files.readAllLines(flagFilePath));
+ } catch (IOException e) {
+ Origin origin = new FlagFileOrigin(flagFilePath);
+ reporter.error(new ExceptionDiagnostic(e, origin));
+ }
} else {
flags.add(arg);
}
diff --git a/src/test/examples/abstractmethodremoval/AbstractMethodRemoval.java b/src/test/examples/abstractmethodremoval/AbstractMethodRemoval.java
new file mode 100644
index 0000000..1da772d
--- /dev/null
+++ b/src/test/examples/abstractmethodremoval/AbstractMethodRemoval.java
@@ -0,0 +1,34 @@
+// 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 abstractmethodremoval;
+
+import abstractmethodremoval.a.PackageBase;
+import abstractmethodremoval.a.Public;
+import abstractmethodremoval.b.Impl1;
+import abstractmethodremoval.b.Impl2;
+
+public class AbstractMethodRemoval {
+
+ private static int dummy;
+
+ public static void main(String[] args) {
+ dummy = args.length;
+ invokeFoo(new Impl1());
+ invokeFoo(new Impl2());
+ invokeFoo(new Impl2());
+ PackageBase.invokeFoo(new Impl1());
+ PackageBase.invokeFoo(new Impl2());
+ PackageBase.invokeFoo(new Impl2());
+ }
+
+ private static void invokeFoo(Public aPublic) {
+ // Enough instructions to avoid inlining.
+ aPublic.foo(dummy() + dummy() + dummy() + dummy() + dummy() + dummy() + dummy() + dummy());
+ }
+
+ private static int dummy() {
+ // Enough instructions to avoid inlining.
+ return dummy + dummy + dummy + dummy + dummy + dummy + dummy + dummy + dummy + dummy + dummy;
+ }
+}
diff --git a/src/test/examples/abstractmethodremoval/a/PackageBase.java b/src/test/examples/abstractmethodremoval/a/PackageBase.java
new file mode 100644
index 0000000..1075b57
--- /dev/null
+++ b/src/test/examples/abstractmethodremoval/a/PackageBase.java
@@ -0,0 +1,12 @@
+// 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 abstractmethodremoval.a;
+
+public abstract class PackageBase {
+ abstract void foo(int i);
+
+ public static void invokeFoo(PackageBase o) {
+ o.foo(0);
+ }
+}
diff --git a/src/test/examples/abstractmethodremoval/a/Public.java b/src/test/examples/abstractmethodremoval/a/Public.java
new file mode 100644
index 0000000..492ab87
--- /dev/null
+++ b/src/test/examples/abstractmethodremoval/a/Public.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 abstractmethodremoval.a;
+
+public abstract class Public extends PackageBase {
+ @Override
+ public abstract void foo(int i);
+}
diff --git a/src/test/examples/abstractmethodremoval/b/Impl1.java b/src/test/examples/abstractmethodremoval/b/Impl1.java
new file mode 100644
index 0000000..bfdf4d1
--- /dev/null
+++ b/src/test/examples/abstractmethodremoval/b/Impl1.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package abstractmethodremoval.b;
+
+import abstractmethodremoval.a.Public;
+
+public class Impl1 extends Public {
+ @Override
+ public void foo(int i) {
+ System.out.println("Impl1.foo(" + i + ")");
+ }
+}
diff --git a/src/test/examples/abstractmethodremoval/b/Impl2.java b/src/test/examples/abstractmethodremoval/b/Impl2.java
new file mode 100644
index 0000000..a92e2a1
--- /dev/null
+++ b/src/test/examples/abstractmethodremoval/b/Impl2.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package abstractmethodremoval.b;
+
+import abstractmethodremoval.a.Public;
+
+public class Impl2 extends Public {
+ @Override
+ public void foo(int i) {
+ System.out.println("Impl2.foo(" + i + ")");
+ }
+}
diff --git a/src/test/examples/abstractmethodremoval/keep-rules.txt b/src/test/examples/abstractmethodremoval/keep-rules.txt
new file mode 100644
index 0000000..ec37858
--- /dev/null
+++ b/src/test/examples/abstractmethodremoval/keep-rules.txt
@@ -0,0 +1 @@
+-keep public class abstractmethodremoval.AbstractMethodRemoval { public static void main(...); }
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index 5576058..1b846a5 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -9,6 +9,7 @@
import static java.nio.file.StandardOpenOption.CREATE_NEW;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import com.android.sdklib.AndroidVersion;
@@ -99,7 +100,7 @@
FileUtils.writeTextFile(
flagsFile,
"--output",
- output.toString(),
+ "output.zip",
"--min-api",
"24",
input.toString());
@@ -110,6 +111,19 @@
assertEquals(Tool.D8, marker.getTool());
}
+ @Test(expected=CompilationFailedException.class)
+ public void nonExistingFlagsFile() throws Throwable {
+ Path working = temp.getRoot().toPath();
+ Path flags = working.resolve("flags.txt").toAbsolutePath();
+ assertNotEquals(0, ToolHelper.forkR8(working, "@flags.txt").exitCode);
+ DiagnosticsChecker.checkErrorsContains("File not found", handler ->
+ D8.run(
+ D8Command.parse(
+ new String[] { "@" + flags.toString() },
+ EmbeddedOrigin.INSTANCE,
+ handler).build()));
+ }
+
@Test
public void printsHelpOnNoInput() throws Throwable {
ProcessResult result = ToolHelper.forkD8(temp.getRoot().toPath());
diff --git a/src/test/java/com/android/tools/r8/DiagnosticsChecker.java b/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
index 2265c3e..2f45ae2 100644
--- a/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
+++ b/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
@@ -49,13 +49,14 @@
try {
runner.run(handler);
} catch (CompilationFailedException e) {
+ List<String> messages = ListUtils.map(handler.errors, Diagnostic::getDiagnosticMessage);
System.out.println("Expecting match for '" + snippet + "'");
- System.out.println("StdErr:\n" + handler.errors);
+ System.out.println("StdErr:\n" + messages);
assertTrue(
"Expected to find snippet '"
+ snippet
+ "' in error messages:\n"
- + String.join("\n", ListUtils.map(handler.errors, Diagnostic::getDiagnosticMessage)),
+ + String.join("\n", messages),
handler.errors.stream().anyMatch(d -> d.getDiagnosticMessage().contains(snippet)));
throw e;
}
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java
index c1072bd..fbd88be 100644
--- a/src/test/java/com/android/tools/r8/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -101,7 +101,7 @@
FileUtils.writeTextFile(
flagsFile,
"--output",
- output.toString(),
+ "output.zip",
"--min-api",
"24",
"--lib",
@@ -114,6 +114,20 @@
assertEquals(Tool.R8, marker.getTool());
}
+
+ @Test(expected=CompilationFailedException.class)
+ public void nonExistingFlagsFile() throws Throwable {
+ Path working = temp.getRoot().toPath();
+ Path flags = working.resolve("flags.txt").toAbsolutePath();
+ assertNotEquals(0, ToolHelper.forkR8(working, "@flags.txt").exitCode);
+ DiagnosticsChecker.checkErrorsContains("File not found", handler ->
+ R8.run(
+ R8Command.parse(
+ new String[] { "@" + flags.toString() },
+ EmbeddedOrigin.INSTANCE,
+ handler).build()));
+ }
+
@Test
public void printsHelpOnNoInput() throws Throwable {
ProcessResult result = ToolHelper.forkR8(temp.getRoot().toPath());
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index 913278a..2ac50a6 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -27,6 +27,7 @@
@Parameters(name = "{0}_{1}_{2}_{3}_{5}_{6}")
public static Collection<String[]> data() {
String[] tests = {
+ "abstractmethodremoval.AbstractMethodRemoval",
"arithmetic.Arithmetic",
"arrayaccess.ArrayAccess",
"barray.BArray",
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAbstractMethodRemovalTest.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAbstractMethodRemovalTest.java
new file mode 100644
index 0000000..a5b13a7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAbstractMethodRemovalTest.java
@@ -0,0 +1,49 @@
+// 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.shaking.examples;
+
+import com.android.tools.r8.TestBase.MinifyMode;
+import com.android.tools.r8.shaking.TreeShakingTest;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+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 TreeShakingAbstractMethodRemovalTest extends TreeShakingTest {
+
+ @Parameters(name = "mode:{0}-{1} minify:{2}")
+ public static Collection<Object[]> data() {
+ List<Object[]> parameters = new ArrayList<>();
+ for (MinifyMode minify : MinifyMode.values()) {
+ parameters.add(new Object[] {Frontend.JAR, Backend.CF, minify});
+ parameters.add(new Object[] {Frontend.JAR, Backend.DEX, minify});
+ parameters.add(new Object[] {Frontend.DEX, Backend.DEX, minify});
+ }
+ return parameters;
+ }
+
+ public TreeShakingAbstractMethodRemovalTest(
+ Frontend frontend, Backend backend, MinifyMode minify) {
+ super(
+ "examples/abstractmethodremoval",
+ "abstractmethodremoval.AbstractMethodRemoval",
+ frontend,
+ backend,
+ minify);
+ }
+
+ @Test
+ public void test() throws Exception {
+ runTest(
+ null,
+ null,
+ null,
+ ImmutableList.of("src/test/examples/abstractmethodremoval/keep-rules.txt"));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/IfZeroObjectTest.java b/src/test/java/com/android/tools/r8/smali/IfZeroObjectTest.java
new file mode 100644
index 0000000..ebcd835
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/IfZeroObjectTest.java
@@ -0,0 +1,68 @@
+// 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.smali;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+
+public class IfZeroObjectTest extends SmaliTestBase {
+
+ public static final String CLASS = "Test";
+
+ @Test
+ public void testObjectIfs() throws Throwable {
+ ProcessResult result = testIfs("ExpectedToPass", Arrays.asList(
+ "eqz",
+ "nez"));
+ assertEquals(result.toString(), 0, result.exitCode);
+ }
+
+ @Test
+ public void testNumericIfs() throws Throwable {
+ ProcessResult result = testIfs("ExpectedToFail", Arrays.asList(
+ "ltz",
+ "gez",
+ "gtz",
+ "lez"));
+ assertEquals(result.toString(), 1, result.exitCode);
+ assertTrue("Did not find 'Verification error' in " + result.stderr,
+ result.stderr.contains("Verification error") || result.stderr.contains("VerifyError"));
+ }
+
+ private ProcessResult testIfs(String clazz, List<String> ifZeroOps) throws Throwable {
+
+ SmaliBuilder builder = new SmaliBuilder(clazz);
+ for (String ifZeroOp : ifZeroOps) {
+ builder.addStaticMethod("int", "if" + ifZeroOp, Collections.singletonList("java.lang.Object"),
+ 1,
+ " if-" + ifZeroOp + " p0, :L",
+ " const v0, 0",
+ " return v0",
+ " :L",
+ " const v0, 1",
+ " return v0"
+ );
+ }
+
+ List<String> main = new ArrayList<>();
+ main.add(" sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;");
+ for (String ifZeroOp : ifZeroOps) {
+ main.add(" invoke-static { p0 }, L" + clazz + ";->if" + ifZeroOp + "(Ljava/lang/Object;)I");
+ main.add(" move-result v1");
+ main.add(" invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(I)V");
+ }
+ main.add(" return-void");
+ builder.addMainMethod(2, main.toArray(new String[0]));
+
+ return runOnArtRaw(builder.build(), clazz);
+ }
+}