Version 1.4.26
Cherry-pick: Don't try to look for a class named "null" when the type name is null
CL: https://r8-review.googlesource.com/c/r8/+/33025/
Cherry-pick: Update testing framework
CL: https://r8-review.googlesource.com/c/r8/+/33027/
Cherry-pick: Improve configuration parser error
CL: https://r8-review.googlesource.com/c/r8/+/33030/
Cherry-pick: Add handling of -checkdiscard in the stand alone main dex list generator
CL: https://r8-review.googlesource.com/c/r8/+/32887/
Cherry-pick: Update tests of -checkdiscard in the standalone main dex list generator
CL: https://r8-review.googlesource.com/c/r8/+/33061/
Bug: 116774422
Bug: 122823789
Change-Id: I556173d07fbf43ed28b6345b504752f8249f6c78
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index 48c2cde..14dd1ae 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexDefinition;
import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.shaking.DiscardedChecker;
import com.android.tools.r8.shaking.Enqueuer;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.shaking.MainDexClasses;
@@ -72,6 +73,11 @@
options.mainDexListConsumer.accept(String.join("\n", result), options.reporter);
}
+ if (!mainDexRootSet.checkDiscarded.isEmpty()) {
+ new DiscardedChecker(
+ mainDexRootSet, mainDexClasses.getClasses(), appView.appInfo(), options)
+ .run();
+ }
// Print -whyareyoukeeping results if any.
if (whyAreYouKeepingConsumer != null) {
for (DexDefinition definition : mainDexRootSet.reasonAsked) {
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 6c27fb1..128245f 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "1.4.25";
+ public static final String LABEL = "1.4.26";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/graph/JarCode.java b/src/main/java/com/android/tools/r8/graph/JarCode.java
index 04e6516..c203270 100644
--- a/src/main/java/com/android/tools/r8/graph/JarCode.java
+++ b/src/main/java/com/android/tools/r8/graph/JarCode.java
@@ -214,9 +214,13 @@
triggerDelayedParsingIfNeccessary();
node.instructions.accept(
new JarRegisterEffectsVisitor(method.getHolder(), registry, application));
- node.tryCatchBlocks.forEach(tryCatchBlockNode ->
+ for (TryCatchBlockNode tryCatchBlockNode : node.tryCatchBlocks) {
+ // Exception type can be null for "catch all" used for try/finally.
+ if (tryCatchBlockNode.type != null) {
registry.registerTypeReference(application.getTypeFromDescriptor(
- DescriptorUtils.getDescriptorFromClassBinaryName(tryCatchBlockNode.type))));
+ DescriptorUtils.getDescriptorFromClassBinaryName(tryCatchBlockNode.type)));
+ }
+ }
}
@Override
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 47d3743..1b74e3b 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -401,13 +401,22 @@
&& (unknownOption.equals("forceinline") || unknownOption.equals("neverinline"))) {
devMessage = ", this option needs to be turned on explicitly if used for tests.";
}
- reporter.error(new StringDiagnostic(
- "Unknown option \"-" + unknownOption + "\"" + devMessage,
- origin, getPosition(optionStart)));
+ unknownOption(unknownOption, optionStart, devMessage);
}
return true;
}
+ private void unknownOption(String unknownOption, TextPosition optionStart) {
+ unknownOption(unknownOption, optionStart, "");
+ }
+
+ private void unknownOption(
+ String unknownOption, TextPosition optionStart, String additionalMessage) {
+ throw reporter.fatalError((new StringDiagnostic(
+ "Unknown option \"-" + unknownOption + "\"" + additionalMessage,
+ origin, getPosition(optionStart))));
+ }
+
private boolean parseUnsupportedOptionAndErr(TextPosition optionStart) {
String option = Iterables.find(UNSUPPORTED_FLAG_OPTIONS, this::skipFlag, null);
if (option != null) {
@@ -768,14 +777,19 @@
TextPosition start = getPosition();
acceptString("-");
String unknownOption = acceptString();
- throw reporter.fatalError(new StringDiagnostic(
- "Unknown option \"-" + unknownOption + "\"",
- origin,
- start));
+ unknownOption(unknownOption, start);
}
} else {
builder.setType(ProguardKeepRuleType.KEEP);
}
+ if (!eof() && !Character.isWhitespace(peekChar()) && peekChar() != ',') {
+ // The only path to here is through "-keep" with an unsupported suffix.
+ unacceptString("-keep");
+ TextPosition start = getPosition();
+ acceptString("-");
+ String unknownOption = acceptString();
+ unknownOption(unknownOption, start);
+ }
parseRuleModifiers(builder);
}
diff --git a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
index 56dc071..2499ccb 100644
--- a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
@@ -288,6 +288,7 @@
* @return a class descriptor i.e. "Ljava/lang/Object;"
*/
public static String getDescriptorFromClassBinaryName(String typeBinaryName) {
+ assert typeBinaryName != null;
return ('L' + typeBinaryName + ';');
}
diff --git a/src/test/java/com/android/tools/r8/GenerateMainDexListResult.java b/src/test/java/com/android/tools/r8/GenerateMainDexListResult.java
new file mode 100644
index 0000000..392a72b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/GenerateMainDexListResult.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2019, 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;
+
+public class GenerateMainDexListResult
+ extends TestBaseResult<GenerateMainDexListResult, GenerateMainDexListRunResult> {
+
+ GenerateMainDexListResult(TestState state) {
+ super(state);
+ }
+
+ @Override
+ public GenerateMainDexListResult self() {
+ return this;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/GenerateMainDexListRunResult.java b/src/test/java/com/android/tools/r8/GenerateMainDexListRunResult.java
new file mode 100644
index 0000000..b9842f2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/GenerateMainDexListRunResult.java
@@ -0,0 +1,22 @@
+// Copyright (c) 2019, 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 java.util.List;
+
+public class GenerateMainDexListRunResult extends TestRunResult<GenerateMainDexListRunResult> {
+
+ List<String> mainDexList;
+
+ public GenerateMainDexListRunResult(List<String> mainDexList) {
+ super(null, null);
+ this.mainDexList = mainDexList;
+ }
+
+ @Override
+ protected GenerateMainDexListRunResult self() {
+ return this;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/GenerateMainDexListTestBuilder.java b/src/test/java/com/android/tools/r8/GenerateMainDexListTestBuilder.java
new file mode 100644
index 0000000..6eae670
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/GenerateMainDexListTestBuilder.java
@@ -0,0 +1,64 @@
+// Copyright (c) 2019, 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.GenerateMainDexListCommand.Builder;
+import com.android.tools.r8.debug.DebugTestConfig;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.origin.Origin;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+
+public class GenerateMainDexListTestBuilder
+ extends TestBaseBuilder<
+ GenerateMainDexListCommand,
+ Builder,
+ GenerateMainDexListResult,
+ GenerateMainDexListRunResult,
+ GenerateMainDexListTestBuilder> {
+
+ private GenerateMainDexListTestBuilder(TestState state, Builder builder) {
+ super(state, builder);
+ }
+
+ public static GenerateMainDexListTestBuilder create(TestState state) {
+ return new GenerateMainDexListTestBuilder(state, GenerateMainDexListCommand.builder());
+ }
+
+ @Override
+ GenerateMainDexListTestBuilder self() {
+ return this;
+ }
+
+ @Override
+ public GenerateMainDexListRunResult run(String mainClass)
+ throws IOException, CompilationFailedException {
+ throw new Unimplemented("No support for running with a main class");
+ }
+
+ @Override
+ public GenerateMainDexListRunResult run(Class mainClass)
+ throws IOException, CompilationFailedException {
+ throw new Unimplemented("No support for running with a main class");
+ }
+
+ public DebugTestConfig debugConfig() {
+ throw new Unimplemented("No support for debug configuration");
+ }
+
+ public GenerateMainDexListRunResult run() throws CompilationFailedException {
+ return new GenerateMainDexListRunResult(GenerateMainDexList.run(builder.build()));
+ }
+
+ public GenerateMainDexListTestBuilder addMainDexRules(Collection<String> rules) {
+ builder.addMainDexRules(new ArrayList<>(rules), Origin.unknown());
+ return self();
+ }
+
+ public GenerateMainDexListTestBuilder addMainDexRules(String... rules) {
+ return addMainDexRules(Arrays.asList(rules));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index e0baab1..1df6808 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -103,6 +103,10 @@
return ProguardTestBuilder.create(new TestState(temp));
}
+ public static GenerateMainDexListTestBuilder testForMainDexListGenerator(TemporaryFolder temp) {
+ return GenerateMainDexListTestBuilder.create(new TestState(temp));
+ }
+
public R8TestBuilder testForR8(Backend backend) {
return testForR8(temp, backend);
}
@@ -131,6 +135,10 @@
return testForProguard(temp);
}
+ public GenerateMainDexListTestBuilder testForMainDexListGenerator() {
+ return testForMainDexListGenerator(temp);
+ }
+
public enum Backend {
CF,
DEX
@@ -163,6 +171,8 @@
/**
* Write lines of text to a temporary file.
+ *
+ * The file will include a line separator after the last line.
*/
protected Path writeTextToTempFile(String... lines) throws IOException {
return writeTextToTempFile(System.lineSeparator(), Arrays.asList(lines));
@@ -170,11 +180,28 @@
/**
* Write lines of text to a temporary file, along with the specified line separator.
+ *
+ * The file will include a line separator after the last line.
*/
protected Path writeTextToTempFile(String lineSeparator, List<String> lines)
throws IOException {
+ return writeTextToTempFile(lineSeparator, lines, true);
+ }
+
+ /**
+ * Write lines of text to a temporary file, along with the specified line separator.
+ *
+ * The argument <code>includeTerminatingLineSeparator</code> control if the file will include
+ * a line separator after the last line.
+ */
+ protected Path writeTextToTempFile(
+ String lineSeparator, List<String> lines, boolean includeTerminatingLineSeparator)
+ throws IOException {
Path file = temp.newFile().toPath();
- String contents = String.join(lineSeparator, lines) + lineSeparator;
+ String contents = String.join(lineSeparator, lines);
+ if (includeTerminatingLineSeparator) {
+ contents += lineSeparator;
+ }
Files.write(file, contents.getBytes(StandardCharsets.UTF_8));
return file;
}
diff --git a/src/test/java/com/android/tools/r8/TestBaseBuilder.java b/src/test/java/com/android/tools/r8/TestBaseBuilder.java
new file mode 100644
index 0000000..5fcd1b5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/TestBaseBuilder.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2019, 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 java.nio.file.Path;
+import java.util.Collection;
+
+public abstract class TestBaseBuilder<
+ C extends BaseCommand,
+ B extends BaseCommand.Builder<C, B>,
+ CR extends TestBaseResult<CR, RR>,
+ RR extends TestRunResult,
+ T extends TestBaseBuilder<C, B, CR, RR, T>>
+ extends TestBuilder<RR, T> {
+
+ final B builder;
+
+ TestBaseBuilder(TestState state, B builder) {
+ super(state);
+ this.builder = builder;
+ }
+
+ @Override
+ public T addProgramClassFileData(Collection<byte[]> classes) {
+ for (byte[] clazz : classes) {
+ builder.addClassProgramData(clazz, Origin.unknown());
+ }
+ return self();
+ }
+
+ @Override
+ public T addProgramFiles(Collection<Path> files) {
+ builder.addProgramFiles(files);
+ return self();
+ }
+
+ @Override
+ public T addLibraryFiles(Collection<Path> files) {
+ builder.addLibraryFiles(files);
+ return self();
+ }
+
+ public T addMainDexListFiles(Collection<Path> files) {
+ builder.addMainDexListFiles(files);
+ return self();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/TestBaseResult.java b/src/test/java/com/android/tools/r8/TestBaseResult.java
new file mode 100644
index 0000000..e7623a3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/TestBaseResult.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2019, 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;
+
+public abstract class TestBaseResult<CR extends TestBaseResult<CR, RR>, RR extends TestRunResult> {
+ final TestState state;
+
+ TestBaseResult(TestState state) {
+ this.state = state;
+ }
+
+ public abstract CR self();
+}
diff --git a/src/test/java/com/android/tools/r8/TestBuilder.java b/src/test/java/com/android/tools/r8/TestBuilder.java
index 61bf1b3..c67c05c 100644
--- a/src/test/java/com/android/tools/r8/TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestBuilder.java
@@ -3,16 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
-import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
-
import com.android.tools.r8.debug.DebugTestConfig;
import com.android.tools.r8.utils.ListUtils;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
public abstract class TestBuilder<RR extends TestRunResult, T extends TestBuilder<RR, T>> {
@@ -92,14 +88,6 @@
}
static Collection<Path> getFilesForInnerClasses(Collection<Class<?>> classes) throws IOException {
- Set<Path> paths = new HashSet<>();
- for (Class clazz : classes) {
- Path path = ToolHelper.getClassFileForTestClass(clazz);
- String prefix = path.toString().replace(CLASS_EXTENSION, "$");
- paths.addAll(
- ToolHelper.getClassFilesForTestDirectory(
- path.getParent(), p -> p.toString().startsWith(prefix)));
- }
- return paths;
+ return ToolHelper.getClassFilesForInnerClasses(classes);
}
}
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index 0c84e7c..d74fab5 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -4,9 +4,6 @@
package com.android.tools.r8;
import static com.android.tools.r8.TestBase.Backend.DEX;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.fail;
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.ToolHelper.DexVm;
@@ -29,19 +26,17 @@
import org.hamcrest.Matcher;
public abstract class TestCompileResult<
- CR extends TestCompileResult<CR, RR>, RR extends TestRunResult> {
+ CR extends TestCompileResult<CR, RR>, RR extends TestRunResult>
+ extends TestBaseResult<CR, RR> {
- final TestState state;
public final AndroidApp app;
final List<Path> additionalRunClassPath = new ArrayList<>();
TestCompileResult(TestState state, AndroidApp app) {
- this.state = state;
+ super(state);
this.app = app;
}
- public abstract CR self();
-
public abstract Backend getBackend();
public abstract TestDiagnosticMessages getDiagnosticMessages();
@@ -83,57 +78,27 @@
}
public CR assertNoMessages() {
- assertEquals(0, getDiagnosticMessages().getInfos().size());
- assertEquals(0, getDiagnosticMessages().getWarnings().size());
- assertEquals(0, getDiagnosticMessages().getErrors().size());
+ getDiagnosticMessages().assertNoMessages();
return self();
}
public CR assertOnlyInfos() {
- assertNotEquals(0, getDiagnosticMessages().getInfos().size());
- assertEquals(0, getDiagnosticMessages().getWarnings().size());
- assertEquals(0, getDiagnosticMessages().getErrors().size());
+ getDiagnosticMessages().assertOnlyInfos();
return self();
}
public CR assertOnlyWarnings() {
- assertEquals(0, getDiagnosticMessages().getInfos().size());
- assertNotEquals(0, getDiagnosticMessages().getWarnings().size());
- assertEquals(0, getDiagnosticMessages().getErrors().size());
+ getDiagnosticMessages().assertOnlyWarnings();
return self();
}
public CR assertWarningMessageThatMatches(Matcher<String> matcher) {
- assertNotEquals(0, getDiagnosticMessages().getWarnings().size());
- for (int i = 0; i < getDiagnosticMessages().getWarnings().size(); i++) {
- if (matcher.matches(getDiagnosticMessages().getWarnings().get(i).getDiagnosticMessage())) {
- return self();
- }
- }
- StringBuilder builder = new StringBuilder("No warning matches " + matcher.toString());
- builder.append(System.lineSeparator());
- if (getDiagnosticMessages().getWarnings().size() == 0) {
- builder.append("There where no warnings.");
- } else {
- builder.append("There where " + getDiagnosticMessages().getWarnings().size() + " warnings:");
- builder.append(System.lineSeparator());
- for (int i = 0; i < getDiagnosticMessages().getWarnings().size(); i++) {
- builder.append(getDiagnosticMessages().getWarnings().get(i).getDiagnosticMessage());
- builder.append(System.lineSeparator());
- }
- }
- fail(builder.toString());
+ getDiagnosticMessages().assertWarningMessageThatMatches(matcher);
return self();
}
public CR assertNoWarningMessageThatMatches(Matcher<String> matcher) {
- assertNotEquals(0, getDiagnosticMessages().getWarnings().size());
- for (int i = 0; i < getDiagnosticMessages().getWarnings().size(); i++) {
- String message = getDiagnosticMessages().getWarnings().get(i).getDiagnosticMessage();
- if (matcher.matches(message)) {
- fail("The warning: \"" + message + "\" + matches " + matcher + ".");
- }
- }
+ getDiagnosticMessages().assertNoWarningMessageThatMatches(matcher);
return self();
}
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index cbd0cb9..77dc0df 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -5,7 +5,6 @@
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.debug.DebugTestConfig;
-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.AndroidAppConsumers;
@@ -24,7 +23,7 @@
CR extends TestCompileResult<CR, RR>,
RR extends TestRunResult,
T extends TestCompilerBuilder<C, B, CR, RR, T>>
- extends TestBuilder<RR, T> {
+ extends TestBaseBuilder<C, B, CR, RR, T> {
public static final Consumer<InternalOptions> DEFAULT_OPTIONS =
new Consumer<InternalOptions>() {
@@ -32,7 +31,6 @@
public void accept(InternalOptions options) {}
};
- final B builder;
final Backend backend;
// Default initialized setup. Can be overwritten if needed.
@@ -44,8 +42,7 @@
private PrintStream stdout = null;
TestCompilerBuilder(TestState state, B builder, Backend backend) {
- super(state);
- this.builder = builder;
+ super(state, builder);
this.backend = backend;
defaultLibrary = TestBase.runtimeJar(backend);
programConsumer = TestBase.emptyConsumer(backend);
@@ -135,30 +132,10 @@
return self();
}
- public T addMainDexListFiles(Collection<Path> files) {
- builder.addMainDexListFiles(files);
- return self();
- }
-
- @Override
- public T addProgramClassFileData(Collection<byte[]> classes) {
- for (byte[] clazz : classes) {
- builder.addClassProgramData(clazz, Origin.unknown());
- }
- return self();
- }
-
- @Override
- public T addProgramFiles(Collection<Path> files) {
- builder.addProgramFiles(files);
- return self();
- }
-
@Override
public T addLibraryFiles(Collection<Path> files) {
defaultLibrary = null;
- builder.addLibraryFiles(files);
- return self();
+ return super.addLibraryFiles(files);
}
public T noDesugaring() {
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
index 3e734cc..d1e55f4 100644
--- a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
@@ -5,6 +5,8 @@
package com.android.tools.r8;
import java.util.List;
+import org.hamcrest.Matcher;
+
public interface TestDiagnosticMessages {
@@ -13,4 +15,20 @@
public List<Diagnostic> getWarnings();
public List<Diagnostic> getErrors();
+
+ public TestDiagnosticMessages assertNoMessages();
+
+ public TestDiagnosticMessages assertOnlyInfos();
+
+ public TestDiagnosticMessages assertOnlyWarnings();
+
+ public TestDiagnosticMessages assertInfosCount(int count);
+
+ public TestDiagnosticMessages assertWarningsCount(int count);
+
+ public TestDiagnosticMessages assertErrorsCount(int count);
+
+ public TestDiagnosticMessages assertWarningMessageThatMatches(Matcher<String> matcher);
+
+ public TestDiagnosticMessages assertNoWarningMessageThatMatches(Matcher<String> matcher);
}
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
index d0476b9..43d71b1 100644
--- a/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
@@ -4,8 +4,13 @@
package com.android.tools.r8;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.fail;
+
import java.util.ArrayList;
import java.util.List;
+import org.hamcrest.Matcher;
public class TestDiagnosticMessagesImpl implements DiagnosticsHandler, TestDiagnosticMessages {
private final List<Diagnostic> infos = new ArrayList<>();
@@ -38,4 +43,75 @@
public List<Diagnostic> getErrors() {
return errors;
}
+
+
+ public TestDiagnosticMessages assertNoMessages() {
+ assertEquals(0, getInfos().size());
+ assertEquals(0, getWarnings().size());
+ assertEquals(0, getErrors().size());
+ return this;
+ }
+
+ public TestDiagnosticMessages assertOnlyInfos() {
+ assertNotEquals(0, getInfos().size());
+ assertEquals(0, getWarnings().size());
+ assertEquals(0, getErrors().size());
+ return this;
+ }
+
+ public TestDiagnosticMessages assertOnlyWarnings() {
+ assertEquals(0, getInfos().size());
+ assertNotEquals(0, getWarnings().size());
+ assertEquals(0, getErrors().size());
+ return this;
+ }
+
+ public TestDiagnosticMessages assertInfosCount(int count) {
+ assertEquals(count, getInfos().size());
+ return this;
+ }
+
+ public TestDiagnosticMessages assertWarningsCount(int count) {
+ assertEquals(count, getWarnings().size());
+ return this;
+ }
+
+ public TestDiagnosticMessages assertErrorsCount(int count) {
+ assertEquals(count, getErrors().size());
+ return this;
+ }
+
+ public TestDiagnosticMessages assertWarningMessageThatMatches(Matcher<String> matcher) {
+ assertNotEquals(0, getWarnings().size());
+ for (int i = 0; i < getWarnings().size(); i++) {
+ if (matcher.matches(getWarnings().get(i).getDiagnosticMessage())) {
+ return this;
+ }
+ }
+ StringBuilder builder = new StringBuilder("No warning matches " + matcher.toString());
+ builder.append(System.lineSeparator());
+ if (getWarnings().size() == 0) {
+ builder.append("There where no warnings.");
+ } else {
+ builder.append("There where " + getWarnings().size() + " warnings:");
+ builder.append(System.lineSeparator());
+ for (int i = 0; i < getWarnings().size(); i++) {
+ builder.append(getWarnings().get(i).getDiagnosticMessage());
+ builder.append(System.lineSeparator());
+ }
+ }
+ fail(builder.toString());
+ return this;
+ }
+
+ public TestDiagnosticMessages assertNoWarningMessageThatMatches(Matcher<String> matcher) {
+ assertNotEquals(0, getWarnings().size());
+ for (int i = 0; i < getWarnings().size(); i++) {
+ String message = getWarnings().get(i).getDiagnosticMessage();
+ if (matcher.matches(message)) {
+ fail("The warning: \"" + message + "\" + matches " + matcher + ".");
+ }
+ }
+ return this;
+ }
}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 64c735d..dcfda2c 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
import static com.android.tools.r8.utils.FileUtils.isDexFile;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -57,6 +58,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -849,6 +851,28 @@
Paths.get("", parts.toArray(new String[parts.size() - 1])));
}
+ public static Collection<Path> getClassFilesForInnerClasses(Path path) throws IOException {
+ Set<Path> paths = new HashSet<>();
+ String prefix = path.toString().replace(CLASS_EXTENSION, "$");
+ paths.addAll(
+ ToolHelper.getClassFilesForTestDirectory(
+ path.getParent(), p -> p.toString().startsWith(prefix)));
+ return paths;
+ }
+
+ public static Collection<Path> getClassFilesForInnerClasses(Collection<Class<?>> classes)
+ throws IOException {
+ Set<Path> paths = new HashSet<>();
+ for (Class clazz : classes) {
+ Path path = ToolHelper.getClassFileForTestClass(clazz);
+ String prefix = path.toString().replace(CLASS_EXTENSION, "$");
+ paths.addAll(
+ ToolHelper.getClassFilesForTestDirectory(
+ path.getParent(), p -> p.toString().startsWith(prefix)));
+ }
+ return paths;
+ }
+
public static Path getFileNameForTestClass(Class clazz) {
List<String> parts = getNamePartsForTestClass(clazz);
return Paths.get("", parts.toArray(new String[parts.size() - 1]));
diff --git a/src/test/java/com/android/tools/r8/maindexlist/checkdiscard/MainDexListCheckDiscard.java b/src/test/java/com/android/tools/r8/maindexlist/checkdiscard/MainDexListCheckDiscard.java
index cd85cd4..125c364 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/checkdiscard/MainDexListCheckDiscard.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/checkdiscard/MainDexListCheckDiscard.java
@@ -5,15 +5,17 @@
package com.android.tools.r8.maindexlist.checkdiscard;
import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.CompilationMode;
-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.origin.Origin;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.google.common.collect.ImmutableList;
+import java.util.List;
import org.junit.Assert;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
class HelloWorldMain {
public static void main(String[] args) {
@@ -25,22 +27,59 @@
class NonMainDexClass {}
+@RunWith(Parameterized.class)
public class MainDexListCheckDiscard extends TestBase {
+ private enum Command {
+ R8,
+ Generator
+ }
+
+ private static final List<Class<?>> CLASSES =
+ ImmutableList.of(HelloWorldMain.class, MainDexClass.class, NonMainDexClass.class);
+
+ @Parameters(name = "{0}")
+ public static Object[] parameters() {
+ return Command.values();
+ }
+
+ private final Command command;
+
+ public MainDexListCheckDiscard(Command command) {
+ this.command = command;
+ }
+
+ public void runTestWithR8(String checkDiscardRule) throws Exception {
+ testForR8(Backend.DEX)
+ .addProgramClasses(CLASSES)
+ .setMinApi(AndroidApiLevel.K)
+ .addMainDexRules(keepMainProguardConfiguration(HelloWorldMain.class))
+ .addMainDexRules(checkDiscardRule)
+ .noTreeShaking()
+ .noMinification()
+ .compile();
+ }
+
+ public void runTestWithGenerator(String checkDiscardRule) throws Exception {
+ testForMainDexListGenerator()
+ .addProgramClasses(CLASSES)
+ .addLibraryFiles(ToolHelper.getDefaultAndroidJar())
+ .addMainDexRules(keepMainProguardConfiguration(HelloWorldMain.class))
+ .addMainDexRules(checkDiscardRule)
+ .run();
+ }
+
public void runTest(String checkDiscardRule, boolean shouldFail) throws Exception {
- R8Command command =
- ToolHelper.prepareR8CommandBuilder(
- readClasses(HelloWorldMain.class, MainDexClass.class, NonMainDexClass.class))
- .addMainDexRules(
- ImmutableList.of(keepMainProguardConfiguration(HelloWorldMain.class)),
- Origin.unknown())
- .addMainDexRules(ImmutableList.of(checkDiscardRule), Origin.unknown())
- .setOutput(temp.getRoot().toPath(), OutputMode.DexIndexed)
- .setMode(CompilationMode.RELEASE)
- .setDisableTreeShaking(true)
- .setDisableMinification(true)
- .build();
try {
- ToolHelper.runR8(command);
+ switch (command) {
+ case R8:
+ runTestWithR8(checkDiscardRule);
+ break;
+ case Generator:
+ runTestWithGenerator(checkDiscardRule);
+ break;
+ default:
+ throw new Unreachable();
+ }
} catch (CompilationFailedException e) {
Assert.assertTrue(shouldFail);
return;
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index ff84486..db5f631 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -1113,6 +1113,39 @@
}
@Test
+ public void parseInvalidKeepOption() throws Exception {
+ Path proguardConfig = writeTextToTempFile(
+ "-keepx public class * { ",
+ " native <methods>; ",
+ "} "
+ );
+ try {
+ ProguardConfigurationParser parser =
+ new ProguardConfigurationParser(new DexItemFactory(), reporter);
+ parser.parse(proguardConfig);
+ fail();
+ } catch (AbortException e) {
+ checkDiagnostics(handler.errors, proguardConfig, 1, 1,
+ "Unknown option", "-keepx");
+ }
+ }
+
+ @Test
+ public void parseKeepOptionEOF() throws Exception {
+ Path proguardConfig = writeTextToTempFile(
+ System.lineSeparator(), ImmutableList.of("-keep"), false);
+ try {
+ ProguardConfigurationParser parser =
+ new ProguardConfigurationParser(new DexItemFactory(), reporter);
+ parser.parse(proguardConfig);
+ fail();
+ } catch (AbortException e) {
+ checkDiagnostics(handler.errors, proguardConfig, 1, 6,
+ "Expected [!]interface|@interface|class|enum");
+ }
+ }
+
+ @Test
public void parseInvalidKeepClassOption() throws Exception {
Path proguardConfig = writeTextToTempFile(
"-keepclassx public class * { ",
@@ -1493,7 +1526,7 @@
parser.parse(createConfigurationForTesting(ImmutableList.of(option + " class A { *; }")));
fail("Expect to fail due to testing option being turned off.");
} catch (AbortException e) {
- assertEquals(2, handler.errors.size());
+ assertEquals(1, handler.errors.size());
checkDiagnostics(handler.errors, 0, null, 1, 1, "Unknown option \"" + option + "\"");
}
}