Merge commit '537d711fb733af80445dcf523dff772ddd844c3d' into dev-release
diff --git a/src/main/java/com/android/tools/r8/AssertionsConfiguration.java b/src/main/java/com/android/tools/r8/AssertionsConfiguration.java
index 15acd6f..7199b77 100644
--- a/src/main/java/com/android/tools/r8/AssertionsConfiguration.java
+++ b/src/main/java/com/android/tools/r8/AssertionsConfiguration.java
@@ -4,8 +4,7 @@
package com.android.tools.r8;
-import java.util.ArrayList;
-import java.util.List;
+import com.android.tools.r8.utils.Reporter;
@Keep
public class AssertionsConfiguration {
@@ -24,55 +23,37 @@
PASSTHROUGH
}
- public enum ConfigurationType {
+ public enum AssertionTransformationScope {
ALL,
PACKAGE,
CLASS
}
- public static class ConfigurationEntry {
- private final AssertionTransformation transformation;
- private final ConfigurationType type;
- private final String value;
+ private final AssertionTransformation transformation;
+ private final AssertionTransformationScope scope;
+ private final String value;
- private ConfigurationEntry(
- AssertionTransformation transformation, ConfigurationType type, String value) {
- assert value != null || type == ConfigurationType.ALL;
- this.transformation = transformation;
- this.type = type;
- this.value = value;
- }
-
- public AssertionTransformation getTransformation() {
- return transformation;
- }
-
- public ConfigurationType getType() {
- return type;
- }
-
- public String getValue() {
- return value;
- }
+ AssertionsConfiguration(
+ AssertionTransformation transformation, AssertionTransformationScope scope, String value) {
+ this.transformation = transformation;
+ this.scope = scope;
+ this.value = value;
}
- // Methods which need to be public.
- public static class InternalAssertionConfiguration {
-
- public static List<ConfigurationEntry> getConfiguration(AssertionsConfiguration configuration) {
- return configuration.entries;
- }
+ public AssertionTransformation getTransformation() {
+ return transformation;
}
- private final List<ConfigurationEntry> entries;
-
- private AssertionsConfiguration(List<ConfigurationEntry> entries) {
- this.entries = entries;
+ public AssertionTransformationScope getScope() {
+ return scope;
}
- static AssertionsConfiguration.Builder builder(AssertionsConfiguration previous) {
- return new AssertionsConfiguration.Builder(
- previous != null ? previous.entries : new ArrayList<>());
+ public String getValue() {
+ return value;
+ }
+
+ static AssertionsConfiguration.Builder builder(Reporter reporter) {
+ return new AssertionsConfiguration.Builder(reporter);
}
/**
@@ -83,33 +64,19 @@
*/
@Keep
public static class Builder {
- private final List<ConfigurationEntry> entries;
+ Reporter reporter;
+ private AssertionTransformation transformation;
+ private AssertionTransformationScope scope;
+ private String value;
- private Builder(List<ConfigurationEntry> previousEntries) {
- assert previousEntries != null;
- this.entries = previousEntries;
- }
-
- private void addEntry(
- AssertionTransformation transformation, ConfigurationType type, String value) {
- entries.add(new ConfigurationEntry(transformation, type, value));
+ private Builder(Reporter reporter) {
+ this.reporter = reporter;
}
/** Set how to handle javac generated assertion code. */
public AssertionsConfiguration.Builder setTransformation(
AssertionTransformation transformation) {
- addEntry(transformation, ConfigurationType.ALL, null);
- return this;
- }
-
- AssertionsConfiguration.Builder setDefault(AssertionTransformation transformation) {
- // Add the default by inserting a transform all entry at the beginning of the list, if there
- // isn't already one.
- ConfigurationEntry defaultEntry =
- new ConfigurationEntry(transformation, ConfigurationType.ALL, null);
- if (entries.size() == 0 || entries.get(0).type != ConfigurationType.ALL) {
- entries.listIterator().add(defaultEntry);
- }
+ this.transformation = transformation;
return this;
}
@@ -117,7 +84,7 @@
* Unconditionally enable javac generated assertion code in all packages and classes. This
* corresponds to passing <code>-enableassertions</code> or <code>-ea</code> to the java CLI.
*/
- public AssertionsConfiguration.Builder enable() {
+ public AssertionsConfiguration.Builder setEnable() {
setTransformation(AssertionTransformation.ENABLE);
return this;
}
@@ -126,86 +93,145 @@
* Disable the javac generated assertion code in all packages and classes. This corresponds to
* passing <code>-disableassertions</code> or <code>-da</code> to the java CLI.
*/
- public AssertionsConfiguration.Builder disable() {
+ public AssertionsConfiguration.Builder setDisable() {
setTransformation(AssertionTransformation.DISABLE);
return this;
}
/** Passthrough of the javac generated assertion code in all packages and classes. */
- public AssertionsConfiguration.Builder passthrough() {
+ public AssertionsConfiguration.Builder setPassthrough() {
setTransformation(AssertionTransformation.PASSTHROUGH);
return this;
}
- /** Set how to handle javac generated assertion code in package and all subpackages. */
- public AssertionsConfiguration.Builder setTransformationForPackage(
- String packageName, AssertionTransformation transformation) {
- addEntry(transformation, ConfigurationType.PACKAGE, packageName);
+ public AssertionsConfiguration.Builder setScopeAll() {
+ this.scope = AssertionTransformationScope.ALL;
+ this.value = null;
return this;
}
/**
- * Unconditionally enable javac generated assertion code in package <code>packageName</code> and
- * all subpackages. This corresponds to passing <code>-enableassertions:packageName...</code> or
- * <code>-ea:packageName...</code> to the java CLI.
+ * Apply the specified transformation in package <code>packageName</code> and all subpackages.
+ * If <code>packageName</code> is the empty string, this specifies that the transformation is
+ * applied ion the unnamed package.
*
- * <p>If <code>packageName</code> is the empty string, assertions are enabled in the unnamed
- * package, which corresponds to passing <code>-enableassertions:...</code> or <code>-ea:...
- * </code> to the java CLI.
- */
- public AssertionsConfiguration.Builder enableForPackage(String packageName) {
- return setTransformationForPackage(packageName, AssertionTransformation.ENABLE);
- }
-
- /**
- * Disable the javac generated assertion code in package <code>packageName</code> and all
- * subpackages. This corresponds to passing <code>-disableassertions:packageName...</code> or
- * <code>-da:packageName...</code> to the java CLI.
+ * <p>If the transformation is 'enable' this corresponds to passing <code>
+ * -enableassertions:packageName...</code> or <code>-ea:packageName...</code> to the java CLI.
*
- * <p>If <code>packageName</code> is the empty string assertions are disabled in the unnamed
- * package, which corresponds to passing <code>-disableassertions:...</code> or <code>-da:...
- * </code> to the java CLI.
+ * <p>If the transformation is 'disable' this corresponds to passing <code>
+ * -disableassertions:packageName...</code> or <code>-da:packageName...</code> to the java CLI.
*/
- public AssertionsConfiguration.Builder disableForPackage(String packageName) {
- return setTransformationForPackage(packageName, AssertionTransformation.DISABLE);
- }
-
- public AssertionsConfiguration.Builder passthroughForPackage(String packageName) {
- return setTransformationForPackage(packageName, AssertionTransformation.PASSTHROUGH);
- }
-
- /** Set how to handle javac generated assertion code in class. */
- public AssertionsConfiguration.Builder setTransformationForClass(
- String className, AssertionTransformation transformation) {
- addEntry(transformation, ConfigurationType.CLASS, className);
+ public AssertionsConfiguration.Builder setScopePackage(String packageName) {
+ this.scope = AssertionTransformationScope.PACKAGE;
+ this.value = packageName;
return this;
}
/**
- * Unconditionally enable javac generated assertion in class <code>className</code>. This
- * corresponds to passing <code> -enableassertions:className</code> or <code>-ea:className
- * </code> to the java CLI.
+ * Apply the specified transformation in class <code>className</code>.
+ *
+ * <p>If the transformation is 'enable' this corresponds to passing <code>
+ * -enableassertions:className</code> or <code>-ea:className...</code> to the java CLI.
+ *
+ * <p>If the transformation is 'disable' this corresponds to passing <code>
+ * -disableassertions:className</code> or <code>-da:className</code> to the java CLI.
*/
- public AssertionsConfiguration.Builder enableForClass(String className) {
- return setTransformationForClass(className, AssertionTransformation.ENABLE);
- }
-
- /**
- * Disable the javac generated assertion code in class <code>className</code>. This corresponds
- * to passing <code> -disableassertions:className</code> or <code>-da:className</code> to the
- * java CLI.
- */
- public AssertionsConfiguration.Builder disableForClass(String className) {
- return setTransformationForClass(className, AssertionTransformation.DISABLE);
- }
-
- public AssertionsConfiguration.Builder passthroughForClass(String className) {
- return setTransformationForClass(className, AssertionTransformation.PASSTHROUGH);
+ public AssertionsConfiguration.Builder setScopeClass(String className) {
+ this.scope = AssertionTransformationScope.CLASS;
+ this.value = className;
+ return this;
}
/** Build and return the {@link AssertionsConfiguration}. */
public AssertionsConfiguration build() {
- return new AssertionsConfiguration(entries);
+ if (transformation == null) {
+ reporter.error("No transformation specified for building AccertionConfiguration");
+ }
+ if (scope == null) {
+ reporter.error("No scope specified for building AccertionConfiguration");
+ }
+ if (scope == AssertionTransformationScope.PACKAGE && value == null) {
+ reporter.error("No package name specified for building AccertionConfiguration");
+ }
+ if (scope == AssertionTransformationScope.CLASS && value == null) {
+ reporter.error("No class name specified for building AccertionConfiguration");
+ }
+ return new AssertionsConfiguration(transformation, scope, value);
+ }
+
+ /**
+ * Static helper to build an <code>AssertionConfiguration</code> which unconditionally enables
+ * javac generated assertion code in all packages and classes. To be used like this:
+ *
+ * <pre>
+ * D8Command command = D8Command.builder()
+ * .addAssertionsConfiguration(AssertionsConfiguration.Builder::enableAllAssertions)
+ * ...
+ * .build();
+ * </pre>
+ *
+ * which is a shorthand for:
+ *
+ * <pre>
+ * D8Command command = D8Command.builder()
+ * .addAssertionsConfiguration(builder -> builder.setEnabled().setScopeAll().build())
+ * ...
+ * .build();
+ * </pre>
+ */
+ public static AssertionsConfiguration enableAllAssertions(
+ AssertionsConfiguration.Builder builder) {
+ return builder.setEnable().setScopeAll().build();
+ }
+
+ /**
+ * Static helper to build an <code>AssertionConfiguration</code> which unconditionally disables
+ * javac generated assertion code in all packages and classes. To be used like this:
+ *
+ * <pre>
+ * D8Command command = D8Command.builder()
+ * .addAssertionsConfiguration(AssertionsConfiguration.Builder::disableAllAssertions)
+ * ...
+ * .build();
+ * </pre>
+ *
+ * which is a shorthand for:
+ *
+ * <pre>
+ * D8Command command = D8Command.builder()
+ * .addAssertionsConfiguration(builder -> builder.setDisabled().setScopeAll().build())
+ * ...
+ * .build();
+ * </pre>
+ */
+ public static AssertionsConfiguration disableAllAssertions(
+ AssertionsConfiguration.Builder builder) {
+ return builder.setDisable().setScopeAll().build();
+ }
+
+ /**
+ * Static helper to build an <code>AssertionConfiguration</code> which will passthrough javac
+ * generated assertion code in all packages and classes. To be used like this:
+ *
+ * <pre>
+ * D8Command command = D8Command.builder()
+ * .addAssertionsConfiguration(AssertionsConfiguration.Builder::passthroughAllAssertions)
+ * ...
+ * .build();
+ * </pre>
+ *
+ * which is a shorthand for:
+ *
+ * <pre>
+ * D8Command command = D8Command.builder()
+ * .addAssertionsConfiguration(builder -> builder.setPassthrough().setScopeAll().build())
+ * ...
+ * .build();
+ * </pre>
+ */
+ public static AssertionsConfiguration passthroughAllAssertions(
+ AssertionsConfiguration.Builder builder) {
+ return builder.setPassthrough().setScopeAll().build();
}
}
}
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index 2b94a54..fd2ecb3 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
-import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexItemFactory;
@@ -40,7 +39,7 @@
private final boolean includeClassesChecksum;
private final boolean optimizeMultidexForLinearAlloc;
private final BiPredicate<String, Long> dexClassChecksumFilter;
- private final AssertionsConfiguration assertionsConfiguration;
+ private final List<AssertionsConfiguration> assertionsConfiguration;
BaseCompilerCommand(boolean printHelp, boolean printVersion) {
super(printHelp, printVersion);
@@ -53,7 +52,7 @@
includeClassesChecksum = false;
optimizeMultidexForLinearAlloc = false;
dexClassChecksumFilter = (name, checksum) -> true;
- assertionsConfiguration = null;
+ assertionsConfiguration = new ArrayList<>();
}
BaseCompilerCommand(
@@ -67,7 +66,7 @@
boolean optimizeMultidexForLinearAlloc,
boolean includeClassesChecksum,
BiPredicate<String, Long> dexClassChecksumFilter,
- AssertionsConfiguration assertionsConfiguration) {
+ List<AssertionsConfiguration> assertionsConfiguration) {
super(app);
assert minApiLevel > 0;
assert mode != null;
@@ -135,11 +134,8 @@
return optimizeMultidexForLinearAlloc;
}
- AssertionsConfiguration getAssertionsConfiguration(
- AssertionTransformation defaultTransformation) {
- return AssertionsConfiguration.builder(assertionsConfiguration)
- .setDefault(defaultTransformation)
- .build();
+ public List<AssertionsConfiguration> getAssertionsConfiguration() {
+ return assertionsConfiguration;
}
Reporter getReporter() {
@@ -171,7 +167,7 @@
private boolean lookupLibraryBeforeProgram = true;
private boolean optimizeMultidexForLinearAlloc = false;
private BiPredicate<String, Long> dexClassChecksumFilter = (name, checksum) -> true;
- private AssertionsConfiguration assertionsConfiguration;
+ private List<AssertionsConfiguration> assertionsConfiguration = new ArrayList<>();
abstract CompilationMode defaultCompilationMode();
@@ -490,20 +486,19 @@
return includeClassesChecksum;
}
+ List<AssertionsConfiguration> getAssertionsConfiguration() {
+ return assertionsConfiguration;
+ }
+
/** Configure compile time assertion enabling through a {@link AssertionsConfiguration}. */
public B addAssertionsConfiguration(
Function<AssertionsConfiguration.Builder, AssertionsConfiguration>
assertionsConfigurationGenerator) {
- assertionsConfiguration =
- assertionsConfigurationGenerator.apply(
- AssertionsConfiguration.builder(assertionsConfiguration));
+ assertionsConfiguration.add(
+ assertionsConfigurationGenerator.apply(AssertionsConfiguration.builder(getReporter())));
return self();
}
- public AssertionsConfiguration getAssertionsConfiguration() {
- return assertionsConfiguration;
- }
-
@Override
void validate() {
Reporter reporter = getReporter();
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index d3ec378..cf5e6c8 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -159,7 +159,7 @@
final CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
- if (!AssertionsRewriter.isPassthroughAll(options.assertionsConfiguration)) {
+ if (AssertionsRewriter.isEnabled(options)) {
// Run analysis to mark all <clinit> methods having the javac generated assertion
// enabling code.
ClassInitializerAssertionEnablingAnalysis analysis =
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 2401aa5..b76e6a7 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -10,11 +10,13 @@
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.AssertionConfigurationWithDefault;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
import java.nio.file.Path;
import java.util.Collection;
+import java.util.List;
import java.util.function.BiPredicate;
/**
@@ -287,7 +289,7 @@
DesugarGraphConsumer desugarGraphConsumer,
StringConsumer desugaredLibraryKeepRuleConsumer,
DesugaredLibraryConfiguration libraryConfiguration,
- AssertionsConfiguration assertionsConfiguration,
+ List<AssertionsConfiguration> assertionsConfiguration,
DexItemFactory factory) {
super(
inputApp,
@@ -361,10 +363,11 @@
internal.desugaredLibraryConfiguration = libraryConfiguration;
internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
+ // Default is to remove all javac generated assertion code when generating dex.
assert internal.assertionsConfiguration == null;
- // Default, when no configuration is provided, is to remove all javac generated assertion
- // code when generating dex.
- internal.assertionsConfiguration = getAssertionsConfiguration(AssertionTransformation.DISABLE);
+ internal.assertionsConfiguration =
+ new AssertionConfigurationWithDefault(
+ AssertionTransformation.DISABLE, getAssertionsConfiguration());
return internal;
}
diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java
index 792f8b9..1099cf9 100644
--- a/src/main/java/com/android/tools/r8/D8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
@@ -30,7 +30,8 @@
"--classpath",
"--min-api",
"--main-dex-list",
- "--main-dex-list-output");
+ "--main-dex-list-output",
+ "--desugared-lib");
private static final String APK_EXTENSION = ".apk";
private static final String JAR_EXTENSION = ".jar";
@@ -124,6 +125,8 @@
" # merging.",
" --file-per-class # Produce a separate dex file per input class",
" --no-desugaring # Force disable desugaring.",
+ " --desugared-lib <file> # Specify desugared library configuration.",
+ " # <file> is a desugared library configuration (json).",
" --main-dex-list <file> # List of classes to place in the primary dex file.",
" --main-dex-list-output <file>",
" # Output resulting main dex list in <file>.",
@@ -245,6 +248,8 @@
builder.setIntermediate(true);
} else if (arg.equals("--no-desugaring")) {
builder.setDisableDesugaring(true);
+ } else if (arg.equals("--desugared-lib")) {
+ builder.addDesugaredLibraryConfiguration(StringResource.fromFile(Paths.get(nextArg)));
} else {
if (arg.startsWith("--")) {
builder.error(new StringDiagnostic("Unknown option: " + arg, origin));
diff --git a/src/main/java/com/android/tools/r8/DesugarGraphConsumer.java b/src/main/java/com/android/tools/r8/DesugarGraphConsumer.java
index d75b2b0..2210490 100644
--- a/src/main/java/com/android/tools/r8/DesugarGraphConsumer.java
+++ b/src/main/java/com/android/tools/r8/DesugarGraphConsumer.java
@@ -21,4 +21,11 @@
* @param dependency Origin of code that is a dependency to compile {@code dependent}.
*/
void accept(Origin dependent, Origin dependency);
+
+ /**
+ * Callback indicating no more dependency edges for the active compilation unit.
+ *
+ * <p>Note: this callback places no other guarantees on number of calls or on which threads.
+ */
+ void finished();
}
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index 50ea501..a692e1d 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.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.L8Command.USAGE_MESSAGE;
import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
import com.android.tools.r8.dex.ApplicationReader;
@@ -15,11 +16,13 @@
import com.android.tools.r8.ir.desugar.PrefixRewritingMapper;
import com.android.tools.r8.jar.CfApplicationWriter;
import com.android.tools.r8.naming.PrefixRewritingNamingLens;
+import com.android.tools.r8.origin.CommandLineOrigin;
import com.android.tools.r8.shaking.AnnotationRemover;
import com.android.tools.r8.shaking.L8TreePruner;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.SelfRetraceTest;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import java.io.IOException;
@@ -144,4 +147,31 @@
}
}
}
+
+ private static void run(String[] args) throws CompilationFailedException {
+ L8Command command = L8Command.parse(args, CommandLineOrigin.INSTANCE).build();
+ if (command.isPrintHelp()) {
+ SelfRetraceTest.test();
+ System.out.println(USAGE_MESSAGE);
+ return;
+ }
+ if (command.isPrintVersion()) {
+ System.out.println("L8 " + Version.getVersionString());
+ return;
+ }
+ run(command);
+ }
+
+ /**
+ * Command-line entry to L8.
+ *
+ * <p>See {@link L8Command#USAGE_MESSAGE} or run {@code l8 --help} for usage information.
+ */
+ public static void main(String[] args) {
+ if (args.length == 0) {
+ System.err.println(USAGE_MESSAGE);
+ System.exit(ExceptionUtils.STATUS_ERROR);
+ }
+ ExceptionUtils.withMainProgramHandler(() -> run(args));
+ }
}
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 1346623..1ddd338 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -10,10 +10,12 @@
import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.AssertionConfigurationWithDefault;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
+import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
@@ -24,9 +26,11 @@
@Keep
public final class L8Command extends BaseCompilerCommand {
+ static final String USAGE_MESSAGE = R8CommandParser.USAGE_MESSAGE;
+
private final D8Command d8Command;
private final R8Command r8Command;
- private final com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration libraryConfiguration;
+ private final DesugaredLibraryConfiguration libraryConfiguration;
private final DexItemFactory factory;
boolean isShrinking() {
@@ -41,6 +45,33 @@
return r8Command;
}
+ /**
+ * Parse the L8 command-line.
+ *
+ * <p>Parsing will set the supplied options or their default value if they have any.
+ *
+ * @param args Command-line arguments array.
+ * @param origin Origin description of the command-line arguments.
+ * @return L8 command builder with state set up according to parsed command line.
+ */
+ public static Builder parse(String[] args, Origin origin) {
+ return L8CommandParser.parse(args, origin);
+ }
+
+ /**
+ * Parse the L8 command-line.
+ *
+ * <p>Parsing will set the supplied options or their default value if they have any.
+ *
+ * @param args Command-line arguments array.
+ * @param origin Origin description of the command-line arguments.
+ * @param handler Custom defined diagnostics handler.
+ * @return L8 command builder with state set up according to parsed command line.
+ */
+ public static Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
+ return L8CommandParser.parse(args, origin, handler);
+ }
+
private L8Command(
R8Command r8Command,
D8Command d8Command,
@@ -63,7 +94,7 @@
false,
false,
(name, checksum) -> true,
- AssertionsConfiguration.builder(null).build());
+ ImmutableList.of());
this.d8Command = d8Command;
this.r8Command = r8Command;
this.libraryConfiguration = libraryConfiguration;
@@ -142,10 +173,11 @@
// TODO(134732760): This is still work in progress.
internal.desugaredLibraryConfiguration = libraryConfiguration;
+ // Default is to remove all javac generated assertion code when generating dex.
assert internal.assertionsConfiguration == null;
- // Default, when no configuration is provided, is to remove all javac generated assertion
- // code when generating dex.
- internal.assertionsConfiguration = getAssertionsConfiguration(AssertionTransformation.DISABLE);
+ internal.assertionsConfiguration =
+ new AssertionConfigurationWithDefault(
+ AssertionTransformation.DISABLE, getAssertionsConfiguration());
return internal;
}
diff --git a/src/main/java/com/android/tools/r8/L8CommandParser.java b/src/main/java/com/android/tools/r8/L8CommandParser.java
new file mode 100644
index 0000000..1dc83c1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/L8CommandParser.java
@@ -0,0 +1,164 @@
+// 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.D8CommandParser.OrderedClassFileResourceProvider;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.FlagFile;
+import com.android.tools.r8.utils.StringDiagnostic;
+import com.google.common.collect.ImmutableSet;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Set;
+
+public class L8CommandParser extends BaseCompilerCommandParser {
+
+ private static final Set<String> OPTIONS_WITH_PARAMETER =
+ ImmutableSet.of("--output", "--lib", "--min-api", "--desugared-lib");
+
+ public static void main(String[] args) throws CompilationFailedException {
+ L8Command command = parse(args, Origin.root()).build();
+ if (command.isPrintHelp()) {
+ System.out.println(USAGE_MESSAGE);
+ System.exit(1);
+ }
+ L8.run(command);
+ }
+
+ static final String USAGE_MESSAGE =
+ String.join(
+ "\n",
+ Arrays.asList(
+ "Usage: l8 [options] <input-files>",
+ " where <input-files> are any combination of dex, class, zip, jar, or apk files",
+ " and options are:",
+ " --debug # Compile with debugging information (default).",
+ " --release # Compile without debugging information.",
+ " --output <file> # Output result in <outfile>.",
+ " # <file> must be an existing directory or a zip file.",
+ " --lib <file|jdk-home> # Add <file|jdk-home> as a library resource.",
+ " --min-api <number> # Minimum Android API level compatibility, default: "
+ + AndroidApiLevel.getDefault().getLevel()
+ + ".",
+ " --pg-conf <file> # Proguard configuration <file>.",
+ " --desugared-lib <file> # Specify desugared library configuration.",
+ " # <file> is a desugared library configuration (json).",
+ " --version # Print the version of l8.",
+ " --help # Print this message."));
+
+ /**
+ * Parse the D8 command-line.
+ *
+ * <p>Parsing will set the supplied options or their default value if they have any.
+ *
+ * @param args Command-line arguments array.
+ * @param origin Origin description of the command-line arguments.
+ * @return D8 command builder with state set up according to parsed command line.
+ */
+ public static L8Command.Builder parse(String[] args, Origin origin) {
+ return parse(args, origin, L8Command.builder());
+ }
+
+ /**
+ * Parse the D8 command-line.
+ *
+ * <p>Parsing will set the supplied options or their default value if they have any.
+ *
+ * @param args Command-line arguments array.
+ * @param origin Origin description of the command-line arguments.
+ * @param handler Custom defined diagnostics handler.
+ * @return D8 command builder with state set up according to parsed command line.
+ */
+ public static L8Command.Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
+ return parse(args, origin, L8Command.builder(handler));
+ }
+
+ private static L8Command.Builder parse(String[] args, Origin origin, L8Command.Builder builder) {
+ CompilationMode compilationMode = null;
+ Path outputPath = null;
+ OutputMode outputMode = null;
+ boolean hasDefinedApiLevel = false;
+ OrderedClassFileResourceProvider.Builder classpathBuilder =
+ OrderedClassFileResourceProvider.builder();
+ String[] expandedArgs = FlagFile.expandFlagFiles(args, builder);
+ for (int i = 0; i < expandedArgs.length; i++) {
+ String arg = expandedArgs[i].trim();
+ String nextArg = null;
+ if (OPTIONS_WITH_PARAMETER.contains(arg)) {
+ if (++i < expandedArgs.length) {
+ nextArg = expandedArgs[i];
+ } else {
+ builder.error(
+ new StringDiagnostic("Missing parameter for " + expandedArgs[i - 1] + ".", origin));
+ break;
+ }
+ }
+ if (arg.length() == 0) {
+ continue;
+ } else if (arg.equals("--help")) {
+ builder.setPrintHelp(true);
+ } else if (arg.equals("--version")) {
+ builder.setPrintVersion(true);
+ } else if (arg.equals("--debug")) {
+ if (compilationMode == CompilationMode.RELEASE) {
+ builder.error(
+ new StringDiagnostic("Cannot compile in both --debug and --release mode.", origin));
+ continue;
+ }
+ compilationMode = CompilationMode.DEBUG;
+ } else if (arg.equals("--release")) {
+ if (compilationMode == CompilationMode.DEBUG) {
+ builder.error(
+ new StringDiagnostic("Cannot compile in both --debug and --release mode.", origin));
+ continue;
+ }
+ compilationMode = CompilationMode.RELEASE;
+ } else if (arg.equals("--output")) {
+ if (outputPath != null) {
+ builder.error(
+ new StringDiagnostic(
+ "Cannot output both to '" + outputPath.toString() + "' and '" + nextArg + "'",
+ origin));
+ continue;
+ }
+ outputPath = Paths.get(nextArg);
+ } else if (arg.equals("--min-api")) {
+ if (hasDefinedApiLevel) {
+ builder.error(new StringDiagnostic("Cannot set multiple --min-api options", origin));
+ } else {
+ parseMinApi(builder, nextArg, origin);
+ hasDefinedApiLevel = true;
+ }
+ } else if (arg.equals("--lib")) {
+ addLibraryArgument(builder, origin, nextArg);
+ } else if (arg.equals("--pg-conf")) {
+ builder.addProguardConfigurationFiles(Paths.get(nextArg));
+ } else if (arg.equals("--desugared-lib")) {
+ builder.addDesugaredLibraryConfiguration(StringResource.fromFile(Paths.get(nextArg)));
+ } else {
+ if (arg.startsWith("--")) {
+ builder.error(new StringDiagnostic("Unknown option: " + arg, origin));
+ continue;
+ }
+ builder.addProgramFiles(Paths.get(arg));
+ }
+ }
+ if (!classpathBuilder.isEmpty()) {
+ builder.addClasspathResourceProvider(classpathBuilder.build());
+ }
+ if (compilationMode != null) {
+ builder.setMode(compilationMode);
+ }
+ if (outputMode == null) {
+ outputMode = OutputMode.DexIndexed;
+ }
+ if (outputPath == null) {
+ outputPath = Paths.get(".");
+ }
+ return builder.setOutput(outputPath, outputMode);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 8c26650..dbe1572 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -807,7 +807,7 @@
if (appView.options().enableInitializedClassesInInstanceMethodsAnalysis) {
enqueuer.registerAnalysis(new InitializedClassesInInstanceMethodsAnalysis(appView));
}
- if (!AssertionsRewriter.isPassthroughAll(appView.options().assertionsConfiguration)) {
+ if (AssertionsRewriter.isEnabled(appView.options())) {
enqueuer.registerAnalysis(
new ClassInitializerAssertionEnablingAnalysis(
appView.dexItemFactory(), OptimizationFeedbackSimple.getInstance()));
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 265fa38..187e095 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.shaking.ProguardConfigurationSourceStrings;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.AssertionConfigurationWithDefault;
import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -720,7 +721,7 @@
StringConsumer desugaredLibraryKeepRuleConsumer,
DesugaredLibraryConfiguration libraryConfiguration,
FeatureSplitConfiguration featureSplitConfiguration,
- AssertionsConfiguration assertionsConfiguration) {
+ List<AssertionsConfiguration> assertionsConfiguration) {
super(
inputApp,
mode,
@@ -870,14 +871,14 @@
internal.syntheticProguardRulesConsumer = syntheticProguardRulesConsumer;
+ // Default is to remove all javac generated assertion code when generating dex.
assert internal.assertionsConfiguration == null;
- // Default, when no configuration is provided, is to remove all javac generated assertion
- // code when generating dex and leave it when generating class files.
internal.assertionsConfiguration =
- getAssertionsConfiguration(
- internal.isGeneratingClassFiles()
+ new AssertionConfigurationWithDefault(
+ getProgramConsumer() instanceof ClassFileConsumer
? AssertionTransformation.PASSTHROUGH
- : AssertionTransformation.DISABLE);
+ : AssertionTransformation.DISABLE,
+ getAssertionsConfiguration());
// When generating class files the build is "intermediate" and we cannot pollute the namespace
// with the a hard-coded outline class. Doing so would prohibit subsequent merging of two
diff --git a/src/main/java/com/android/tools/r8/R8CommandParser.java b/src/main/java/com/android/tools/r8/R8CommandParser.java
index b0acdb6..dd9ab8e 100644
--- a/src/main/java/com/android/tools/r8/R8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/R8CommandParser.java
@@ -25,7 +25,8 @@
"--main-dex-list",
"--main-dex-list-output",
"--pg-conf",
- "--pg-map-output");
+ "--pg-map-output",
+ "--desugared-lib");
public static void main(String[] args) throws CompilationFailedException {
R8Command command = parse(args, Origin.root()).build();
@@ -65,6 +66,8 @@
+ ".",
" --pg-conf <file> # Proguard configuration <file>.",
" --pg-map-output <file> # Output the resulting name and line mapping to <file>.",
+ " --desugared-lib <file> # Specify desugared library configuration.",
+ " # <file> is a desugared library configuration (json).",
" --no-tree-shaking # Force disable tree shaking of unreachable classes.",
" --no-minification # Force disable minification of names.",
" --no-data-resources # Ignore all data resources.",
@@ -206,6 +209,8 @@
builder.addProguardConfigurationFiles(Paths.get(nextArg));
} else if (arg.equals("--pg-map-output")) {
builder.setProguardMapOutputPath(Paths.get(nextArg));
+ } else if (arg.equals("--desugared-lib")) {
+ builder.addDesugaredLibraryConfiguration(StringResource.fromFile(Paths.get(nextArg)));
} else if (arg.equals("--no-data-resources")) {
state.includeDataResources = false;
} else {
diff --git a/src/main/java/com/android/tools/r8/SwissArmyKnife.java b/src/main/java/com/android/tools/r8/SwissArmyKnife.java
index f54233b..d13081c 100644
--- a/src/main/java/com/android/tools/r8/SwissArmyKnife.java
+++ b/src/main/java/com/android/tools/r8/SwissArmyKnife.java
@@ -76,6 +76,9 @@
case "r8":
R8.main(shift(args));
break;
+ case "l8":
+ L8.main(shift(args));
+ break;
default:
runDefault(args);
break;
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
index afdc15b..4656408 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -212,7 +212,7 @@
// default interface methods, it is expected they are targeted with invoke-direct.
return this.itf && desugaringEnabled ? Type.DIRECT : Type.SUPER;
}
- if (!encodedMethod.isVirtualMethod()) {
+ if (!encodedMethod.isNonPrivateVirtualMethod()) {
return Type.DIRECT;
}
if (encodedMethod.accessFlags.isFinal()) {
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 7ba04b5..993363c 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -49,8 +49,8 @@
import com.android.tools.r8.utils.ThreadUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ObjectArrays;
-import it.unimi.dsi.fastutil.objects.Object2LongMap;
-import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Reference2LongMap;
+import it.unimi.dsi.fastutil.objects.Reference2LongOpenHashMap;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
@@ -204,14 +204,15 @@
*/
private void encodeChecksums(Iterable<VirtualFile> files) {
List<DexProgramClass> classes = application.classes();
- Object2LongMap<String> inputChecksums = new Object2LongOpenHashMap<>(classes.size());
+ Reference2LongMap<DexString> inputChecksums = new Reference2LongOpenHashMap<>(classes.size());
for (DexProgramClass clazz : classes) {
- inputChecksums.put(clazz.getType().descriptor.toASCIIString(), clazz.getChecksum());
+ inputChecksums.put(clazz.getType().descriptor, clazz.getChecksum());
}
for (VirtualFile file : files) {
ClassesChecksum toWrite = new ClassesChecksum();
- for (String desc : file.getClassDescriptors()) {
- toWrite.addChecksum(desc, inputChecksums.getLong(desc));
+ for (DexProgramClass clazz : file.classes()) {
+ DexString desc = clazz.type.descriptor;
+ toWrite.addChecksum(desc.toString(), inputChecksums.getLong(desc));
}
file.injectString(application.dexItemFactory.createString(toWrite.toJsonString()));
}
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java
index d5e338e..c69d8e0 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -716,7 +716,7 @@
Long checksum = null;
if (checksums != null && !checksums.isEmpty()) {
- String desc = type.descriptor.toASCIIString();
+ String desc = type.toDescriptorString();
checksum = checksums.getOrDefault(desc, null);
if (!options.dexClassChecksumFilter.test(desc, checksum)) {
continue;
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index 86b2cb8..ad19ed4 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -256,16 +256,17 @@
/**
* Lookup virtual method starting in type and following the super chain.
- * <p>
- * This method will resolve the method on the holder of {@code method} and only return a
- * non-null value if the result of resolution was a non-static, non-private method.
+ *
+ * <p>This method will resolve the method on the holder of {@code method} and only return a
+ * non-null value if the result of resolution was a virtual target.
+ *
+ * <p>TODO(b/140204899): Delete this method as it does resolution and not a "lookup of targets".
*/
public DexEncodedMethod lookupVirtualTarget(DexType type, DexMethod method) {
assert checkIfObsolete();
assert type.isClassType() || type.isArrayType();
ResolutionResult resolutionResult = resolveMethod(type, method);
- DexEncodedMethod target = resolutionResult.getSingleTarget();
- return target == null || target.isVirtualMethod() ? target : null;
+ return resolutionResult.isVirtualTarget() ? resolutionResult.getSingleTarget() : null;
}
/**
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index dbd455b..a928529 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -221,11 +221,11 @@
}
public OptionalBool isLibraryMethodOverride() {
- return isVirtualMethod() ? isLibraryMethodOverride : OptionalBool.FALSE;
+ return isNonPrivateVirtualMethod() ? isLibraryMethodOverride : OptionalBool.FALSE;
}
public void setLibraryMethodOverride(OptionalBool isLibraryMethodOverride) {
- assert isVirtualMethod();
+ assert isNonPrivateVirtualMethod();
assert !isLibraryMethodOverride.isUnknown();
assert isLibraryMethodOverride.isPossiblyFalse()
|| this.isLibraryMethodOverride.isPossiblyTrue()
@@ -287,12 +287,19 @@
}
/**
- * Returns true if this method can be invoked via invoke-virtual, invoke-super or
- * invoke-interface.
+ * Returns true if this method can be invoked via invoke-virtual/interface.
+ *
+ * <p>Note that also private methods can be the target of a virtual invoke. In such cases, the
+ * validity of the invoke depends on the access granted to the call site.
*/
public boolean isVirtualMethod() {
checkIfObsolete();
- return !accessFlags.isStatic() && !accessFlags.isPrivate() && !accessFlags.isConstructor();
+ return !accessFlags.isStatic() && !accessFlags.isConstructor();
+ }
+
+ public boolean isNonPrivateVirtualMethod() {
+ checkIfObsolete();
+ return !isPrivateMethod() && isVirtualMethod();
}
/**
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index bbed9bc..9a8a73c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -82,7 +82,14 @@
public boolean isAlwaysNull(AppView<AppInfoWithLiveness> appView) {
if (isClassType()) {
DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(this));
- return clazz != null && !appView.appInfo().isInstantiatedDirectlyOrIndirectly(clazz);
+ if (clazz == null) {
+ return false;
+ }
+ if (appView.options().enableUninstantiatedTypeOptimizationForInterfaces) {
+ return !appView.appInfo().isInstantiatedDirectlyOrIndirectly(clazz);
+ } else {
+ return !clazz.isInterface() && !appView.appInfo().isInstantiatedDirectlyOrIndirectly(clazz);
+ }
}
return false;
}
diff --git a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
index 3080960..4854741 100644
--- a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
@@ -3,10 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
-import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
-
import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.SetUtils;
import com.google.common.collect.Sets;
import java.util.Collection;
@@ -57,9 +54,9 @@
public abstract boolean isAccessibleForVirtualDispatchFrom(
DexProgramClass context, AppInfoWithSubtyping appInfo);
- public abstract boolean isValidVirtualTarget(InternalOptions options);
-
- public abstract boolean isValidVirtualTargetForDynamicDispatch();
+ // TODO(b/145187573): Remove this and use proper access checks.
+ @Deprecated
+ public abstract boolean isVirtualTarget();
/** Lookup the single target of an invoke-special on this resolution result if possible. */
public abstract DexEncodedMethod lookupInvokeSpecialTarget(
@@ -87,12 +84,6 @@
private final DexClass resolvedHolder;
private final DexEncodedMethod resolvedMethod;
- public static boolean isValidVirtualTarget(InternalOptions options, DexEncodedMethod target) {
- return options.canUseNestBasedAccess()
- ? (!target.accessFlags.isStatic() && !target.accessFlags.isConstructor())
- : target.isVirtualMethod();
- }
-
public SingleResolutionResult(
DexClass initialResolutionHolder,
DexClass resolvedHolder,
@@ -133,20 +124,11 @@
@Override
public boolean isAccessibleForVirtualDispatchFrom(
DexProgramClass context, AppInfoWithSubtyping appInfo) {
- // If a private method is accessible (which implies it is via its nest), then it is a valid
- // virtual dispatch target if non-static.
- return isAccessibleFrom(context, appInfo)
- && (resolvedMethod.isVirtualMethod()
- || (resolvedMethod.isPrivateMethod() && !resolvedMethod.isStatic()));
+ return resolvedMethod.isVirtualMethod() && isAccessibleFrom(context, appInfo);
}
@Override
- public boolean isValidVirtualTarget(InternalOptions options) {
- return isValidVirtualTarget(options, resolvedMethod);
- }
-
- @Override
- public boolean isValidVirtualTargetForDynamicDispatch() {
+ public boolean isVirtualTarget() {
return resolvedMethod.isVirtualMethod();
}
@@ -301,12 +283,16 @@
@Override
// TODO(b/140204899): Leverage refined receiver type if available.
public Set<DexEncodedMethod> lookupVirtualTargets(AppInfoWithSubtyping appInfo) {
- assert isValidVirtualTarget(appInfo.app().options);
+ if (resolvedMethod.isPrivateMethod()) {
+ // If the resolved reference is private there is no dispatch.
+ // This is assuming that the method is accessible, which implies self/nest access.
+ return Collections.singleton(resolvedMethod);
+ }
+ assert resolvedMethod.isNonPrivateVirtualMethod();
// First add the target for receiver type method.type.
- DexEncodedMethod encodedMethod = getSingleTarget();
- Set<DexEncodedMethod> result = SetUtils.newIdentityHashSet(encodedMethod);
+ Set<DexEncodedMethod> result = SetUtils.newIdentityHashSet(resolvedMethod);
// Add all matching targets from the subclass hierarchy.
- DexMethod method = encodedMethod.method;
+ DexMethod method = resolvedMethod.method;
// TODO(b/140204899): Instead of subtypes of holder, we could iterate subtypes of refined
// receiver type if available.
for (DexType type : appInfo.subtypes(method.holder)) {
@@ -314,7 +300,7 @@
if (!clazz.isInterface()) {
ResolutionResult methods = appInfo.resolveMethodOnClass(clazz, method);
DexEncodedMethod target = methods.getSingleTarget();
- if (target != null && target.isVirtualMethod()) {
+ if (target != null && target.isNonPrivateVirtualMethod()) {
result.add(target);
}
}
@@ -325,43 +311,44 @@
@Override
// TODO(b/140204899): Leverage refined receiver type if available.
public Set<DexEncodedMethod> lookupInterfaceTargets(AppInfoWithSubtyping appInfo) {
- assert isValidVirtualTarget(appInfo.app().options);
+ if (resolvedMethod.isPrivateMethod()) {
+ // If the resolved reference is private there is no dispatch.
+ // This is assuming that the method is accessible, which implies self/nest access.
+ assert resolvedMethod.hasCode();
+ return Collections.singleton(resolvedMethod);
+ }
+ assert resolvedMethod.isNonPrivateVirtualMethod();
Set<DexEncodedMethod> result = Sets.newIdentityHashSet();
- if (isSingleResolution()) {
- // Add default interface methods to the list of targets.
- //
- // This helps to make sure we take into account synthesized lambda classes
- // that we are not aware of. Like in the following example, we know that all
- // classes, XX in this case, override B::bar(), but there are also synthesized
- // classes for lambda which don't, so we still need default method to be live.
- //
- // public static void main(String[] args) {
- // X x = () -> {};
- // x.bar();
- // }
- //
- // interface X {
- // void foo();
- // default void bar() { }
- // }
- //
- // class XX implements X {
- // public void foo() { }
- // public void bar() { }
- // }
- //
- DexEncodedMethod singleTarget = getSingleTarget();
- if (singleTarget.hasCode()) {
- DexProgramClass holder =
- asProgramClassOrNull(appInfo.definitionFor(singleTarget.method.holder));
- if (appInfo.hasAnyInstantiatedLambdas(holder)) {
- result.add(singleTarget);
- }
+ // Add default interface methods to the list of targets.
+ //
+ // This helps to make sure we take into account synthesized lambda classes
+ // that we are not aware of. Like in the following example, we know that all
+ // classes, XX in this case, override B::bar(), but there are also synthesized
+ // classes for lambda which don't, so we still need default method to be live.
+ //
+ // public static void main(String[] args) {
+ // X x = () -> {};
+ // x.bar();
+ // }
+ //
+ // interface X {
+ // void foo();
+ // default void bar() { }
+ // }
+ //
+ // class XX implements X {
+ // public void foo() { }
+ // public void bar() { }
+ // }
+ //
+ if (resolvedMethod.hasCode()) {
+ DexProgramClass holder = resolvedHolder.asProgramClass();
+ if (appInfo.hasAnyInstantiatedLambdas(holder)) {
+ result.add(resolvedMethod);
}
}
- DexEncodedMethod encodedMethod = getSingleTarget();
- DexMethod method = encodedMethod.method;
+ DexMethod method = resolvedMethod.method;
Consumer<DexEncodedMethod> addIfNotAbstract =
m -> {
if (!m.accessFlags.isAbstract()) {
@@ -451,12 +438,7 @@
}
@Override
- public boolean isValidVirtualTarget(InternalOptions options) {
- return true;
- }
-
- @Override
- public boolean isValidVirtualTargetForDynamicDispatch() {
+ public boolean isVirtualTarget() {
return true;
}
}
@@ -490,12 +472,7 @@
}
@Override
- public boolean isValidVirtualTarget(InternalOptions options) {
- return false;
- }
-
- @Override
- public boolean isValidVirtualTargetForDynamicDispatch() {
+ public boolean isVirtualTarget() {
return false;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
index d1c82d4..71750f1 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
@@ -42,7 +42,7 @@
*/
@Override
public boolean registerConstClass(DexType type) {
- if (references.isDynamicMethod(currentMethod)) {
+ if (references.isDynamicMethod(getContextMethod())) {
return false;
}
return super.registerConstClass(type);
@@ -57,7 +57,7 @@
*/
@Override
public boolean registerStaticFieldRead(DexField field) {
- if (references.isDynamicMethod(currentMethod)) {
+ if (references.isDynamicMethod(getContextMethod())) {
return false;
}
return super.registerStaticFieldRead(field);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
index c7939b5..ade31ca 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
@@ -180,7 +180,7 @@
method -> {
ResolutionResult resolution =
appView.appInfo().resolveMethod(method.holder, method, isInterface);
- if (resolution.isValidVirtualTarget(appView.options())) {
+ if (resolution.isVirtualTarget()) {
return resolution.lookupVirtualDispatchTargets(isInterface, appView.appInfo());
}
return null;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CodeOptimization.java b/src/main/java/com/android/tools/r8/ir/conversion/CodeOptimization.java
index 5d73cd5..8a40db4 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CodeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CodeOptimization.java
@@ -6,9 +6,14 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.google.common.collect.ImmutableList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.function.Consumer;
/**
- * An abstraction of {@link IRCode}-level optimization.
+ * An abstraction of {@link IRCode}-level optimization, which may retrieve info from
+ * {@link AppView}; update {@link OptimizationFeedback}; or utilize {@link MethodProcessor}.
*/
public interface CodeOptimization {
@@ -17,9 +22,61 @@
// rewriting every affected optimization.
// Note that a code optimization can be a collection of other code optimizations.
// In that way, IRConverter will serve as the default full processing of all optimizations.
- void optimize(
- AppView<?> appView,
- IRCode code,
- OptimizationFeedback feedback,
- MethodProcessor methodProcessor);
+ void optimize(IRCode code, OptimizationFeedback feedback, MethodProcessor methodProcessor);
+
+ static CodeOptimization from(Consumer<IRCode> consumer) {
+ return (code, feedback, methodProcessor) -> {
+ consumer.accept(code);
+ };
+ }
+
+ static CodeOptimization sequence(CodeOptimization... codeOptimizations) {
+ return sequence(Arrays.asList(codeOptimizations));
+ }
+
+ static CodeOptimization sequence(Collection<CodeOptimization> codeOptimizations) {
+ return (code, feedback, methodProcessor) -> {
+ for (CodeOptimization codeOptimization : codeOptimizations) {
+ codeOptimization.optimize(code, feedback, methodProcessor);
+ }
+ };
+ }
+
+ /**
+ * Builder for {@link CodeOptimization}.
+ *
+ * Users can append either {@link CodeOptimization}, or simply {@link IRCode} consumer.
+ *
+ * Note that the order of everything that is appended through the builder matters.
+ */
+ public static class Builder {
+ private ImmutableList.Builder<CodeOptimization> processingQueue;
+
+ private Builder() {
+ processingQueue = ImmutableList.builder();
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public Builder addIRCodeConsumer(Consumer<IRCode> consumer) {
+ processingQueue.add(from(consumer));
+ return this;
+ }
+
+ public Builder addCodeOptimization(CodeOptimization optimization) {
+ processingQueue.add(optimization);
+ return this;
+ }
+
+ public CodeOptimization build() {
+ return (code, feedback, methodProcessor) -> {
+ processingQueue
+ .build()
+ .forEach(codeOptimization ->
+ codeOptimization.optimize(code, feedback, methodProcessor));
+ };
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 00d2f79..b3a6d66 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -1092,12 +1092,11 @@
feedback.markProcessed(method, ConstraintWithTarget.NEVER);
return;
}
- optimize(appView, code, feedback, methodProcessor);
+ optimize(code, feedback, methodProcessor);
}
// TODO(b/140766440): Convert all sub steps an implementer of CodeOptimization
private void optimize(
- AppView<?> appView,
IRCode code,
OptimizationFeedback feedback,
MethodProcessor methodProcessor) {
@@ -1218,7 +1217,7 @@
stringOptimizer.computeTrivialOperationsOnConstString(code);
stringOptimizer.removeTrivialConversions(code);
if (libraryMethodOptimizer != null) {
- libraryMethodOptimizer.optimize(appView, code, feedback, methodProcessor);
+ libraryMethodOptimizer.optimize(code, feedback, methodProcessor);
}
assert code.isConsistentSSA();
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index bf556c8..dc425e4 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -643,7 +643,8 @@
}
private boolean canInvokeTargetWithInvokeVirtual(DexEncodedMethod target) {
- return target.isVirtualMethod() && appView.isInterface(target.method.holder).isFalse();
+ return target.isNonPrivateVirtualMethod()
+ && appView.isInterface(target.method.holder).isFalse();
}
private boolean hasAccessToInvokeTargetFromContext(DexEncodedMethod target, DexType context) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
index 9ecc62d..7e0c9d5 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
@@ -153,7 +153,7 @@
}
// TODO(b/140768815): Reprocessing may trigger more methods to revisit. Update waves on-the-fly.
for (CodeOptimization codeOptimization : codeOptimizations) {
- codeOptimization.optimize(appView, code, feedback, this);
+ codeOptimization.optimize(code, feedback, this);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index a4fc2e9..565af2f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -333,7 +333,7 @@
private boolean isRetargetMethod(DexLibraryClass holder, DexEncodedMethod method) {
assert needsLibraryInfo();
assert holder.type == method.method.holder;
- assert method.isVirtualMethod();
+ assert method.isNonPrivateVirtualMethod();
if (method.isFinal()) {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 7f4c043..328d8e2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -26,7 +26,6 @@
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
-import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
@@ -397,15 +396,17 @@
if (appView.rewritePrefix.hasRewrittenType(dexClass.type)) {
return null;
}
- ResolutionResult resolutionResult =
- appView.appInfo().resolveMaximallySpecificMethods(dexClass, invokedMethod);
- if (!resolutionResult.isSingleResolution()) {
+ DexEncodedMethod singleTarget =
+ appView
+ .appInfo()
+ .resolveMaximallySpecificMethods(dexClass, invokedMethod)
+ .getSingleTarget();
+ if (singleTarget == null) {
// At this point we are in a library class. Failures can happen with NoSuchMethod if a
// library class implement a method with same signature but not related to emulated
// interfaces.
return null;
}
- DexEncodedMethod singleTarget = resolutionResult.getSingleTarget();
if (!singleTarget.isAbstract() && isEmulatedInterface(singleTarget.method.holder)) {
return singleTarget.method.holder;
}
@@ -1143,10 +1144,16 @@
assert !dependency.isLibraryClass();
DesugarGraphConsumer consumer = options.desugarGraphConsumer;
if (consumer != null) {
- Origin dependentOrigin = dependent.getOrigin();
Origin dependencyOrigin = dependency.getOrigin();
- if (dependentOrigin != dependencyOrigin) {
- consumer.accept(dependentOrigin, dependencyOrigin);
+ java.util.Collection<DexProgramClass> dependents = dependent.getSynthesizedFrom();
+ if (dependents == null || dependents.isEmpty()) {
+ dependents = Collections.singletonList(dependent);
+ }
+ for (DexProgramClass clazz : dependents) {
+ Origin dependentOrigin = clazz.getOrigin();
+ if (dependentOrigin != dependencyOrigin) {
+ consumer.accept(dependentOrigin, dependencyOrigin);
+ }
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
index 30edf6a..cf66050 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
@@ -25,6 +25,7 @@
import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.Pair;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -90,7 +91,7 @@
}
// Extract the list of types in the programClass' nest, of host hostClass
- private List<DexType> extractNest(DexClass clazz) {
+ private Pair<DexClass, List<DexType>> extractNest(DexClass clazz) {
assert clazz != null;
DexClass hostClass = clazz.isNestHost() ? clazz : definitionFor(clazz.getNestHost());
if (hostClass == null) {
@@ -105,22 +106,22 @@
classesInNest.add(nestmate.getNestMember());
}
classesInNest.add(hostClass.type);
- return classesInNest;
+ return new Pair<>(hostClass, classesInNest);
}
Future<?> asyncProcessNest(DexClass clazz, ExecutorService executorService) {
return executorService.submit(
() -> {
- List<DexType> nest = extractNest(clazz);
+ Pair<DexClass, List<DexType>> nest = extractNest(clazz);
// Nest is null when nest host is missing, we do nothing in this case.
if (nest != null) {
- processNest(nest);
+ processNest(nest.getFirst(), nest.getSecond());
}
return null; // we want a Callable not a Runnable to be able to throw
});
}
- private void processNest(List<DexType> nest) {
+ private void processNest(DexClass host, List<DexType> nest) {
boolean reported = false;
for (DexType type : nest) {
DexClass clazz = definitionFor(type);
@@ -130,6 +131,7 @@
reported = true;
}
} else {
+ reportDesugarDependencies(host, clazz);
if (shouldProcessClassInNest(clazz, nest)) {
NestBasedAccessDesugaringUseRegistry registry =
new NestBasedAccessDesugaringUseRegistry(clazz);
@@ -142,6 +144,18 @@
}
}
+ private void reportDesugarDependencies(DexClass host, DexClass clazz) {
+ if (host == clazz) {
+ return;
+ }
+ if (host.isProgramClass()) {
+ InterfaceMethodRewriter.reportDependencyEdge(host.asProgramClass(), clazz, appView.options());
+ }
+ if (clazz.isProgramClass()) {
+ InterfaceMethodRewriter.reportDependencyEdge(clazz.asProgramClass(), host, appView.options());
+ }
+ }
+
protected abstract boolean shouldProcessClassInNest(DexClass clazz, List<DexType> nest);
private DexProgramClass createNestAccessConstructor() {
@@ -272,9 +286,9 @@
return true;
}
assert holder.isLibraryClass();
- List<DexType> nest = extractNest(holder);
+ Pair<DexClass, List<DexType>> nest = extractNest(holder);
assert nest != null : "Should be a compilation error if missing nest host on library class.";
- reportIncompleteNest(nest);
+ reportIncompleteNest(nest.getSecond());
throw new Unreachable(
"Incomplete nest due to missing library class should raise a compilation error.");
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLense.java b/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLense.java
index babdbae..9fca058 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLense.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLense.java
@@ -41,9 +41,9 @@
assert methodMap instanceof IdentityHashMap;
assert getFieldMap instanceof IdentityHashMap;
assert putFieldMap instanceof IdentityHashMap;
+ this.nestConstructorType = nestConstructorType;
this.getFieldMap = getFieldMap;
this.putFieldMap = putFieldMap;
- this.nestConstructorType = nestConstructorType;
}
private DexMethod lookupFieldForMethod(
@@ -111,11 +111,8 @@
@Override
public GraphLenseLookupResult lookupMethod(
DexMethod method, DexMethod context, Invoke.Type type) {
- DexMethod previousContext =
- originalMethodSignatures != null
- ? originalMethodSignatures.getOrDefault(context, context)
- : context;
- GraphLenseLookupResult previous = previousLense.lookupMethod(method, previousContext, type);
+ assert originalMethodSignatures == null;
+ GraphLenseLookupResult previous = previousLense.lookupMethod(method, context, type);
DexMethod bridge = methodMap.get(previous.getMethod());
if (bridge == null) {
return previous;
@@ -130,4 +127,31 @@
return new GraphLenseLookupResult(bridge, Invoke.Type.STATIC);
}
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder extends NestedGraphLense.Builder {
+
+ private Map<DexField, DexMethod> getFieldMap = new IdentityHashMap<>();
+ private Map<DexField, DexMethod> putFieldMap = new IdentityHashMap<>();
+
+ public void mapGetField(DexField from, DexMethod to) {
+ getFieldMap.put(from, to);
+ }
+
+ public void mapPutField(DexField from, DexMethod to) {
+ putFieldMap.put(from, to);
+ }
+
+ public GraphLense build(AppView<?> appView, DexType nestConstructorType) {
+ assert typeMap.isEmpty();
+ assert fieldMap.isEmpty();
+ if (getFieldMap.isEmpty() && methodMap.isEmpty() && putFieldMap.isEmpty()) {
+ return appView.graphLense();
+ }
+ return new NestedPrivateMethodLense(
+ appView, nestConstructorType, methodMap, getFieldMap, putFieldMap, appView.graphLense());
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java
index a4642cd..1a6c365 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
@@ -16,13 +15,13 @@
import com.android.tools.r8.utils.ThreadUtils;
import com.google.common.collect.Sets;
import java.util.ArrayList;
-import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
+import java.util.function.BiConsumer;
// Summary:
// - Computes all the live nests reachable from Program Classes (Sequential), each time a
@@ -31,10 +30,6 @@
// for the lens (Sequential)
public class R8NestBasedAccessDesugaring extends NestBasedAccessDesugaring {
- private final Map<DexMethod, DexMethod> lensBridges = new IdentityHashMap<>();
- private final Map<DexField, DexMethod> lensGetFieldBridges = new IdentityHashMap<>();
- private final Map<DexField, DexMethod> lensPutFieldBridges = new IdentityHashMap<>();
-
public R8NestBasedAccessDesugaring(AppView<?> appView) {
super(appView);
}
@@ -44,43 +39,27 @@
assert !appView.options().canUseNestBasedAccess()
|| appView.options().testing.enableForceNestBasedAccessDesugaringForTest;
computeAndProcessNestsConcurrently(executorService);
- addDeferredBridgesAndMapMethods();
+ NestedPrivateMethodLense.Builder lensBuilder = NestedPrivateMethodLense.builder();
+ addDeferredBridgesAndMapMethods(lensBuilder);
clearNestAttributes();
- if (nothingToMap()) {
- return appView.graphLense();
- }
synthesizeNestConstructor(appBuilder);
- return new NestedPrivateMethodLense(
- appView,
- getNestConstructorType(),
- lensBridges,
- lensGetFieldBridges,
- lensPutFieldBridges,
- appView.graphLense());
+ return lensBuilder.build(appView, getNestConstructorType());
}
- private boolean nothingToMap() {
- return lensBridges.isEmpty() && lensGetFieldBridges.isEmpty() && lensPutFieldBridges.isEmpty();
- }
-
- private void addDeferredBridgesAndMapMethods() {
+ private void addDeferredBridgesAndMapMethods(NestedPrivateMethodLense.Builder lensBuilder) {
// Here we add the bridges and we fill the lens map.
- // The lens map are different than the original map since
- // they refer DexMethod and not DexEncodedMethod (so they can be long lived without issues),
- // and since they do not require synchronization (they are only read in the lens).
- // We cannot easily do this concurrently since methods are added to classes.
- addDeferredBridgesAndMapMethods(bridges, lensBridges);
- addDeferredBridgesAndMapMethods(getFieldBridges, lensGetFieldBridges);
- addDeferredBridgesAndMapMethods(putFieldBridges, lensPutFieldBridges);
+ addDeferredBridgesAndMapMethods(bridges, lensBuilder::map);
+ addDeferredBridgesAndMapMethods(getFieldBridges, lensBuilder::mapGetField);
+ addDeferredBridgesAndMapMethods(putFieldBridges, lensBuilder::mapPutField);
}
private <E> void addDeferredBridgesAndMapMethods(
- Map<E, DexEncodedMethod> bridges, Map<E, DexMethod> map) {
+ Map<E, DexEncodedMethod> bridges, BiConsumer<E, DexMethod> lensInserter) {
for (Map.Entry<E, DexEncodedMethod> entry : bridges.entrySet()) {
DexClass holder = definitionFor(entry.getValue().method.holder);
assert holder != null && holder.isProgramClass();
holder.asProgramClass().addMethod(entry.getValue());
- map.put(entry.getKey(), entry.getValue().method);
+ lensInserter.accept(entry.getKey(), entry.getValue().method);
}
bridges.clear();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
index 2292b0a..a344095 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
@@ -6,9 +6,6 @@
import com.android.tools.r8.AssertionsConfiguration;
import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
-import com.android.tools.r8.AssertionsConfiguration.ConfigurationEntry;
-import com.android.tools.r8.AssertionsConfiguration.ConfigurationType;
-import com.android.tools.r8.AssertionsConfiguration.InternalAssertionConfiguration;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
@@ -21,7 +18,9 @@
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.StaticPut;
+import com.android.tools.r8.utils.AssertionConfigurationWithDefault;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThrowingCharIterator;
import java.io.UTFDataFormatException;
import java.util.List;
@@ -31,21 +30,21 @@
private static class ConfigurationEntryWithDexString {
- private ConfigurationEntry entry;
+ private AssertionsConfiguration entry;
private final DexString value;
private ConfigurationEntryWithDexString(
- ConfigurationEntry entry, DexItemFactory dexItemFactory) {
- this.entry = entry;
- switch (entry.getType()) {
+ AssertionsConfiguration configuration, DexItemFactory dexItemFactory) {
+ this.entry = configuration;
+ switch (configuration.getScope()) {
case PACKAGE:
- if (entry.getValue().length() == 0) {
+ if (configuration.getValue().length() == 0) {
value = dexItemFactory.createString("");
} else {
value =
dexItemFactory.createString(
"L"
- + entry
+ + configuration
.getValue()
.replace(
DescriptorUtils.JAVA_PACKAGE_SEPARATOR,
@@ -57,7 +56,7 @@
value =
dexItemFactory.createString(
"L"
- + entry
+ + configuration
.getValue()
.replace(
DescriptorUtils.JAVA_PACKAGE_SEPARATOR,
@@ -75,39 +74,38 @@
private final AppView<?> appView;
private final DexItemFactory dexItemFactory;
+ private final AssertionTransformation defaultTransformation;
private final List<ConfigurationEntryWithDexString> configuration;
private final boolean enabled;
public AssertionsRewriter(AppView<?> appView) {
this.appView = appView;
this.dexItemFactory = appView.dexItemFactory();
- if (appView.options().assertionsConfiguration == null) {
- this.configuration = null;
- this.enabled = false;
- } else {
- List<ConfigurationEntry> configuration =
- InternalAssertionConfiguration
- .getConfiguration(appView.options().assertionsConfiguration);
- this.configuration =
- configuration.stream()
- .map(entry -> new ConfigurationEntryWithDexString(entry, appView.dexItemFactory()))
- .collect(Collectors.toList());
- this.enabled = !isPassthroughAll(appView.options().assertionsConfiguration);
+ this.enabled = isEnabled(appView.options());
+ if (!enabled) {
+ defaultTransformation = null;
+ configuration = null;
+ return;
}
+ // Convert the assertion transformation to the representation used for this rewriter.
+ this.defaultTransformation = appView.options().assertionsConfiguration.defautlTransformation;
+ this.configuration =
+ appView.options().assertionsConfiguration.assertionsConfigurations.stream()
+ .map(entry -> new ConfigurationEntryWithDexString(entry, appView.dexItemFactory()))
+ .collect(Collectors.toList());
}
- public static boolean isPassthroughAll(AssertionsConfiguration assertionsConfiguration) {
- List<ConfigurationEntry> configuration =
- InternalAssertionConfiguration.getConfiguration(assertionsConfiguration);
- return configuration.size() == 1
- && configuration.get(0).getTransformation() == AssertionTransformation.PASSTHROUGH
- && configuration.get(0).getType() == ConfigurationType.ALL;
+ // Static method used by other analyses to see if additional analysis is required to support
+ // this rewriting.
+ public static boolean isEnabled(InternalOptions options) {
+ AssertionConfigurationWithDefault configuration = options.assertionsConfiguration;
+ return configuration != null && !configuration.isPassthroughAll();
}
private AssertionTransformation getTransformationForMethod(DexEncodedMethod method) {
- AssertionTransformation transformation = null;
+ AssertionTransformation transformation = defaultTransformation;
for (ConfigurationEntryWithDexString entry : configuration) {
- switch (entry.entry.getType()) {
+ switch (entry.entry.getScope()) {
case ALL:
transformation = entry.entry.getTransformation();
break;
@@ -132,7 +130,7 @@
throw new Unreachable();
}
}
- assert transformation != null; // Default transformation are always added.
+ assert transformation != null;
return transformation;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
index e83a17a..00c797d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
@@ -88,7 +88,7 @@
ResolutionResult resolutionResult =
appView.appInfo().resolveMethod(invokedMethod.holder, invokedMethod);
// For virtual and interface calls, proceed on valid results only (since it's enforced).
- if (!resolutionResult.isValidVirtualTarget(appView.options())) {
+ if (!resolutionResult.isVirtualTarget()) {
continue;
}
// If the resolution ended up with a single target, check if it is a library override.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
index 4977d4c..b95f69e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
@@ -352,7 +352,7 @@
// resolution result.
ResolutionResult resolutionResult =
appView.appInfo().resolveMethod(method.holder, method, isInterface);
- if (!resolutionResult.isValidVirtualTarget(appView.options())) {
+ if (!resolutionResult.isVirtualTarget()) {
return ConstraintWithTarget.NEVER;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
index 194b84a..aec5219 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
@@ -291,7 +291,7 @@
if (method.getCode() == null || !method.getCode().isCfCode()) {
return null;
}
- if (method.isVirtualMethod()) {
+ if (method.isNonPrivateVirtualMethod()) {
// Abort if the method overrides another method, or if the method is overridden. In both cases
// an unused argument cannot be removed unless it is unused in all of the related methods in
// the hierarchy.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index c522ff3..d4d43f4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -859,13 +859,14 @@
// We should not inline a method if the invocation has type interface or virtual and the
// signature of the invocation resolves to a private or static method.
+ // TODO(b/147212189): Why not inline private methods? If access is permitted it is valid.
ResolutionResult resolutionResult = appView.appInfo().resolveMethod(callee.holder, callee);
if (resolutionResult.isSingleResolution()
- && !resolutionResult.getSingleTarget().isVirtualMethod()) {
+ && !resolutionResult.getSingleTarget().isNonPrivateVirtualMethod()) {
return null;
}
- if (!singleTarget.isVirtualMethod()) {
+ if (!singleTarget.isNonPrivateVirtualMethod()) {
return null;
}
if (method == singleTarget) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index a11f309..8fee6c5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -135,7 +135,7 @@
return;
}
- assert method.isVirtualMethod();
+ assert method.isNonPrivateVirtualMethod();
assert context == null;
Map<InvokeVirtual, InliningInfo> invokesToInline = new IdentityHashMap<>();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java
index 0a92679..9c5a8d1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java
@@ -24,10 +24,13 @@
public class LibraryMethodOptimizer implements CodeOptimization {
+ private final AppView<?> appView;
+
private final Map<DexType, LibraryMethodModelCollection> libraryMethodModelCollections =
new IdentityHashMap<>();
public LibraryMethodOptimizer(AppView<? extends AppInfoWithSubtyping> appView) {
+ this.appView = appView;
register(new BooleanMethodOptimizer(appView));
}
@@ -39,7 +42,6 @@
@Override
public void optimize(
- AppView<?> appView,
IRCode code,
OptimizationFeedback feedback,
MethodProcessor methodProcessor) {
diff --git a/src/main/java/com/android/tools/r8/retrace/Retrace.java b/src/main/java/com/android/tools/r8/retrace/Retrace.java
index 79d9721..057875d 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.utils.ExceptionUtils.STATUS_ERROR;
+import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.Keep;
import com.android.tools.r8.naming.ClassNameMapper;
@@ -35,7 +36,7 @@
public static final String USAGE_MESSAGE =
StringUtils.lines(
- "Usage: retrace <proguard-map> <stacktrace-file>",
+ "Usage: retrace <proguard-map> <stacktrace-file> [--regex <regexp>, --verbose, --info]",
" where <proguard-map> is an r8 generated mapping file.");
private static Builder parseArguments(String[] args, DiagnosticsHandler diagnosticsHandler) {
@@ -48,16 +49,19 @@
if (help != null) {
return null;
}
+ Boolean info = OptionsParsing.tryParseBoolean(context, "--info");
+ if (info != null) {
+ // This is already set in the diagnostics handler.
+ continue;
+ }
Boolean verbose = OptionsParsing.tryParseBoolean(context, "--verbose");
if (verbose != null) {
- // TODO(b/132850880): Enable support for verbose.
- diagnosticsHandler.error(new StringDiagnostic("Currently no support for --verbose"));
+ builder.setVerbose(true);
continue;
}
String regex = OptionsParsing.tryParseSingle(context, "--regex", "r");
if (regex != null && !regex.isEmpty()) {
- // TODO(b/132850880): Enable support for regex.
- diagnosticsHandler.error(new StringDiagnostic("Currently no support for --regex"));
+ builder.setRegularExpression(regex);
continue;
}
if (!hasSetProguardMap) {
@@ -108,7 +112,7 @@
}
/**
- * The main entry point for running the retrace.
+ * The main entry point for running retrace.
*
* @param command The command that describes the desired behavior of this retrace invocation.
*/
@@ -128,7 +132,8 @@
.retrace();
} else {
result =
- new RetraceStackTrace(retraceBase, command.stackTrace, command.diagnosticsHandler)
+ new RetraceStackTrace(
+ retraceBase, command.stackTrace, command.diagnosticsHandler, command.isVerbose)
.retrace();
}
command.retracedStackTraceConsumer.accept(result.getNodes());
@@ -139,9 +144,11 @@
}
}
- static void run(String[] args) {
- DiagnosticsHandler diagnosticsHandler = new DiagnosticsHandler() {};
- Builder builder = parseArguments(args, diagnosticsHandler);
+ public static void run(String[] args) {
+ RetraceDiagnosticsHandler retraceDiagnosticsHandler =
+ new RetraceDiagnosticsHandler(
+ new DiagnosticsHandler() {}, Arrays.asList(args).contains("--info"));
+ Builder builder = parseArguments(args, retraceDiagnosticsHandler);
if (builder == null) {
// --help was an argument to list
assert Arrays.asList(args).contains("--help");
@@ -152,6 +159,7 @@
retraced -> System.out.print(StringUtils.lines(retraced)));
run(builder.build());
}
+
/**
* The main entry point for running a legacy compatible retrace from the command line.
*
@@ -189,4 +197,33 @@
System.exit(STATUS_ERROR);
}
}
+
+ private static class RetraceDiagnosticsHandler implements DiagnosticsHandler {
+
+ private final DiagnosticsHandler diagnosticsHandler;
+ private final boolean printInfo;
+
+ public RetraceDiagnosticsHandler(DiagnosticsHandler diagnosticsHandler, boolean printInfo) {
+ this.diagnosticsHandler = diagnosticsHandler;
+ this.printInfo = printInfo;
+ assert diagnosticsHandler != null;
+ }
+
+ @Override
+ public void error(Diagnostic error) {
+ diagnosticsHandler.error(error);
+ }
+
+ @Override
+ public void warning(Diagnostic warning) {
+ diagnosticsHandler.warning(warning);
+ }
+
+ @Override
+ public void info(Diagnostic info) {
+ if (printInfo) {
+ diagnosticsHandler.info(info);
+ }
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java b/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java
index b2692a2..93addc4 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.Keep;
+import com.android.tools.r8.utils.StringDiagnostic;
import java.io.IOException;
import java.util.List;
import java.util.function.Consumer;
@@ -56,7 +57,7 @@
public static class Builder {
private boolean isVerbose;
- private DiagnosticsHandler diagnosticsHandler;
+ private final DiagnosticsHandler diagnosticsHandler;
private ProguardMapProducer proguardMapProducer;
private String regularExpression;
private List<String> stackTrace;
@@ -67,8 +68,8 @@
}
/** Set if the produced stack trace should have additional information. */
- public Builder isVerbose() {
- this.isVerbose = true;
+ public Builder setVerbose(boolean verbose) {
+ this.isVerbose = verbose;
return this;
}
@@ -127,6 +128,11 @@
if (this.retracedStackTraceConsumer == null) {
throw new RuntimeException("RetracedStackConsumer not specified");
}
+ if (isVerbose && regularExpression != null) {
+ this.diagnosticsHandler.warning(
+ new StringDiagnostic(
+ "Retrace does not support verbose output when a regular expression is specified"));
+ }
return new RetraceCommand(
isVerbose,
regularExpression,
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java b/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java
index 3a564d8..3b68d3d 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.retrace;
+import static com.android.tools.r8.retrace.RetraceUtils.methodDescriptionFromMethodReference;
import static com.google.common.base.Predicates.not;
import com.android.tools.r8.DiagnosticsHandler;
@@ -11,7 +12,7 @@
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.DescriptorUtils;
-import com.google.common.base.Strings;
+import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
@@ -42,17 +43,21 @@
if (lines.get(0).asAtLine().isAmbiguous) {
lines.sort(new AtStackTraceLineComparator());
}
- String previousClazz = "";
+ boolean shouldPrintOr = false;
for (StackTraceLine line : lines) {
assert line.isAtLine();
AtLine atLine = line.asAtLine();
- if (atLine.isAmbiguous) {
+ if (atLine.isAmbiguous && shouldPrintOr) {
+ String atLineString = atLine.toString();
+ int firstNonWhitespaceCharacter = StringUtils.firstNonWhitespaceCharacter(atLineString);
strings.add(
- atLine.toString(previousClazz.isEmpty() ? atLine.at : "<OR> " + atLine.at, ""));
+ atLineString.substring(0, firstNonWhitespaceCharacter)
+ + "<OR> "
+ + atLineString.substring(firstNonWhitespaceCharacter));
} else {
strings.add(atLine.toString());
}
- previousClazz = atLine.clazz;
+ shouldPrintOr = true;
}
}
}
@@ -84,12 +89,17 @@
private final RetraceBase retraceBase;
private final List<String> stackTrace;
private final DiagnosticsHandler diagnosticsHandler;
+ private final boolean verbose;
RetraceStackTrace(
- RetraceBase retraceBase, List<String> stackTrace, DiagnosticsHandler diagnosticsHandler) {
+ RetraceBase retraceBase,
+ List<String> stackTrace,
+ DiagnosticsHandler diagnosticsHandler,
+ boolean verbose) {
this.retraceBase = retraceBase;
this.stackTrace = stackTrace;
this.diagnosticsHandler = diagnosticsHandler;
+ this.verbose = verbose;
}
public RetraceCommandLineResult retrace() {
@@ -107,7 +117,7 @@
return;
}
StackTraceLine stackTraceLine = parseLine(index + 1, stackTrace.get(index));
- List<StackTraceLine> retraced = stackTraceLine.retrace(retraceBase);
+ List<StackTraceLine> retraced = stackTraceLine.retrace(retraceBase, verbose);
StackTraceNode node = new StackTraceNode(retraced);
result.add(node);
retraceLine(stackTrace, index + 1, result);
@@ -115,7 +125,7 @@
abstract static class StackTraceLine {
- abstract List<StackTraceLine> retrace(RetraceBase retraceBase);
+ abstract List<StackTraceLine> retrace(RetraceBase retraceBase, boolean verbose);
static int firstNonWhiteSpaceCharacterFromIndex(String line, int index) {
return firstFromIndex(line, index, not(Character::isWhitespace));
@@ -215,7 +225,7 @@
}
@Override
- List<StackTraceLine> retrace(RetraceBase retraceBase) {
+ List<StackTraceLine> retrace(RetraceBase retraceBase, boolean verbose) {
List<StackTraceLine> exceptionLines = new ArrayList<>();
retraceBase
.retrace(Reference.classFromTypeName(exceptionClass))
@@ -267,6 +277,7 @@
private final String at;
private final String clazz;
private final String method;
+ private final String methodAsString;
private final String fileName;
private final int linePosition;
private final boolean isAmbiguous;
@@ -276,6 +287,7 @@
String at,
String clazz,
String method,
+ String methodAsString,
String fileName,
int linePosition,
boolean isAmbiguous) {
@@ -283,6 +295,7 @@
this.at = at;
this.clazz = clazz;
this.method = method;
+ this.methodAsString = methodAsString;
this.fileName = fileName;
this.linePosition = linePosition;
this.isAmbiguous = isAmbiguous;
@@ -335,11 +348,14 @@
} else {
fileName = line.substring(parensStart + 1, parensEnd);
}
+ String className = line.substring(classStartIndex, methodSeparator);
+ String methodName = line.substring(methodSeparator + 1, parensStart);
return new AtLine(
line.substring(0, firstNonWhiteSpace),
line.substring(firstNonWhiteSpace, classStartIndex),
- line.substring(classStartIndex, methodSeparator),
- line.substring(methodSeparator + 1, parensStart),
+ className,
+ methodName,
+ className + "." + methodName,
fileName,
position,
false);
@@ -350,49 +366,38 @@
}
@Override
- List<StackTraceLine> retrace(RetraceBase retraceBase) {
+ List<StackTraceLine> retrace(RetraceBase retraceBase, boolean verbose) {
List<StackTraceLine> lines = new ArrayList<>();
ClassReference classReference = Reference.classFromTypeName(clazz);
- RetraceMethodResult retraceMethodResult =
- retraceBase
- .retrace(classReference)
- .lookupMethod(method)
- .narrowByLine(linePosition)
- .forEach(
- methodElement -> {
- MethodReference methodReference = methodElement.getMethodReference();
- lines.add(
- new AtLine(
- startingWhitespace,
- at,
- methodReference.getHolderClass().getTypeName(),
- methodReference.getMethodName(),
- retraceBase.retraceSourceFile(
- classReference, fileName, methodReference.getHolderClass(), true),
- hasLinePosition()
- ? methodElement.getOriginalLineNumber(linePosition)
- : linePosition,
- methodElement.getRetraceMethodResult().isAmbiguous()));
- });
+ retraceBase
+ .retrace(classReference)
+ .lookupMethod(method)
+ .narrowByLine(linePosition)
+ .forEach(
+ methodElement -> {
+ MethodReference methodReference = methodElement.getMethodReference();
+ lines.add(
+ new AtLine(
+ startingWhitespace,
+ at,
+ methodReference.getHolderClass().getTypeName(),
+ methodReference.getMethodName(),
+ methodDescriptionFromMethodReference(methodReference, verbose),
+ retraceBase.retraceSourceFile(
+ classReference, fileName, methodReference.getHolderClass(), true),
+ hasLinePosition()
+ ? methodElement.getOriginalLineNumber(linePosition)
+ : linePosition,
+ methodElement.getRetraceMethodResult().isAmbiguous()));
+ });
return lines;
}
@Override
public String toString() {
- return toString(at, "");
- }
-
- protected String toString(String at, String previousClass) {
StringBuilder sb = new StringBuilder(startingWhitespace);
sb.append(at);
- String commonPrefix = Strings.commonPrefix(clazz, previousClass);
- if (commonPrefix.length() == clazz.length()) {
- sb.append(Strings.repeat(" ", clazz.length() + 1));
- } else {
- sb.append(Strings.padStart(clazz.substring(commonPrefix.length()), clazz.length(), ' '));
- sb.append(".");
- }
- sb.append(method);
+ sb.append(methodAsString);
sb.append("(");
sb.append(fileName);
if (linePosition != NO_POSITION) {
@@ -443,7 +448,7 @@
}
@Override
- List<StackTraceLine> retrace(RetraceBase retraceBase) {
+ List<StackTraceLine> retrace(RetraceBase retraceBase, boolean verbose) {
return ImmutableList.of(new MoreLine(line));
}
@@ -461,7 +466,7 @@
}
@Override
- List<StackTraceLine> retrace(RetraceBase retraceBase) {
+ List<StackTraceLine> retrace(RetraceBase retraceBase, boolean verbose) {
return ImmutableList.of(new UnknownLine(line));
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceUtils.java b/src/main/java/com/android/tools/r8/retrace/RetraceUtils.java
new file mode 100644
index 0000000..81c2272
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceUtils.java
@@ -0,0 +1,38 @@
+// 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.retrace;
+
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.TypeReference;
+
+public class RetraceUtils {
+
+ public static String methodDescriptionFromMethodReference(
+ MethodReference methodReference, boolean verbose) {
+ if (!verbose || methodReference.isUnknown()) {
+ return methodReference.getHolderClass().getTypeName() + "." + methodReference.getMethodName();
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append(
+ methodReference.getReturnType() == null
+ ? "void"
+ : methodReference.getReturnType().getTypeName());
+ sb.append(" ");
+ sb.append(methodReference.getHolderClass().getTypeName());
+ sb.append(".");
+ sb.append(methodReference.getMethodName());
+ sb.append("(");
+ boolean seenFirstIndex = false;
+ for (TypeReference formalType : methodReference.getFormalTypes()) {
+ if (seenFirstIndex) {
+ sb.append(",");
+ }
+ seenFirstIndex = true;
+ sb.append(formalType.getTypeName());
+ }
+ sb.append(")");
+ return sb.toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 25a7b15..9b5029f 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -26,7 +26,6 @@
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.PresortedComparable;
import com.android.tools.r8.graph.ResolutionResult;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement;
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.utils.CollectionUtils;
@@ -979,7 +978,7 @@
private DexEncodedMethod validateSingleVirtualTarget(
DexEncodedMethod singleTarget, DexEncodedMethod resolutionResult) {
- assert SingleResolutionResult.isValidVirtualTarget(options(), resolutionResult);
+ assert resolutionResult.isVirtualMethod();
if (singleTarget == null || singleTarget == DexEncodedMethod.SENTINEL) {
return null;
@@ -996,7 +995,7 @@
private boolean isInvalidSingleVirtualTarget(
DexEncodedMethod singleTarget, DexEncodedMethod resolutionResult) {
- assert SingleResolutionResult.isValidVirtualTarget(options(), resolutionResult);
+ assert resolutionResult.isVirtualMethod();
// Art978_virtual_interfaceTest correctly expects an IncompatibleClassChangeError exception
// at runtime.
return !singleTarget.accessFlags.isAtLeastAsVisibleAs(resolutionResult.accessFlags);
@@ -1039,11 +1038,10 @@
// from the runtime type of the receiver.
if (receiverLowerBoundType != null) {
if (receiverLowerBoundType.getClassType() == refinedReceiverType) {
- if (resolutionResult.isSingleResolution()
- && resolutionResult.isValidVirtualTargetForDynamicDispatch()) {
+ if (resolutionResult.isSingleResolution() && resolutionResult.isVirtualTarget()) {
ResolutionResult refinedResolutionResult = resolveMethod(refinedReceiverType, method);
if (refinedResolutionResult.isSingleResolution()
- && refinedResolutionResult.isValidVirtualTargetForDynamicDispatch()) {
+ && refinedResolutionResult.isVirtualTarget()) {
return validateSingleVirtualTarget(
refinedResolutionResult.getSingleTarget(), resolutionResult.getSingleTarget());
}
@@ -1081,7 +1079,7 @@
// First get the target for the holder type.
ResolutionResult topMethod = resolveMethodOnClass(holder, method);
// We might hit none or multiple targets. Both make this fail at runtime.
- if (!topMethod.isSingleResolution() || !topMethod.isValidVirtualTarget(options())) {
+ if (!topMethod.isSingleResolution() || !topMethod.isVirtualTarget()) {
method.setSingleVirtualMethodCache(refinedReceiverType, null);
return null;
}
@@ -1223,11 +1221,10 @@
if (receiverLowerBoundType != null) {
if (receiverLowerBoundType.getClassType() == refinedReceiverType) {
ResolutionResult resolutionResult = resolveMethod(method.holder, method, true);
- if (resolutionResult.isSingleResolution()
- && resolutionResult.isValidVirtualTargetForDynamicDispatch()) {
+ if (resolutionResult.isSingleResolution() && resolutionResult.isVirtualTarget()) {
ResolutionResult refinedResolutionResult = resolveMethod(refinedReceiverType, method);
if (refinedResolutionResult.isSingleResolution()
- && refinedResolutionResult.isValidVirtualTargetForDynamicDispatch()) {
+ && refinedResolutionResult.isVirtualTarget()) {
return validateSingleVirtualTarget(
refinedResolutionResult.getSingleTarget(), resolutionResult.getSingleTarget());
}
diff --git a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
index 7c78ac5..f05d068 100644
--- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -12,100 +12,107 @@
import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.UseRegistry;
public class DefaultEnqueuerUseRegistry extends UseRegistry {
- private final DexProgramClass currentHolder;
- protected final DexEncodedMethod currentMethod;
+ private final ProgramMethod context;
private final Enqueuer enqueuer;
public DefaultEnqueuerUseRegistry(
- AppView<?> appView,
- DexProgramClass currentHolder,
- DexEncodedMethod currentMethod,
- Enqueuer enqueuer) {
+ AppView<?> appView, DexProgramClass holder, DexEncodedMethod method, Enqueuer enqueuer) {
super(appView.dexItemFactory());
- assert currentHolder.type == currentMethod.method.holder;
- this.currentHolder = currentHolder;
- this.currentMethod = currentMethod;
+ this.context = new ProgramMethod(holder, method);
this.enqueuer = enqueuer;
}
+ public ProgramMethod getContext() {
+ return context;
+ }
+
+ public DexProgramClass getContextHolder() {
+ return context.holder;
+ }
+
+ public DexEncodedMethod getContextMethod() {
+ return context.method;
+ }
+
@Override
public boolean registerInvokeVirtual(DexMethod invokedMethod) {
- return enqueuer.traceInvokeVirtual(invokedMethod, currentHolder, currentMethod);
+ return enqueuer.traceInvokeVirtual(invokedMethod, context);
}
@Override
public boolean registerInvokeDirect(DexMethod invokedMethod) {
- return enqueuer.traceInvokeDirect(invokedMethod, currentHolder, currentMethod);
+ return enqueuer.traceInvokeDirect(invokedMethod, context);
}
@Override
public boolean registerInvokeStatic(DexMethod invokedMethod) {
- return enqueuer.traceInvokeStatic(invokedMethod, currentHolder, currentMethod);
+ return enqueuer.traceInvokeStatic(invokedMethod, context);
}
@Override
public boolean registerInvokeInterface(DexMethod invokedMethod) {
- return enqueuer.traceInvokeInterface(invokedMethod, currentHolder, currentMethod);
+ return enqueuer.traceInvokeInterface(invokedMethod, context);
}
@Override
public boolean registerInvokeSuper(DexMethod invokedMethod) {
- return enqueuer.traceInvokeSuper(invokedMethod, currentHolder, currentMethod);
+ return enqueuer.traceInvokeSuper(invokedMethod, context);
}
@Override
public boolean registerInstanceFieldWrite(DexField field) {
- return enqueuer.traceInstanceFieldWrite(field, currentMethod);
+ return enqueuer.traceInstanceFieldWrite(field, context.method);
}
@Override
public boolean registerInstanceFieldRead(DexField field) {
- return enqueuer.traceInstanceFieldRead(field, currentMethod);
+ return enqueuer.traceInstanceFieldRead(field, context.method);
}
@Override
public boolean registerNewInstance(DexType type) {
- return enqueuer.traceNewInstance(type, currentMethod);
+ return enqueuer.traceNewInstance(type, context);
}
@Override
public boolean registerStaticFieldRead(DexField field) {
- return enqueuer.traceStaticFieldRead(field, currentMethod);
+ return enqueuer.traceStaticFieldRead(field, context.method);
}
@Override
public boolean registerStaticFieldWrite(DexField field) {
- return enqueuer.traceStaticFieldWrite(field, currentMethod);
+ return enqueuer.traceStaticFieldWrite(field, context.method);
}
@Override
public boolean registerConstClass(DexType type) {
- return enqueuer.traceConstClass(type, currentMethod);
+ return enqueuer.traceConstClass(type, context.method);
}
@Override
public boolean registerCheckCast(DexType type) {
- return enqueuer.traceCheckCast(type, currentMethod);
+ return enqueuer.traceCheckCast(type, context.method);
}
@Override
public boolean registerTypeReference(DexType type) {
- return enqueuer.traceTypeReference(type, currentMethod);
+ return enqueuer.traceTypeReference(type, context.method);
}
@Override
public void registerMethodHandle(DexMethodHandle methodHandle, MethodHandleUse use) {
super.registerMethodHandle(methodHandle, use);
- enqueuer.traceMethodHandle(methodHandle, use, currentMethod);
+ enqueuer.traceMethodHandle(methodHandle, use, context.method);
}
@Override
public void registerCallSite(DexCallSite callSite) {
super.registerCallSite(callSite);
- enqueuer.traceCallSite(callSite, currentMethod);
+ enqueuer.traceCallSite(callSite, context);
}
}
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 6bd3db2..ac1dea1 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
+import com.android.tools.r8.graph.AccessControl;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.Descriptor;
@@ -37,6 +38,7 @@
import com.android.tools.r8.graph.FieldAccessInfoImpl;
import com.android.tools.r8.graph.KeyedDexItem;
import com.android.tools.r8.graph.PresortedComparable;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.graph.ResolutionResult.FailedResolutionResult;
import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
@@ -93,7 +95,6 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.BiConsumer;
-import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -586,21 +587,20 @@
return isRead ? info.recordRead(field, context) : info.recordWrite(field, context);
}
- void traceCallSite(DexCallSite callSite, DexEncodedMethod currentMethod) {
+ void traceCallSite(DexCallSite callSite, ProgramMethod context) {
callSites.add(callSite);
List<DexType> directInterfaces = LambdaDescriptor.getInterfaces(callSite, appInfo);
if (directInterfaces != null) {
for (DexType lambdaInstantiatedInterface : directInterfaces) {
- markLambdaInstantiated(lambdaInstantiatedInterface, currentMethod);
+ markLambdaInstantiated(lambdaInstantiatedInterface, context.method);
}
} else {
if (!appInfo.isStringConcat(callSite.bootstrapMethod)) {
if (options.reporter != null) {
Diagnostic message =
new StringDiagnostic(
- "Unknown bootstrap method " + callSite.bootstrapMethod,
- appInfo.originFor(currentMethod.method.holder));
+ "Unknown bootstrap method " + callSite.bootstrapMethod, context.holder.origin);
options.reporter.warning(message);
}
}
@@ -634,19 +634,19 @@
switch (implHandle.type) {
case INVOKE_STATIC:
- traceInvokeStaticFromLambda(method, currentMethod);
+ traceInvokeStaticFromLambda(method, context);
break;
case INVOKE_INTERFACE:
- traceInvokeInterfaceFromLambda(method, currentMethod);
+ traceInvokeInterfaceFromLambda(method, context);
break;
case INVOKE_INSTANCE:
- traceInvokeVirtualFromLambda(method, currentMethod);
+ traceInvokeVirtualFromLambda(method, context);
break;
case INVOKE_DIRECT:
- traceInvokeDirectFromLambda(method, currentMethod);
+ traceInvokeDirectFromLambda(method, context);
break;
case INVOKE_CONSTRUCTOR:
- traceNewInstanceFromLambda(method.holder, currentMethod);
+ traceNewInstanceFromLambda(method.holder, context);
break;
default:
throw new Unreachable();
@@ -726,7 +726,7 @@
if (clazz != null) {
KeepReason reason = KeepReason.methodHandleReferencedIn(currentMethod);
if (clazz.isInterface() && !clazz.accessFlags.isAnnotation()) {
- markInterfaceAsInstantiated(clazz, graphReporter.registerClass(clazz, reason));
+ markInterfaceAsInstantiated(clazz, graphReporter.registerInterface(clazz, reason));
} else {
markInstantiated(clazz, null, reason);
}
@@ -739,8 +739,9 @@
return true;
}
- boolean traceInvokeDirect(
- DexMethod invokedMethod, DexProgramClass currentHolder, DexEncodedMethod currentMethod) {
+ boolean traceInvokeDirect(DexMethod invokedMethod, ProgramMethod context) {
+ DexProgramClass currentHolder = context.holder;
+ DexEncodedMethod currentMethod = context.method;
boolean skipTracing =
registerDeferredActionForDeadProtoBuilder(
invokedMethod.holder,
@@ -753,7 +754,7 @@
}
return traceInvokeDirect(
- invokedMethod, currentMethod, KeepReason.invokedFrom(currentHolder, currentMethod));
+ invokedMethod, context, KeepReason.invokedFrom(currentHolder, currentMethod));
}
/** Returns true if a deferred action was registered. */
@@ -770,13 +771,14 @@
return false;
}
- boolean traceInvokeDirectFromLambda(DexMethod invokedMethod, DexEncodedMethod currentMethod) {
+ boolean traceInvokeDirectFromLambda(DexMethod invokedMethod, ProgramMethod context) {
return traceInvokeDirect(
- invokedMethod, currentMethod, KeepReason.invokedFromLambdaCreatedIn(currentMethod));
+ invokedMethod, context, KeepReason.invokedFromLambdaCreatedIn(context.method));
}
private boolean traceInvokeDirect(
- DexMethod invokedMethod, DexEncodedMethod currentMethod, KeepReason reason) {
+ DexMethod invokedMethod, ProgramMethod context, KeepReason reason) {
+ DexEncodedMethod currentMethod = context.method;
if (!registerMethodWithTargetAndContext(directInvokes, invokedMethod, currentMethod)) {
return false;
}
@@ -787,42 +789,42 @@
return true;
}
- boolean traceInvokeInterface(
- DexMethod invokedMethod, DexProgramClass currentHolder, DexEncodedMethod currentMethod) {
+ boolean traceInvokeInterface(DexMethod invokedMethod, ProgramMethod context) {
return traceInvokeInterface(
- invokedMethod, currentMethod, KeepReason.invokedFrom(currentHolder, currentMethod));
+ invokedMethod, context, KeepReason.invokedFrom(context.holder, context.method));
}
- boolean traceInvokeInterfaceFromLambda(DexMethod invokedMethod, DexEncodedMethod currentMethod) {
+ boolean traceInvokeInterfaceFromLambda(DexMethod invokedMethod, ProgramMethod context) {
return traceInvokeInterface(
- invokedMethod, currentMethod, KeepReason.invokedFromLambdaCreatedIn(currentMethod));
+ invokedMethod, context, KeepReason.invokedFromLambdaCreatedIn(context.method));
}
private boolean traceInvokeInterface(
- DexMethod method, DexEncodedMethod currentMethod, KeepReason keepReason) {
+ DexMethod method, ProgramMethod context, KeepReason keepReason) {
+ DexEncodedMethod currentMethod = context.method;
if (!registerMethodWithTargetAndContext(interfaceInvokes, method, currentMethod)) {
return false;
}
if (Log.ENABLED) {
Log.verbose(getClass(), "Register invokeInterface `%s`.", method);
}
- workList.enqueueMarkReachableInterfaceAction(method, keepReason);
+ markVirtualMethodAsReachable(method, true, context, keepReason);
return true;
}
- boolean traceInvokeStatic(
- DexMethod invokedMethod, DexProgramClass currentHolder, DexEncodedMethod currentMethod) {
+ boolean traceInvokeStatic(DexMethod invokedMethod, ProgramMethod context) {
return traceInvokeStatic(
- invokedMethod, currentMethod, KeepReason.invokedFrom(currentHolder, currentMethod));
+ invokedMethod, context, KeepReason.invokedFrom(context.holder, context.method));
}
- boolean traceInvokeStaticFromLambda(DexMethod invokedMethod, DexEncodedMethod currentMethod) {
+ boolean traceInvokeStaticFromLambda(DexMethod invokedMethod, ProgramMethod context) {
return traceInvokeStatic(
- invokedMethod, currentMethod, KeepReason.invokedFromLambdaCreatedIn(currentMethod));
+ invokedMethod, context, KeepReason.invokedFromLambdaCreatedIn(context.method));
}
private boolean traceInvokeStatic(
- DexMethod invokedMethod, DexEncodedMethod currentMethod, KeepReason reason) {
+ DexMethod invokedMethod, ProgramMethod context, KeepReason reason) {
+ DexEncodedMethod currentMethod = context.method;
DexItemFactory dexItemFactory = appView.dexItemFactory();
if (dexItemFactory.classMethods.isReflectiveClassLookup(invokedMethod)
|| dexItemFactory.atomicFieldUpdaterMethods.isFieldUpdater(invokedMethod)) {
@@ -852,8 +854,9 @@
return true;
}
- boolean traceInvokeSuper(
- DexMethod invokedMethod, DexProgramClass currentHolder, DexEncodedMethod currentMethod) {
+ boolean traceInvokeSuper(DexMethod invokedMethod, ProgramMethod context) {
+ DexProgramClass currentHolder = context.holder;
+ DexEncodedMethod currentMethod = context.method;
// We have to revisit super invokes based on the context they are found in. The same
// method descriptor will hit different targets, depending on the context it is used in.
DexMethod actualTarget = getInvokeSuperTarget(invokedMethod, currentMethod);
@@ -867,56 +870,55 @@
return true;
}
- boolean traceInvokeVirtual(
- DexMethod invokedMethod, DexProgramClass currentHolder, DexEncodedMethod currentMethod) {
+ boolean traceInvokeVirtual(DexMethod invokedMethod, ProgramMethod context) {
return traceInvokeVirtual(
- invokedMethod, currentMethod, KeepReason.invokedFrom(currentHolder, currentMethod));
+ invokedMethod, context, KeepReason.invokedFrom(context.holder, context.method));
}
- boolean traceInvokeVirtualFromLambda(DexMethod invokedMethod, DexEncodedMethod currentMethod) {
+ boolean traceInvokeVirtualFromLambda(DexMethod invokedMethod, ProgramMethod context) {
return traceInvokeVirtual(
- invokedMethod, currentMethod, KeepReason.invokedFromLambdaCreatedIn(currentMethod));
+ invokedMethod, context, KeepReason.invokedFromLambdaCreatedIn(context.method));
}
private boolean traceInvokeVirtual(
- DexMethod invokedMethod, DexEncodedMethod currentMethod, KeepReason reason) {
+ DexMethod invokedMethod, ProgramMethod context, KeepReason reason) {
if (invokedMethod == appView.dexItemFactory().classMethods.newInstance
|| invokedMethod == appView.dexItemFactory().constructorMethods.newInstance) {
- pendingReflectiveUses.add(currentMethod);
+ pendingReflectiveUses.add(context.method);
} else if (appView.dexItemFactory().classMethods.isReflectiveMemberLookup(invokedMethod)) {
// Implicitly add -identifiernamestring rule for the Java reflection in use.
identifierNameStrings.add(invokedMethod);
// Revisit the current method to implicitly add -keep rule for items with reflective access.
- pendingReflectiveUses.add(currentMethod);
+ pendingReflectiveUses.add(context.method);
}
- if (!registerMethodWithTargetAndContext(virtualInvokes, invokedMethod, currentMethod)) {
+ if (!registerMethodWithTargetAndContext(virtualInvokes, invokedMethod, context.method)) {
return false;
}
if (Log.ENABLED) {
Log.verbose(getClass(), "Register invokeVirtual `%s`.", invokedMethod);
}
- workList.enqueueMarkReachableVirtualAction(invokedMethod, reason);
+ markVirtualMethodAsReachable(invokedMethod, false, context, reason);
return true;
}
- boolean traceNewInstance(DexType type, DexEncodedMethod currentMethod) {
+ boolean traceNewInstance(DexType type, ProgramMethod context) {
+ DexEncodedMethod currentMethod = context.method;
boolean skipTracing =
registerDeferredActionForDeadProtoBuilder(
- type, currentMethod, () -> workList.enqueueTraceNewInstanceAction(type, currentMethod));
+ type, currentMethod, () -> workList.enqueueTraceNewInstanceAction(type, context));
if (skipTracing) {
return false;
}
- return traceNewInstance(type, currentMethod, KeepReason.instantiatedIn(currentMethod));
+ return traceNewInstance(type, context, KeepReason.instantiatedIn(currentMethod));
}
- boolean traceNewInstanceFromLambda(DexType type, DexEncodedMethod currentMethod) {
- return traceNewInstance(
- type, currentMethod, KeepReason.invokedFromLambdaCreatedIn(currentMethod));
+ boolean traceNewInstanceFromLambda(DexType type, ProgramMethod context) {
+ return traceNewInstance(type, context, KeepReason.invokedFromLambdaCreatedIn(context.method));
}
- private boolean traceNewInstance(
- DexType type, DexEncodedMethod currentMethod, KeepReason keepReason) {
+ private boolean traceNewInstance(DexType type, ProgramMethod context, KeepReason keepReason) {
+ DexEncodedMethod currentMethod = context.method;
DexProgramClass clazz = getProgramClassOrNull(type);
if (clazz != null) {
if (clazz.isInterface()) {
@@ -1416,7 +1418,7 @@
// It is valid to have an invoke-direct instruction in a default interface method that
// targets another default method in the same interface (see testInvokeSpecialToDefault-
// Method). In a class, that would lead to a verification error.
- if (encodedMethod.isVirtualMethod()
+ if (encodedMethod.isNonPrivateVirtualMethod()
&& virtualMethodsTargetedByInvokeDirect.add(encodedMethod.method)) {
enqueueMarkMethodLiveAction(clazz, encodedMethod, reason);
}
@@ -1658,7 +1660,7 @@
}
private void markResolutionAsLive(DexClass libraryClass, ResolutionResult resolution) {
- if (resolution.isValidVirtualTarget(options)) {
+ if (resolution.isVirtualTarget()) {
DexEncodedMethod target = resolution.getSingleTarget();
DexProgramClass targetHolder = getProgramClassOrNull(target.method.holder);
if (targetHolder != null
@@ -1960,16 +1962,8 @@
}
}
- // Package protected due to entry point from worklist.
- void markVirtualMethodAsReachable(DexMethod method, boolean interfaceInvoke, KeepReason reason) {
- markVirtualMethodAsReachable(method, interfaceInvoke, reason, (x, y) -> true);
- }
-
private void markVirtualMethodAsReachable(
- DexMethod method,
- boolean interfaceInvoke,
- KeepReason reason,
- BiPredicate<DexProgramClass, DexEncodedMethod> possibleTargetsFilter) {
+ DexMethod method, boolean interfaceInvoke, ProgramMethod contextOrNull, KeepReason reason) {
if (method.holder.isArrayType()) {
// This is an array type, so the actual class will be generated at runtime. We treat this
// like an invoke on a direct subtype of java.lang.Object that has no further subtypes.
@@ -2002,9 +1996,18 @@
// Otherwise, the resolution target is marked and cached, and all possible targets identified.
resolution = findAndMarkResolutionTarget(method, interfaceInvoke, reason);
+ if (contextOrNull != null
+ && !resolution.isUnresolved()
+ && !AccessControl.isMethodAccessible(
+ resolution.method, holder, contextOrNull.holder, appInfo)) {
+ // Not accessible from this context, so this call will cause a runtime exception.
+ // Note that the resolution is not cached, as another call context may be valid.
+ return;
+ }
+
+ // The resolution is unresolved or accessible, both are context independent, so cache it.
virtualTargetsMarkedAsReachable.put(method, resolution);
- if (resolution.isUnresolved()
- || !SingleResolutionResult.isValidVirtualTarget(options, resolution.method)) {
+ if (resolution.isUnresolved() || !resolution.method.isVirtualMethod()) {
// There is no valid resolution, so any call will lead to a runtime exception.
return;
}
@@ -2027,24 +2030,20 @@
if (encodedPossibleTarget.isAbstract()) {
continue;
}
- markPossibleTargetsAsReachable(resolution, possibleTargetsFilter, encodedPossibleTarget);
+ markPossibleTargetsAsReachable(resolution, encodedPossibleTarget);
}
}
private void markPossibleTargetsAsReachable(
MarkedResolutionTarget reason,
- BiPredicate<DexProgramClass, DexEncodedMethod> possibleTargetsFilter,
DexEncodedMethod encodedPossibleTarget) {
- assert encodedPossibleTarget.isVirtualMethod() || options.canUseNestBasedAccess();
+ assert encodedPossibleTarget.isVirtualMethod();
assert !encodedPossibleTarget.isAbstract();
DexMethod possibleTarget = encodedPossibleTarget.method;
DexProgramClass clazz = getProgramClassOrNull(possibleTarget.holder);
if (clazz == null) {
return;
}
- if (!possibleTargetsFilter.test(clazz, encodedPossibleTarget)) {
- return;
- }
ReachableVirtualMethodsSet reachable =
reachableVirtualMethods.computeIfAbsent(clazz, ignore -> new ReachableVirtualMethodsSet());
if (!reachable.add(encodedPossibleTarget, reason)) {
@@ -2500,10 +2499,8 @@
// A virtual method. Mark it as reachable so that subclasses, if instantiated, keep
// their overrides. However, we don't mark it live, as a keep rule might not imply that
// the corresponding class is live.
- if (!holder.isInterface()) {
- workList.enqueueMarkReachableVirtualAction(method, reason);
- } else {
- workList.enqueueMarkReachableInterfaceAction(method, reason);
+ markVirtualMethodAsReachable(method, holder.isInterface(), null, reason);
+ if (holder.isInterface()) {
// Reachability for default methods is based on live subtypes in general. For keep rules,
// we need special handling as we essentially might have live subtypes that are outside of
// the current compilation unit. Keep either the default-method or its implementation
@@ -2543,7 +2540,7 @@
DexProgramClass clazz, DexEncodedMethod method) {
assert method.isVirtualMethod();
- if (method.isAbstract()) {
+ if (method.isAbstract() || method.isPrivateMethod()) {
return false;
}
@@ -2719,7 +2716,9 @@
if (clazz == null) {
return;
}
- if (!clazz.isInterface()) {
+ if (clazz.isInterface()) {
+ markTypeAsLive(clazz.type, KeepReason.reflectiveUseIn(method));
+ } else {
markInstantiated(clazz, null, KeepReason.reflectiveUseIn(method));
if (clazz.hasDefaultInitializer()) {
DexEncodedMethod initializer = clazz.getDefaultInitializer();
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
index ff6ece0..d98d7c9 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.shaking.GraphReporter.KeepReasonWitness;
import java.util.ArrayDeque;
import java.util.Queue;
@@ -36,36 +37,6 @@
}
}
- static class MarkReachableVirtualAction extends EnqueuerAction {
- final DexMethod target;
- final KeepReason reason;
-
- MarkReachableVirtualAction(DexMethod target, KeepReason reason) {
- this.target = target;
- this.reason = reason;
- }
-
- @Override
- public void run(Enqueuer enqueuer) {
- enqueuer.markVirtualMethodAsReachable(target, false, reason);
- }
- }
-
- static class MarkReachableInterfaceAction extends EnqueuerAction {
- final DexMethod target;
- final KeepReason reason;
-
- public MarkReachableInterfaceAction(DexMethod target, KeepReason reason) {
- this.target = target;
- this.reason = reason;
- }
-
- @Override
- public void run(Enqueuer enqueuer) {
- enqueuer.markVirtualMethodAsReachable(target, true, reason);
- }
- }
-
static class MarkReachableSuperAction extends EnqueuerAction {
final DexMethod target;
final DexEncodedMethod context;
@@ -194,22 +165,22 @@
@Override
public void run(Enqueuer enqueuer) {
- enqueuer.traceInvokeDirect(invokedMethod, currentHolder, currentMethod);
+ enqueuer.traceInvokeDirect(invokedMethod, new ProgramMethod(currentHolder, currentMethod));
}
}
static class TraceNewInstanceAction extends EnqueuerAction {
final DexType type;
- final DexEncodedMethod currentMethod;
+ final ProgramMethod context;
- TraceNewInstanceAction(DexType type, DexEncodedMethod currentMethod) {
+ TraceNewInstanceAction(DexType type, ProgramMethod context) {
this.type = type;
- this.currentMethod = currentMethod;
+ this.context = context;
}
@Override
public void run(Enqueuer enqueuer) {
- enqueuer.traceNewInstance(type, currentMethod);
+ enqueuer.traceNewInstance(type, context);
}
}
@@ -251,14 +222,6 @@
queue.add(new MarkReachableDirectAction(method, reason));
}
- void enqueueMarkReachableVirtualAction(DexMethod method, KeepReason reason) {
- queue.add(new MarkReachableVirtualAction(method, reason));
- }
-
- void enqueueMarkReachableInterfaceAction(DexMethod method, KeepReason reason) {
- queue.add(new MarkReachableInterfaceAction(method, reason));
- }
-
void enqueueMarkReachableSuperAction(DexMethod method, DexEncodedMethod from) {
queue.add(new MarkReachableSuperAction(method, from));
}
@@ -306,9 +269,8 @@
queue.add(new TraceInvokeDirectAction(invokedMethod, currentHolder, currentMethod));
}
- public void enqueueTraceNewInstanceAction(DexType type, DexEncodedMethod currentMethod) {
- assert currentMethod.isProgramMethod(appView);
- queue.add(new TraceNewInstanceAction(type, currentMethod));
+ public void enqueueTraceNewInstanceAction(DexType type, ProgramMethod context) {
+ queue.add(new TraceNewInstanceAction(type, context));
}
public void enqueueTraceStaticFieldRead(DexField field, DexEncodedMethod currentMethod) {
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index b86857a..b842680 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -533,8 +533,7 @@
}
ResolutionResult resolutionResult =
appView.appInfo().resolveMethod(originalClazz, method.method);
- if (!resolutionResult.isValidVirtualTarget(appView.options())
- || !resolutionResult.isSingleResolution()) {
+ if (!resolutionResult.isVirtualTarget() || !resolutionResult.isSingleResolution()) {
return;
}
DexEncodedMethod methodToKeep = resolutionResult.getSingleTarget();
diff --git a/src/main/java/com/android/tools/r8/utils/AssertionConfigurationWithDefault.java b/src/main/java/com/android/tools/r8/utils/AssertionConfigurationWithDefault.java
new file mode 100644
index 0000000..7dc7b33
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/AssertionConfigurationWithDefault.java
@@ -0,0 +1,34 @@
+// Copyright (c) 2020, 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.utils;
+
+import com.android.tools.r8.AssertionsConfiguration;
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformationScope;
+import java.util.List;
+
+public class AssertionConfigurationWithDefault {
+
+ public final AssertionTransformation defautlTransformation;
+ public final List<AssertionsConfiguration> assertionsConfigurations;
+
+ public AssertionConfigurationWithDefault(
+ AssertionTransformation defautlTransformation,
+ List<AssertionsConfiguration> assertionsConfigurations) {
+ this.defautlTransformation = defautlTransformation;
+ assert assertionsConfigurations != null;
+ this.assertionsConfigurations = assertionsConfigurations;
+ }
+
+ public boolean isPassthroughAll() {
+ if (assertionsConfigurations.size() == 0) {
+ return defautlTransformation == AssertionTransformation.PASSTHROUGH;
+ }
+ return assertionsConfigurations.size() == 1
+ && assertionsConfigurations.get(0).getScope() == AssertionTransformationScope.ALL
+ && assertionsConfigurations.get(0).getTransformation()
+ == AssertionTransformation.PASSTHROUGH;
+ }
+}
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 dccd176..fd51973 100644
--- a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
@@ -329,6 +329,10 @@
return className.replace(JAVA_PACKAGE_SEPARATOR, DESCRIPTOR_PACKAGE_SEPARATOR);
}
+ public static String getJavaTypeFromBinaryName(String className) {
+ return className.replace(DESCRIPTOR_PACKAGE_SEPARATOR, JAVA_PACKAGE_SEPARATOR);
+ }
+
public static String getBinaryNameFromDescriptor(String classDescriptor) {
assert isClassDescriptor(classDescriptor);
return classDescriptor.substring(1, classDescriptor.length() - 1);
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 9b47305..1425bc0 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -5,7 +5,6 @@
import static com.google.common.base.Predicates.not;
-import com.android.tools.r8.AssertionsConfiguration;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.DataResourceConsumer;
@@ -243,6 +242,7 @@
// TODO(b/125282093): Enable member value propagation for instance fields.
public boolean enableValuePropagationForInstanceFields = false;
public boolean enableUninstantiatedTypeOptimization = true;
+ public boolean enableUninstantiatedTypeOptimizationForInterfaces = true;
// TODO(b/138917494): Disable until we have numbers on potential performance penalties.
public boolean enableRedundantConstNumberOptimization = false;
@@ -366,6 +366,9 @@
}
}
}
+ if (desugarGraphConsumer != null) {
+ desugarGraphConsumer.finished();
+ }
}
public boolean shouldDesugarNests() {
@@ -434,7 +437,7 @@
public boolean ignoreMissingClasses = false;
// EXPERIMENTAL flag to get behaviour as close to Proguard as possible.
public boolean forceProguardCompatibility = false;
- public AssertionsConfiguration assertionsConfiguration = null;
+ public AssertionConfigurationWithDefault assertionsConfiguration = null;
public boolean configurationDebugging = false;
// Don't convert Code objects to IRCode.
diff --git a/src/main/java/com/android/tools/r8/utils/SelfRetraceTest.java b/src/main/java/com/android/tools/r8/utils/SelfRetraceTest.java
index ab891e8..dc7ce2b 100644
--- a/src/main/java/com/android/tools/r8/utils/SelfRetraceTest.java
+++ b/src/main/java/com/android/tools/r8/utils/SelfRetraceTest.java
@@ -22,7 +22,7 @@
}
public static void test() {
- if (System.getenv(SELFRETRACETEST_ENVIRONMENT_VAR) != null) {
+ if (System.getProperty(SELFRETRACETEST_ENVIRONMENT_VAR) != null) {
new SelfRetraceTest().foo1();
}
}
diff --git a/src/test/apiUsageSample/com/android/tools/apiusagesample/D8ApiUsageSample.java b/src/test/apiUsageSample/com/android/tools/apiusagesample/D8ApiUsageSample.java
index 23dc7e0..41b328c 100644
--- a/src/test/apiUsageSample/com/android/tools/apiusagesample/D8ApiUsageSample.java
+++ b/src/test/apiUsageSample/com/android/tools/apiusagesample/D8ApiUsageSample.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.D8;
import com.android.tools.r8.D8Command;
+import com.android.tools.r8.DesugarGraphConsumer;
import com.android.tools.r8.DexFilePerClassFileConsumer;
import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.DiagnosticsHandler;
@@ -335,6 +336,7 @@
.addLibraryFiles(libraries)
.addProgramFiles(inputs)
.setDisableDesugaring(false)
+ .setDesugarGraphConsumer(new MyDesugarGraphConsumer())
.build());
} catch (CompilationFailedException e) {
throw new RuntimeException("Unexpected compilation exceptions", e);
@@ -498,4 +500,15 @@
}
}
}
+
+ private static class MyDesugarGraphConsumer implements DesugarGraphConsumer {
+
+ @Override
+ public void accept(Origin dependent, Origin dependency) {
+ }
+
+ public void finished() {
+
+ }
+ }
}
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index ae63272..db0baa3 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -525,6 +525,13 @@
"Missing parameter", handler -> parse(handler, "--output"));
}
+ @Test
+ public void desugaredLibrary() throws CompilationFailedException {
+ D8Command d8Command = parse("--desugared-lib", "src/library_desugar/desugar_jdk_libs.json");
+ assertFalse(
+ d8Command.getInternalOptions().desugaredLibraryConfiguration.getRewritePrefix().isEmpty());
+ }
+
private D8Command parse(String... args) throws CompilationFailedException {
return D8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
}
diff --git a/src/test/java/com/android/tools/r8/L8CommandTest.java b/src/test/java/com/android/tools/r8/L8CommandTest.java
index 0a55b8a..558ba0d 100644
--- a/src/test/java/com/android/tools/r8/L8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/L8CommandTest.java
@@ -4,10 +4,12 @@
package com.android.tools.r8;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.dex.Marker;
+import com.android.tools.r8.origin.EmbeddedOrigin;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import java.nio.charset.StandardCharsets;
@@ -68,6 +70,27 @@
Marker marker = markers.iterator().next();
}
+ @Test
+ public void testMarkerCommandLine() throws Throwable {
+ Path output = temp.newFolder().toPath().resolve("desugar_jdk_libs.zip");
+ L8Command l8Command =
+ parse(
+ ToolHelper.getDesugarJDKLibs().toString(),
+ "--lib",
+ ToolHelper.getAndroidJar(AndroidApiLevel.P).toString(),
+ "--min-api",
+ "20",
+ "--desugared-lib",
+ ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString(),
+ "--output",
+ output.toString());
+ L8.run(l8Command);
+ Collection<Marker> markers = ExtractMarker.extractMarkerFromDexFile(output);
+ // TODO(b/134732760): Shouldn't we remove the D8/R8 marker?
+ assertEquals(2, markers.size());
+ Marker marker = markers.iterator().next();
+ }
+
private L8Command.Builder prepareBuilder(DiagnosticsHandler handler) {
return L8Command.builder(handler)
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
@@ -156,4 +179,15 @@
assertTrue(builder2.isShrinking());
assertNotNull(builder2.build().getR8Command());
}
+
+ @Test
+ public void desugaredLibrary() throws CompilationFailedException {
+ L8Command l8Command = parse("--desugared-lib", "src/library_desugar/desugar_jdk_libs.json");
+ assertFalse(
+ l8Command.getInternalOptions().desugaredLibraryConfiguration.getRewritePrefix().isEmpty());
+ }
+
+ private L8Command parse(String... args) throws CompilationFailedException {
+ return L8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java
index 57d278e..165b510 100644
--- a/src/test/java/com/android/tools/r8/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -206,7 +206,8 @@
Path mainDexRules1 = temp.newFile("main-dex-1.rules").toPath();
Path mainDexRules2 = temp.newFile("main-dex-2.rules").toPath();
parse("--main-dex-rules", mainDexRules1.toString());
- parse("--main-dex-rules", mainDexRules1.toString(), "--main-dex-rules", mainDexRules2.toString());
+ parse(
+ "--main-dex-rules", mainDexRules1.toString(), "--main-dex-rules", mainDexRules2.toString());
}
@Test(expected = CompilationFailedException.class)
@@ -637,6 +638,13 @@
"Missing parameter", handler -> parse(handler, "--output"));
}
+ @Test
+ public void desugaredLibrary() throws CompilationFailedException {
+ R8Command r8Command = parse("--desugared-lib", "src/library_desugar/desugar_jdk_libs.json");
+ assertFalse(
+ r8Command.getInternalOptions().desugaredLibraryConfiguration.getRewritePrefix().isEmpty());
+ }
+
private R8Command parse(String... args) throws CompilationFailedException {
return R8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
}
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
index a19cfec..0ba8510 100644
--- a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
@@ -5,8 +5,12 @@
import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
import static com.google.common.io.ByteStreams.toByteArray;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import com.android.tools.r8.ArchiveClassFileProvider;
import com.android.tools.r8.CompilationFailedException;
@@ -18,8 +22,12 @@
import com.android.tools.r8.TestRuntime;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.retrace.Retrace;
+import com.android.tools.r8.retrace.RetraceCommand;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.Pair;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.Lists;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
@@ -104,14 +112,59 @@
@Test
public void testRetrace() throws IOException {
- ProcessResult result =
+ ProcessResult processResult =
ToolHelper.runProcess(
- new ProcessBuilder(
- "python",
- Paths.get(ToolHelper.TOOLS_DIR, "test_self_retrace.py").toString(),
- r8R8Release.getFirst().toString(),
- r8R8Release.getSecond().toString()));
- assertEquals(result.toString(), 0, result.exitCode);
+ new ProcessBuilder()
+ .command(
+ parameters.getRuntime().asCf().getJavaExecutable().toString(),
+ "-DR8_THROW_EXCEPTION_FOR_TESTING_RETRACE=1",
+ "-cp",
+ r8R8Release.getFirst().toString(),
+ "com.android.tools.r8.R8",
+ "--help"));
+ assertNotEquals(0, processResult.exitCode);
+ assertThat(processResult.stderr, not(containsString("SelfRetraceTest")));
+
+ List<String> expectedStackTrace =
+ Lists.newArrayList(
+ "Intentional exception for testing retrace.",
+ "com.android.tools.r8.utils.SelfRetraceTest.foo3(SelfRetraceTest.java:13)",
+ "com.android.tools.r8.utils.SelfRetraceTest.foo2(SelfRetraceTest.java:17)",
+ "com.android.tools.r8.utils.SelfRetraceTest.foo1(SelfRetraceTest.java:21)",
+ "com.android.tools.r8.utils.SelfRetraceTest.test(SelfRetraceTest.java:26)",
+ "com.android.tools.r8.R8.run(R8.java:");
+
+ RetraceCommand retraceCommand =
+ RetraceCommand.builder()
+ .setStackTrace(StringUtils.splitLines(processResult.stderr))
+ .setProguardMapProducer(
+ () -> {
+ Path mappingFile = r8R8Release.getSecond();
+ try {
+ return new String(Files.readAllBytes(mappingFile));
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new RuntimeException(
+ "Could not read mapping file " + mappingFile.toString());
+ }
+ })
+ .setRetracedStackTraceConsumer(
+ retraced -> {
+ int expectedIndex = -1;
+ for (String line : retraced) {
+ if (expectedIndex >= expectedStackTrace.size()) {
+ break;
+ } else if (expectedIndex == -1 && line.contains("java.lang.RuntimeException")) {
+ expectedIndex = 0;
+ }
+ if (expectedIndex > -1) {
+ assertThat(line, containsString(expectedStackTrace.get(expectedIndex++)));
+ }
+ }
+ assertEquals(expectedStackTrace.size(), expectedIndex);
+ })
+ .build();
+ Retrace.run(retraceCommand);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
index cd3e666..2330854 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
@@ -90,11 +90,6 @@
.addInnerClasses(CustomCollectionTest.class)
.setMinApi(parameters.getApiLevel())
.addKeepClassAndMembersRules(Executor.class)
- .addOptionsModification(
- options -> {
- // TODO(b/140233505): Allow devirtualization once fixed.
- options.enableDevirtualization = false;
- })
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
.inspect(
@@ -145,22 +140,28 @@
instr ->
instr.toString().contains("$-EL")
|| instr.toString().contains("Comparator$-CC")));
- inherited.streamInstructions().forEach(CustomCollectionTest::assertEmulatedInterfaceDispatch);
+ inherited.streamInstructions().forEach(x -> assertInheritedDispatchCorrect(x, r8));
}
- private static void assertEmulatedInterfaceDispatch(InstructionSubject instructionSubject) {
+ private void assertInheritedDispatchCorrect(InstructionSubject instructionSubject, boolean r8) {
if (!instructionSubject.isConstString(JumboStringMode.ALLOW)) {
for (String s : new String[] {"stream", "parallelStream", "spliterator", "sort"}) {
if (instructionSubject.toString().contains(s)) {
- assertTrue(instructionSubject.isInvokeStatic());
- assertTrue(
- instructionSubject.toString().contains("$-EL")
- || instructionSubject.toString().contains("Comparator$-CC"));
+ if (!r8 || instructionSubject.isInvokeStatic()) {
+ assertTrue(instructionSubject.isInvokeStatic());
+ assertTrue(
+ instructionSubject.toString().contains("$-EL")
+ || instructionSubject.toString().contains("Comparator$-CC"));
+ } else {
+ // Has been devirtualized.
+ assertTrue(instructionSubject.isInvokeVirtual());
+ }
}
}
}
}
+
static class Executor {
// In directTypes() the collections use directly their type which implements a j$ interface
diff --git a/src/test/java/com/android/tools/r8/desugar/graph/DesugarGraphTestConsumer.java b/src/test/java/com/android/tools/r8/desugar/graph/DesugarGraphTestConsumer.java
index 2111b7d..186b01b 100644
--- a/src/test/java/com/android/tools/r8/desugar/graph/DesugarGraphTestConsumer.java
+++ b/src/test/java/com/android/tools/r8/desugar/graph/DesugarGraphTestConsumer.java
@@ -3,6 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.desugar.graph;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
import com.android.tools.r8.DesugarGraphConsumer;
import com.android.tools.r8.origin.Origin;
import java.util.Collections;
@@ -13,13 +16,16 @@
public class DesugarGraphTestConsumer implements DesugarGraphConsumer {
+ private boolean finished = false;
private Map<Origin, Set<Origin>> edges = new HashMap<>();
public boolean contains(Origin dependency, Origin dependent) {
+ assertTrue(finished);
return edges.getOrDefault(dependency, Collections.emptySet()).contains(dependent);
}
public int totalEdgeCount() {
+ assertTrue(finished);
int count = 0;
for (Set<Origin> dependents : edges.values()) {
count += dependents.size();
@@ -29,6 +35,13 @@
@Override
public synchronized void accept(Origin dependent, Origin dependency) {
+ assertFalse(finished);
edges.computeIfAbsent(dependency, s -> new HashSet<>()).add(dependent);
}
+
+ @Override
+ public void finished() {
+ assertFalse(finished);
+ finished = true;
+ }
}
diff --git a/src/test/java/com/android/tools/r8/desugar/graph/DesugarGraphUtils.java b/src/test/java/com/android/tools/r8/desugar/graph/DesugarGraphUtils.java
index af3c6a0..937540e 100644
--- a/src/test/java/com/android/tools/r8/desugar/graph/DesugarGraphUtils.java
+++ b/src/test/java/com/android/tools/r8/desugar/graph/DesugarGraphUtils.java
@@ -12,8 +12,12 @@
public static Origin addClassWithOrigin(Class<?> clazz, D8TestBuilder builder)
throws IOException {
- Origin origin = makeOrigin(clazz.getTypeName());
- builder.getBuilder().addClassProgramData(ToolHelper.getClassAsBytes(clazz), origin);
+ return addClassWithOrigin(clazz.getTypeName(), ToolHelper.getClassAsBytes(clazz), builder);
+ }
+
+ public static Origin addClassWithOrigin(String name, byte[] bytes, D8TestBuilder builder) {
+ Origin origin = makeOrigin(name);
+ builder.getBuilder().addClassProgramData(bytes, origin);
return origin;
}
diff --git a/src/test/java/com/android/tools/r8/desugar/graph/InterfaceToImplementingClassDependencyTest.java b/src/test/java/com/android/tools/r8/desugar/graph/InterfaceToImplementingClassDependencyTest.java
index 7f76cad..705410a 100644
--- a/src/test/java/com/android/tools/r8/desugar/graph/InterfaceToImplementingClassDependencyTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/graph/InterfaceToImplementingClassDependencyTest.java
@@ -36,7 +36,7 @@
// Emtpy.
}
- public class A implements I {
+ public static class A implements I {
// Empty.
}
@@ -80,8 +80,8 @@
.assertSuccessWithOutputLines("Hello World!");
// If API level indicates desugaring is needed check the edges are reported.
if (parameters.getApiLevel().getLevel() < AndroidApiLevel.N.getLevel()) {
- assertEquals(1, consumer.totalEdgeCount());
assertTrue(consumer.contains(originI, originA));
+ assertEquals(1, consumer.totalEdgeCount());
} else {
assertEquals(0, consumer.totalEdgeCount());
}
diff --git a/src/test/java/com/android/tools/r8/desugar/graph/LambdaDependencyTest.java b/src/test/java/com/android/tools/r8/desugar/graph/LambdaDependencyTest.java
new file mode 100644
index 0000000..d7f39e5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/graph/LambdaDependencyTest.java
@@ -0,0 +1,81 @@
+// 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.desugar.graph;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.D8TestBuilder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
+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 LambdaDependencyTest extends TestBase {
+
+ public interface I {
+ void foo();
+ }
+
+ public static class A {
+ void bar(I i) {
+ i.foo();
+ }
+ }
+
+ public static class TestClass {
+
+ public static void main(String[] args) {
+ new A().bar(() -> System.out.println("lambda!"));
+ }
+ }
+
+ // Test runner follows.
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+ }
+
+ private final TestParameters parameters;
+
+ public LambdaDependencyTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ if (parameters.isCfRuntime()) {
+ testForJvm()
+ .addProgramClasses(I.class, A.class, TestClass.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("lambda!");
+ } else {
+ D8TestBuilder builder = testForD8();
+ DesugarGraphTestConsumer consumer = new DesugarGraphTestConsumer();
+ builder.getBuilder().setDesugarGraphConsumer(consumer);
+ Origin originI = DesugarGraphUtils.addClassWithOrigin(I.class, builder);
+ Origin originA = DesugarGraphUtils.addClassWithOrigin(A.class, builder);
+ Origin originMain = DesugarGraphUtils.addClassWithOrigin(TestClass.class, builder);
+ builder
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("lambda!");
+ // If API level indicates desugaring is needed check the edges are reported.
+ if (parameters.getApiLevel().getLevel() < AndroidApiLevel.N.getLevel()) {
+ // Generated lambda class in TestClass.main depends on potential default methods in I.
+ assertTrue(consumer.contains(originI, originMain));
+ assertEquals(1, consumer.totalEdgeCount());
+ } else {
+ assertEquals(0, consumer.totalEdgeCount());
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/graph/NestDependencyTest.java b/src/test/java/com/android/tools/r8/desugar/graph/NestDependencyTest.java
new file mode 100644
index 0000000..c67f497
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/graph/NestDependencyTest.java
@@ -0,0 +1,87 @@
+// 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.desugar.graph;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.D8TestBuilder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.origin.Origin;
+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 NestDependencyTest extends TestBase {
+
+ public static class Host {}
+
+ public static class Member1 {}
+
+ public static class Member2 {}
+
+ public static class TestClass {
+
+ public static void main(String[] args) {
+ System.out.println("Hello World!");
+ }
+ }
+
+ // Test runner follows.
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+ .withDexRuntimes()
+ .withAllApiLevels()
+ .build();
+ }
+
+ private final TestParameters parameters;
+
+ public NestDependencyTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private byte[] inNest(Class<?> clazz) throws Exception {
+ return transformer(clazz).setNest(Host.class, Member1.class, Member2.class).transform();
+ }
+
+ @Test
+ public void test() throws Exception {
+ if (parameters.isCfRuntime()) {
+ testForJvm()
+ .addProgramClassFileData(inNest(Host.class), inNest(Member1.class), inNest(Member2.class))
+ .addProgramClasses(TestClass.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello World!");
+ } else {
+ D8TestBuilder builder = testForD8();
+ DesugarGraphTestConsumer consumer = new DesugarGraphTestConsumer();
+ builder.getBuilder().setDesugarGraphConsumer(consumer);
+ Origin originHost = DesugarGraphUtils.addClassWithOrigin("Host", inNest(Host.class), builder);
+ Origin originMember1 =
+ DesugarGraphUtils.addClassWithOrigin("Member1", inNest(Member1.class), builder);
+ Origin originMember2 =
+ DesugarGraphUtils.addClassWithOrigin("Member2", inNest(Member2.class), builder);
+ builder
+ .addProgramClasses(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello World!");
+ // Currently there is no level at which nest desugaring is not needed.
+ assertTrue(consumer.contains(originHost, originMember1));
+ assertTrue(consumer.contains(originHost, originMember2));
+ assertTrue(consumer.contains(originMember1, originHost));
+ assertTrue(consumer.contains(originMember2, originHost));
+ assertEquals(4, consumer.totalEdgeCount());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
index aa52d82..dbdc654 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
@@ -10,7 +10,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -30,7 +30,11 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withDexRuntimesStartingFromIncluding(Version.V5_1_1).build();
+ return getTestParameters()
+ // Use of APIs, such as java.util.functions.* are only available from 24+
+ .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
+ .withDexRuntimes()
+ .build();
}
private static final Path MAIN_KEEP = Paths.get("src/main/keep.txt");
@@ -42,6 +46,7 @@
@Test
public void testR8CompiledWithR8() throws Exception {
testForR8(parameters.getBackend())
+ .setMinApi(parameters.getApiLevel())
.addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR_11)
.addKeepRuleFiles(MAIN_KEEP)
.addOptionsModification(opt -> opt.ignoreMissingClasses = true)
diff --git a/src/test/java/com/android/tools/r8/dexfilemerger/NonAsciiClassNameChecksumTest.java b/src/test/java/com/android/tools/r8/dexfilemerger/NonAsciiClassNameChecksumTest.java
new file mode 100644
index 0000000..8e4700b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/dexfilemerger/NonAsciiClassNameChecksumTest.java
@@ -0,0 +1,100 @@
+// Copyright (c) 2020, 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.dexfilemerger;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.io.IOException;
+import java.nio.file.Path;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class NonAsciiClassNameChecksumTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("Hello æ");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ public NonAsciiClassNameChecksumTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private String getTransformedName(Class<?> clazz) {
+ return getTransformedName(clazz.getTypeName());
+ }
+
+ private String getTransformedName(String typeName) {
+ return NonAsciiClassNameChecksumTest.class.getTypeName()
+ + "$"
+ + (typeName.equals(TestClaass.class.getTypeName()) ? "TestClåss" : "TæstClass");
+ }
+
+ private byte[] getTransform(Class<?> clazz) throws IOException {
+ return transformer(clazz)
+ .setClassDescriptor(DescriptorUtils.javaTypeToDescriptor(getTransformedName(clazz)))
+ .transform();
+ }
+
+ @Test
+ public void test() throws Exception {
+ Path intermediate1 = compileIntermediate(TaestClass.class);
+ Path intermediate2 = compileIntermediate(TestClaass.class);
+ testForD8()
+ .addProgramFiles(intermediate1, intermediate2)
+ .setMinApi(parameters.getApiLevel())
+ .setIncludeClassesChecksum(true)
+ .run(parameters.getRuntime(), getTransformedName(TaestClass.class))
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(inspector -> {
+ checkIncludesChecksum(inspector, TaestClass.class);
+ checkIncludesChecksum(inspector, TestClaass.class);
+ });
+ }
+
+ private Path compileIntermediate(Class<?> clazz) throws Exception {
+ return testForD8()
+ .setOutputMode(OutputMode.DexFilePerClassFile)
+ .addProgramClassFileData(getTransform(clazz))
+ .setMinApi(parameters.getApiLevel())
+ .setIncludeClassesChecksum(true)
+ .compile()
+ .inspect(inspector -> checkIncludesChecksum(inspector, clazz))
+ .writeToZip();
+ }
+
+ private void checkIncludesChecksum(CodeInspector inspector, Class<?> clazz) {
+ ClassSubject classSubject = inspector.clazz(getTransformedName(clazz));
+ assertThat(classSubject, isPresent());
+ assertTrue(classSubject.getDexClass().asProgramClass().getChecksum() > 0);
+ }
+
+ static class TaestClass {
+ public static void main(String[] args) {
+ System.out.println("Hello æ");
+ }
+ }
+
+ static class TestClaass {
+ public static void main(String[] args) {
+ System.out.println("Hello å");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/ForNameInterfaceTest.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/ForNameInterfaceTest.java
new file mode 100644
index 0000000..31f8463
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/ForNameInterfaceTest.java
@@ -0,0 +1,60 @@
+// 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.ir.optimize.reflection;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+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 ForNameInterfaceTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public ForNameInterfaceTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ public interface I {}
+
+ public @interface J {}
+
+ public static class Main {
+
+ public static void main(String[] args) throws ClassNotFoundException {
+ Class<?> aClass =
+ Class.forName("com.android.tools.r8.ir.optimize.reflection.ForNameInterfaceTest$I");
+ System.out.println(aClass.getName());
+ aClass = Class.forName("com.android.tools.r8.ir.optimize.reflection.ForNameInterfaceTest$J");
+ System.out.println(aClass.getName());
+ }
+ }
+
+ @Test
+ public void testForNameOnInterface()
+ throws ExecutionException, CompilationFailedException, IOException {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(ForNameInterfaceTest.class)
+ .addKeepMainRule(Main.class)
+ .minification(false)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(
+ "com.android.tools.r8.ir.optimize.reflection.ForNameInterfaceTest$I",
+ "com.android.tools.r8.ir.optimize.reflection.ForNameInterfaceTest$J");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/B146957343.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/B146957343.java
new file mode 100644
index 0000000..289e095
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/B146957343.java
@@ -0,0 +1,93 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.uninstantiatedtypes;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import java.io.IOException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class B146957343 extends TestBase implements Opcodes {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+ }
+
+ public B146957343(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private byte[] getAimplementsI() throws IOException {
+ return transformer(A.class).setImplements(I.class).transform();
+ }
+
+ @Test
+ public void testWithoutR8() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(I.class, J.class, Main.class)
+ .addProgramClassFileData(getAimplementsI())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("In A.f()");
+ }
+
+ @Test
+ public void testWithUninstantiatedTypeOptimizationForInterfaces() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(I.class, J.class, Main.class)
+ .addProgramClassFileData(getAimplementsI())
+ .addKeepMainRule(Main.class)
+ .addKeepRules("-keep class **A { createA(); }")
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(
+ options -> options.enableUninstantiatedTypeOptimizationForInterfaces = true)
+ .compile()
+ .disassemble()
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatThrows(NullPointerException.class);
+ }
+
+ @Test
+ public void testWithoutUninstantiatedTypeOptimizationForInterfaces() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(I.class, J.class, Main.class)
+ .addProgramClassFileData(getAimplementsI())
+ .addKeepMainRule(Main.class)
+ .addKeepRules("-keep class **A { createA(); }")
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(
+ options -> options.enableUninstantiatedTypeOptimizationForInterfaces = false)
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("In A.f()");
+ }
+
+ public interface I {}
+
+ public interface J extends I {}
+
+ public static class A implements J {
+ public static J createA() {
+ return new A();
+ }
+
+ public void f() {
+ System.out.println("In A.f()");
+ }
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ ((A) A.createA()).f();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/PrivateInvokeVirtualTest.java b/src/test/java/com/android/tools/r8/resolution/PrivateInvokeVirtualTest.java
new file mode 100644
index 0000000..543ecad
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/PrivateInvokeVirtualTest.java
@@ -0,0 +1,95 @@
+// 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.resolution;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class PrivateInvokeVirtualTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public PrivateInvokeVirtualTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ public static class UInt implements Comparable<UInt> {
+
+ private final int val;
+
+ public UInt(int val) {
+ this.val = val;
+ }
+
+ private int compareToHelper(int i) {
+ return Integer.compare(val, i);
+ }
+
+ @Override
+ public int compareTo(UInt o) {
+ return compareToHelper(o.val);
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(new UInt(3).compareTo(new UInt(args.length)));
+ }
+ }
+
+ @Test
+ public void testRuntime() throws IOException, CompilationFailedException, ExecutionException {
+ testForRuntime(parameters)
+ .addProgramClassFileData(getUIntWithTransformedInvoke())
+ .addProgramClasses(Main.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("1");
+ }
+
+ @Test
+ public void testPrivateInvokeVirtual()
+ throws IOException, CompilationFailedException, ExecutionException {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class)
+ .addProgramClassFileData(getUIntWithTransformedInvoke())
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("1");
+ }
+
+ private byte[] getUIntWithTransformedInvoke() throws IOException {
+ return transformer(UInt.class)
+ .transformMethodInsnInMethod(
+ "compareTo",
+ (opcode, owner, name, descriptor, isInterface, continuation) -> {
+ if (name.contains("compareToHelper")) {
+ assertEquals(Opcodes.INVOKESPECIAL, opcode);
+ continuation.apply(Opcodes.INVOKEVIRTUAL, owner, name, descriptor, isInterface);
+ } else {
+ continuation.apply(opcode, owner, name, descriptor, isInterface);
+ }
+ })
+ .transform();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
index b1bcb52..e290d51 100644
--- a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
@@ -228,7 +228,7 @@
Assert.assertNotNull(
appInfo.resolveMethod(toType(invokeReceiver, appInfo), method).getSingleTarget());
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
- if (resolutionResult.isValidVirtualTarget(appInfo.app().options)) {
+ if (resolutionResult.isVirtualTarget()) {
Set<DexEncodedMethod> targets = resolutionResult.lookupVirtualTargets(appInfo);
Set<DexType> targetHolders =
targets.stream().map(m -> m.method.holder).collect(Collectors.toSet());
diff --git a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java
index 63aa76f..c90b6e4 100644
--- a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java
@@ -122,7 +122,7 @@
appInfo.resolveMethodOnInterface(methodOnB.holder, methodOnB);
DexEncodedMethod resolved = resolutionResult.getSingleTarget();
assertEquals(methodOnB, resolved.method);
- assertFalse(resolutionResult.isValidVirtualTarget(appInfo.app().options));
+ assertFalse(resolutionResult.isVirtualTarget());
DexEncodedMethod singleVirtualTarget =
appInfo.lookupSingleInterfaceTarget(methodOnB, methodOnB.holder);
Assert.assertNull(singleVirtualTarget);
@@ -133,7 +133,7 @@
ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(methodOnB.holder, methodOnB);
DexEncodedMethod resolved = resolutionResult.getSingleTarget();
assertEquals(methodOnB, resolved.method);
- assertFalse(resolutionResult.isValidVirtualTarget(appInfo.app().options));
+ assertFalse(resolutionResult.isVirtualTarget());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java
index 22a12f2..f41a60f 100644
--- a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java
@@ -168,7 +168,7 @@
ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(methodOnB.holder, methodOnB);
DexEncodedMethod resolved = resolutionResult.getSingleTarget();
assertEquals(methodOnA, resolved.method);
- assertFalse(resolutionResult.isValidVirtualTarget(appInfo.app().options));
+ assertFalse(resolutionResult.isVirtualTarget());
DexEncodedMethod singleVirtualTarget =
appInfo.lookupSingleVirtualTarget(methodOnB, methodOnB.holder);
Assert.assertNull(singleVirtualTarget);
@@ -179,7 +179,7 @@
ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(methodOnB.holder, methodOnB);
DexEncodedMethod resolved = resolutionResult.getSingleTarget();
assertEquals(methodOnA, resolved.method);
- assertFalse(resolutionResult.isValidVirtualTarget(appInfo.app().options));
+ assertFalse(resolutionResult.isVirtualTarget());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessTest.java
index 69cdad5..f1c5fa1 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessTest.java
@@ -92,16 +92,7 @@
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
.run(parameters.getRuntime(), Main.class)
- .apply(
- result -> {
- if (parameters.isDexRuntime() && inSameNest) {
- // TODO(b/145187969): R8 incorrectly compiles the nest based access away.
- result.assertFailureWithErrorThatMatches(
- containsString(NullPointerException.class.getName()));
- } else {
- checkExpectedResult(result);
- }
- });
+ .apply(this::checkExpectedResult);
}
private void checkExpectedResult(TestRunResult<?> result) {
diff --git a/src/test/java/com/android/tools/r8/resolution/access/SelfVirtualMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/SelfVirtualMethodAccessTest.java
index a997622..f2f3309 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/SelfVirtualMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/SelfVirtualMethodAccessTest.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.resolution.access;
-import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestBase;
@@ -77,16 +76,7 @@
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
.run(parameters.getRuntime(), Main.class)
- .apply(
- result -> {
- if (parameters.isCfRuntime()) {
- result.assertSuccessWithOutput(EXPECTED);
- } else {
- // TODO(b/145187969): R8 compiles an incorrect program.
- result.assertFailureWithErrorThatMatches(
- containsString(NullPointerException.class.getName()));
- }
- });
+ .assertSuccessWithOutput(EXPECTED);
}
static class A {
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineWithoutNullCheck.java b/src/test/java/com/android/tools/r8/retrace/InlineWithoutNullCheckTest.java
similarity index 94%
rename from src/test/java/com/android/tools/r8/retrace/stacktraces/InlineWithoutNullCheck.java
rename to src/test/java/com/android/tools/r8/retrace/InlineWithoutNullCheckTest.java
index 6126c4b..c46d3ce 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineWithoutNullCheck.java
+++ b/src/test/java/com/android/tools/r8/retrace/InlineWithoutNullCheckTest.java
@@ -2,7 +2,7 @@
// 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.retrace.stacktraces;
+package com.android.tools.r8.retrace;
import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForFileNameAndLineNumber;
@@ -25,7 +25,7 @@
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
-public class InlineWithoutNullCheck extends TestBase {
+public class InlineWithoutNullCheckTest extends TestBase {
private final TestParameters parameters;
@@ -34,7 +34,7 @@
return getTestParameters().withAllRuntimes().withAllApiLevels().build();
}
- public InlineWithoutNullCheck(TestParameters parameters) {
+ public InlineWithoutNullCheckTest(TestParameters parameters) {
this.parameters = parameters;
}
@@ -47,21 +47,21 @@
// Get the expected stack traces by running on the runtime to test.
expectedStackTraceForInlineMethod =
testForRuntime(parameters.getRuntime(), parameters.getApiLevel())
- .addInnerClasses(InlineWithoutNullCheck.class)
+ .addInnerClasses(InlineWithoutNullCheckTest.class)
.run(parameters.getRuntime(), TestClassForInlineMethod.class)
.writeProcessResult(System.out)
.assertFailure()
.getStackTrace();
expectedStackTraceForInlineField =
testForRuntime(parameters.getRuntime(), parameters.getApiLevel())
- .addInnerClasses(InlineWithoutNullCheck.class)
+ .addInnerClasses(InlineWithoutNullCheckTest.class)
.run(parameters.getRuntime(), TestClassForInlineField.class)
.writeProcessResult(System.out)
.assertFailure()
.getStackTrace();
expectedStackTraceForInlineStaticField =
testForRuntime(parameters.getRuntime(), parameters.getApiLevel())
- .addInnerClasses(InlineWithoutNullCheck.class)
+ .addInnerClasses(InlineWithoutNullCheckTest.class)
.run(parameters.getRuntime(), TestClassForInlineStaticField.class)
.writeProcessResult(System.out)
.assertFailure()
@@ -104,7 +104,7 @@
@Test
public void testInlineMethodWhichChecksNullReceiverBeforeAnySideEffectMethod() throws Exception {
testForR8(parameters.getBackend())
- .addInnerClasses(InlineWithoutNullCheck.class)
+ .addInnerClasses(InlineWithoutNullCheckTest.class)
.addKeepMainRule(TestClassForInlineMethod.class)
.enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
@@ -132,7 +132,7 @@
@Test
public void testInlineMethodWhichChecksNullReceiverBeforeAnySideEffectField() throws Exception {
testForR8(parameters.getBackend())
- .addInnerClasses(InlineWithoutNullCheck.class)
+ .addInnerClasses(InlineWithoutNullCheckTest.class)
.addKeepMainRule(TestClassForInlineField.class)
.enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
@@ -161,7 +161,7 @@
public void testInlineMethodWhichChecksNullReceiverBeforeAnySideEffectStaticField()
throws Exception {
testForR8(parameters.getBackend())
- .addInnerClasses(InlineWithoutNullCheck.class)
+ .addInnerClasses(InlineWithoutNullCheckTest.class)
.addKeepMainRule(TestClassForInlineStaticField.class)
.enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
index 258d869..102f7f0 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
@@ -4,13 +4,16 @@
package com.android.tools.r8.retrace;
-import static com.android.tools.r8.ToolHelper.LINE_SEPARATOR;
+import static com.android.tools.r8.retrace.RetraceTests.DEFAULT_REGULAR_EXPRESSION;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.StringContains.containsString;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.retrace.stacktraces.ActualRetraceBotStackTrace;
+import com.android.tools.r8.retrace.stacktraces.ActualRetraceBotStackTraceWithInfo;
+import com.android.tools.r8.retrace.stacktraces.FoundMethodVerboseStackTrace;
import com.android.tools.r8.utils.StringUtils;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -22,6 +25,7 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.List;
import org.hamcrest.Matcher;
import org.junit.Rule;
@@ -30,7 +34,7 @@
public class RetraceCommandLineTests {
- private static final boolean testExternal = true;
+ private static final boolean testExternal = false;
@Rule public TemporaryFolder folder = new TemporaryFolder();
@@ -56,7 +60,36 @@
@Test
public void testVerbose() throws IOException {
- runAbortTest(containsString("Currently no support for --verbose"), "--verbose");
+ FoundMethodVerboseStackTrace stackTrace = new FoundMethodVerboseStackTrace();
+ runTest(
+ stackTrace.mapping(),
+ StringUtils.joinLines(stackTrace.obfuscatedStackTrace()),
+ false,
+ StringUtils.joinLines(stackTrace.retracedStackTrace()) + StringUtils.LINE_SEPARATOR,
+ "--verbose");
+ }
+
+ @Test
+ public void testRegularExpression() throws IOException {
+ ActualRetraceBotStackTrace stackTrace = new ActualRetraceBotStackTrace();
+ runTest(
+ stackTrace.mapping(),
+ StringUtils.joinLines(stackTrace.obfuscatedStackTrace()),
+ false,
+ StringUtils.joinLines(stackTrace.retracedStackTrace()) + StringUtils.LINE_SEPARATOR,
+ "--regex=" + DEFAULT_REGULAR_EXPRESSION);
+ }
+
+ @Test
+ public void testRegularExpressionWithInfo() throws IOException {
+ ActualRetraceBotStackTraceWithInfo stackTrace = new ActualRetraceBotStackTraceWithInfo();
+ runTest(
+ stackTrace.mapping(),
+ StringUtils.joinLines(stackTrace.obfuscatedStackTrace()),
+ false,
+ StringUtils.joinLines(stackTrace.retracedStackTrace()) + StringUtils.LINE_SEPARATOR,
+ "--regex=" + DEFAULT_REGULAR_EXPRESSION,
+ "--info");
}
@Test
@@ -66,7 +99,7 @@
@Test
public void testHelp() throws IOException {
- ProcessResult processResult = runRetraceCommandLine(null, "--help");
+ ProcessResult processResult = runRetraceCommandLine(null, Arrays.asList("--help"));
assertEquals(0, processResult.exitCode);
assertEquals(Retrace.USAGE_MESSAGE, processResult.stdout);
}
@@ -82,34 +115,39 @@
" at r8.retrace(App:184)",
" ... 7 more");
- private void runTest(String mapping, String stackTrace, boolean stacktraceStdIn, String expected)
+ private void runTest(
+ String mapping, String stackTrace, boolean stacktraceStdIn, String expected, String... args)
throws IOException {
- ProcessResult result = runRetrace(mapping, stackTrace, stacktraceStdIn);
+ ProcessResult result = runRetrace(mapping, stackTrace, stacktraceStdIn, args);
assertEquals(0, result.exitCode);
assertEquals(expected, result.stdout);
}
private void runAbortTest(Matcher<String> errorMatch, String... args) throws IOException {
- ProcessResult result = runRetraceCommandLine(null, args);
+ ProcessResult result = runRetraceCommandLine(null, Arrays.asList(args));
assertEquals(1, result.exitCode);
assertThat(result.stderr, errorMatch);
}
- private ProcessResult runRetrace(String mapping, String stackTrace, boolean stacktraceStdIn)
+ private ProcessResult runRetrace(
+ String mapping, String stackTrace, boolean stacktraceStdIn, String... additionalArgs)
throws IOException {
Path mappingFile = folder.newFile("mapping.txt").toPath();
Files.write(mappingFile, mapping.getBytes());
File stackTraceFile = folder.newFile("stacktrace.txt");
Files.write(stackTraceFile.toPath(), stackTrace.getBytes());
- if (stacktraceStdIn) {
- return runRetraceCommandLine(stackTraceFile, mappingFile.toString());
- } else {
- return runRetraceCommandLine(
- null, mappingFile.toString(), stackTraceFile.toPath().toString());
+
+ Collection<String> args = new ArrayList<>();
+ args.add(mappingFile.toString());
+ if (!stacktraceStdIn) {
+ args.add(stackTraceFile.toPath().toString());
}
+ args.addAll(Arrays.asList(additionalArgs));
+ return runRetraceCommandLine(stacktraceStdIn ? stackTraceFile : null, args);
}
- private ProcessResult runRetraceCommandLine(File stdInput, String... args) throws IOException {
+ private ProcessResult runRetraceCommandLine(File stdInput, Collection<String> args)
+ throws IOException {
if (testExternal) {
List<String> command = new ArrayList<>();
command.add(ToolHelper.getSystemJavaExecutable());
@@ -117,7 +155,7 @@
command.add("-cp");
command.add(ToolHelper.R8_JAR.toString());
command.add("com.android.tools.r8.retrace.Retrace");
- command.addAll(Arrays.asList(args));
+ command.addAll(args);
ProcessBuilder builder = new ProcessBuilder(command);
if (stdInput != null) {
builder.redirectInput(stdInput);
@@ -136,7 +174,9 @@
System.setErr(new PrintStream(errorByteStream));
int exitCode = 0;
try {
- Retrace.run(args);
+ String[] strArgs = new String[0];
+ strArgs = args.toArray(strArgs);
+ Retrace.run(strArgs);
} catch (Throwable t) {
exitCode = 1;
}
@@ -149,7 +189,7 @@
exitCode,
outputByteStream.toString(),
errorByteStream.toString(),
- StringUtils.join(LINE_SEPARATOR, args));
+ StringUtils.joinLines(args));
}
}
}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
index 59a67ad..e29c928 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -43,7 +43,7 @@
// This is a slight modification of the default regular expression shown for proguard retrace
// that allow for retracing classes in the form <class>: lorem ipsum...
// Seems like Proguard retrace is expecting the form "Caused by: <class>".
- private static final String DEFAULT_REGULAR_EXPRESSION =
+ public static final String DEFAULT_REGULAR_EXPRESSION =
"(?:.*?\\bat\\s+%c\\.%m\\s*\\(%s(?::%l)?\\)\\s*(?:~\\[.*\\])?)|(?:(?:(?:%c|.*)?[:\"]\\s+)?%c(?::.*)?)";
@Parameters(name = "{0}, use regular expression: {1}")
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceVerboseTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceVerboseTests.java
new file mode 100644
index 0000000..277d190
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceVerboseTests.java
@@ -0,0 +1,54 @@
+// 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.retrace;
+
+import static junit.framework.TestCase.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.retrace.stacktraces.FoundMethodVerboseStackTrace;
+import com.android.tools.r8.retrace.stacktraces.StackTraceForTest;
+import com.android.tools.r8.retrace.stacktraces.UnknownMethodVerboseStackTrace;
+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 RetraceVerboseTests extends TestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public RetraceVerboseTests(TestParameters parameters) {}
+
+ @Test
+ public void testFoundMethod() {
+ runRetraceTest(new FoundMethodVerboseStackTrace());
+ }
+
+ @Test
+ public void testUnknownMethod() {
+ runRetraceTest(new UnknownMethodVerboseStackTrace());
+ }
+
+ private TestDiagnosticMessagesImpl runRetraceTest(StackTraceForTest stackTraceForTest) {
+ TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
+ RetraceCommand retraceCommand =
+ RetraceCommand.builder(diagnosticsHandler)
+ .setProguardMapProducer(stackTraceForTest::mapping)
+ .setStackTrace(stackTraceForTest.obfuscatedStackTrace())
+ .setVerbose(true)
+ .setRetracedStackTraceConsumer(
+ retraced -> assertEquals(stackTraceForTest.retracedStackTrace(), retraced))
+ .build();
+ Retrace.run(retraceCommand);
+ return diagnosticsHandler;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/ActualRetraceBotStackTraceWithInfo.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/ActualRetraceBotStackTraceWithInfo.java
new file mode 100644
index 0000000..53aa7c6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/ActualRetraceBotStackTraceWithInfo.java
@@ -0,0 +1,128 @@
+// 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.retrace.stacktraces;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class ActualRetraceBotStackTraceWithInfo extends ActualBotStackTraceBase {
+
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return Arrays.asList(
+ "com.android.tools.r8.CompilationFailedException: Compilation failed to complete",
+ "\tat com.android.tools.r8.BaseCommand$Builder.build(:6)",
+ "\tat com.android.tools.r8.R8TestBuilder.internalCompile(R8TestBuilder.java:104)",
+ "\tat com.android.tools.r8.R8TestBuilder.internalCompile(R8TestBuilder.java:29)",
+ "\tat com.android.tools.r8.TestCompilerBuilder.compile(TestCompilerBuilder.java:89)",
+ "\tat com.android.tools.r8.TestCompilerBuilder.run(TestCompilerBuilder.java:113)",
+ "\tat com.android.tools.r8.TestBuilder.run(TestBuilder.java:49)",
+ "\tat com.android.tools.r8.ir.optimize.classinliner.ClassInlinerTest.testCodeSample(ClassInlinerTest.java:289)",
+ "",
+ "Caused by:",
+ "com.android.tools.r8.utils.b: Error: offset: 158, line: 2, column: 33, Unexpected"
+ + " attribute at <no file>:2:33",
+ "-keepattributes -keepattributes LineNumberTable",
+ " ^",
+ "\tat com.android.tools.r8.utils.t0.a(:21)",
+ "\tat com.android.tools.r8.shaking.ProguardConfigurationParser.parse(:19)",
+ "\tat com.android.tools.r8.R8Command$Builder.i(:16)",
+ "\tat com.android.tools.r8.R8Command$Builder.b(:11)",
+ "\tat com.android.tools.r8.R8Command$Builder.b(:1)",
+ "\tat com.android.tools.r8.BaseCommand$Builder.build(:2)",
+ "\t... 6 more");
+ }
+
+ @Override
+ public String mapping() {
+ return r8MappingFromGitSha("dab96bbe5948133f0ae6e0a88fc133464421cf47");
+ }
+
+ // The Pruning lines is debug info printed by the command line interface if one passes --info and
+ // --regex=<DEFAULT_REGULAR_EXPRESSION> as options.
+ @Override
+ public List<String> retracedStackTrace() {
+ return Arrays.asList(
+ "Pruning \tat com.android.tools.r8.utils.Reporter.error(Reporter.java:21) from result"
+ + " because method is not defined on line number 21",
+ "Pruning \tat com.android.tools.r8.utils.Reporter.error(Reporter.java:21) from result"
+ + " because method is not defined on line number 21",
+ "Pruning \tat com.android.tools.r8.utils.Reporter.fatalError(Reporter.java:21) from result"
+ + " because method is not defined on line number 21",
+ "Pruning \tat"
+ + " com.android.tools.r8.utils.Reporter.addSuppressedExceptions(Reporter.java:21) from"
+ + " result because method is not defined on line number 21",
+ "Pruning \tat"
+ + " com.android.tools.r8.shaking.ProguardConfigurationParser.parse(ProguardConfigurationParser.java:19)"
+ + " from result because method is not defined on line number 19",
+ "Pruning \tat"
+ + " com.android.tools.r8.shaking.ProguardConfigurationParser.parse(ProguardConfigurationParser.java:19)"
+ + " from result because method is not defined on line number 19",
+ "Pruning \tat"
+ + " com.android.tools.r8.shaking.ProguardConfigurationParser.parse(ProguardConfigurationParser.java:19)"
+ + " from result because method is not in range on line number 19",
+ "Pruning \tat"
+ + " com.android.tools.r8.shaking.ProguardConfigurationParser.parse(ProguardConfigurationParser.java:19)"
+ + " from result because method is not in range on line number 19",
+ "Pruning \tat com.android.tools.r8.R8Command$Builder.makeCommand(R8Command.java:11) from"
+ + " result because method is not defined on line number 11",
+ "Pruning \tat"
+ + " com.android.tools.r8.R8Command$Builder.setDisableVerticalClassMerging(R8Command.java:11)"
+ + " from result because method is not defined on line number 11",
+ "Pruning \tat"
+ + " com.android.tools.r8.R8Command$Builder.lambda$addProguardConfigurationFiles$4(R8Command.java:11)"
+ + " from result because method is not defined on line number 11",
+ "Pruning \tat"
+ + " com.android.tools.r8.R8Command$Builder.lambda$addProguardConfiguration$6(R8Command.java:11)"
+ + " from result because method is not defined on line number 11",
+ "Pruning \tat"
+ + " com.android.tools.r8.R8Command$Builder.lambda$addProguardConfiguration$6(R8Command.java:11)"
+ + " from result because method is not defined on line number 11",
+ "Pruning \tat com.android.tools.r8.R8Command$Builder.makeCommand(R8Command.java:11) from"
+ + " result because method is not in range on line number 11",
+ "Pruning \tat"
+ + " com.android.tools.r8.R8Command$Builder.setDisableVerticalClassMerging(R8Command.java:1)"
+ + " from result because method is not defined on line number 1",
+ "Pruning \tat"
+ + " com.android.tools.r8.R8Command$Builder.lambda$addProguardConfigurationFiles$4(R8Command.java:1)"
+ + " from result because method is not defined on line number 1",
+ "Pruning \tat"
+ + " com.android.tools.r8.R8Command$Builder.lambda$addProguardConfiguration$6(R8Command.java:1)"
+ + " from result because method is not defined on line number 1",
+ "Pruning \tat"
+ + " com.android.tools.r8.R8Command$Builder.lambda$addProguardConfiguration$6(R8Command.java:1)"
+ + " from result because method is not defined on line number 1",
+ "Pruning \tat com.android.tools.r8.R8Command$Builder.makeCommand(R8Command.java:1) from"
+ + " result because method is not defined on line number 1",
+ "Pruning \tat com.android.tools.r8.R8Command$Builder.makeCommand(R8Command.java:1) from"
+ + " result because method is not defined on line number 1",
+ "com.android.tools.r8.CompilationFailedException: Compilation failed to complete",
+ "\tat com.android.tools.r8.BaseCommand$Builder.build(BaseCommand.java:143)",
+ "\tat com.android.tools.r8.R8TestBuilder.internalCompile(R8TestBuilder.java:104)",
+ "\tat com.android.tools.r8.R8TestBuilder.internalCompile(R8TestBuilder.java:29)",
+ "\tat com.android.tools.r8.TestCompilerBuilder.compile(TestCompilerBuilder.java:89)",
+ "\tat com.android.tools.r8.TestCompilerBuilder.run(TestCompilerBuilder.java:113)",
+ "\tat com.android.tools.r8.TestBuilder.run(TestBuilder.java:49)",
+ "\tat com.android.tools.r8.ir.optimize.classinliner.ClassInlinerTest.testCodeSample(ClassInlinerTest.java:289)",
+ "",
+ "Caused by:",
+ "com.android.tools.r8.utils.AbortException: Error: offset: 158, line: 2, column: 33,"
+ + " Unexpected attribute at <no file>:2:33",
+ "-keepattributes -keepattributes LineNumberTable",
+ " ^",
+ "\tat com.android.tools.r8.utils.Reporter.failIfPendingErrors(Reporter.java:101)",
+ "\tat com.android.tools.r8.shaking.ProguardConfigurationParser.parse(ProguardConfigurationParser.java:187)",
+ "\tat com.android.tools.r8.R8Command$Builder.makeR8Command(R8Command.java:432)",
+ "\tat com.android.tools.r8.R8Command$Builder.makeCommand(R8Command.java:413)",
+ "\tat com.android.tools.r8.R8Command$Builder.makeCommand(R8Command.java:61)",
+ "\tat com.android.tools.r8.BaseCommand$Builder.build(BaseCommand.java:139)",
+ "\t... 6 more");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 1;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/FoundMethodVerboseStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/FoundMethodVerboseStackTrace.java
new file mode 100644
index 0000000..4d9418c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/FoundMethodVerboseStackTrace.java
@@ -0,0 +1,38 @@
+// 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.retrace.stacktraces;
+
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
+import java.util.List;
+
+public class FoundMethodVerboseStackTrace implements StackTraceForTest {
+
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return Arrays.asList(
+ "Exception in thread \"main\" java.lang.NullPointerException", "\tat a.a.a(Foo.java:7)");
+ }
+
+ @Override
+ public String mapping() {
+ return StringUtils.lines(
+ "com.android.tools.r8.naming.retrace.Main -> a.a:",
+ " 7:7:com.android.Foo main(java.lang.String[],com.android.Bar):102 -> a");
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return Arrays.asList(
+ "Exception in thread \"main\" java.lang.NullPointerException",
+ "\tat com.android.Foo com.android.tools.r8.naming.retrace.Main.main(java.lang.String[],"
+ + "com.android.Bar)(Main.java:102)");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 0;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/UnknownMethodVerboseStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/UnknownMethodVerboseStackTrace.java
new file mode 100644
index 0000000..3eb9710
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/UnknownMethodVerboseStackTrace.java
@@ -0,0 +1,48 @@
+// 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.retrace.stacktraces;
+
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
+import java.util.List;
+
+public class UnknownMethodVerboseStackTrace implements StackTraceForTest {
+
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return Arrays.asList(
+ "Exception in thread \"main\" java.lang.NullPointerException",
+ "\tat a.a.c(Foo.java)",
+ "\tat a.a.b(Bar.java)",
+ "\tat a.a.a(Baz.java)");
+ }
+
+ @Override
+ public String mapping() {
+ return StringUtils.lines(
+ "com.android.tools.r8.naming.retrace.Main -> a.a:",
+ " com.android.Foo main(java.lang.String[],com.android.Bar) -> a",
+ " com.android.Foo main(java.lang.String[]) -> b",
+ " com.android.Bar main(com.android.Bar) -> b");
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return Arrays.asList(
+ "Exception in thread \"main\" java.lang.NullPointerException",
+ "\tat com.android.tools.r8.naming.retrace.Main.c(Main.java)",
+ "\tat com.android.Foo com.android.tools.r8.naming.retrace.Main.main("
+ + "java.lang.String[])(Main.java)",
+ "\t<OR> at com.android.Bar com.android.tools.r8.naming.retrace.Main.main("
+ + "com.android.Bar)(Main.java)",
+ "\tat com.android.Foo com.android.tools.r8.naming.retrace.Main.main("
+ + "java.lang.String[],com.android.Bar)(Main.java)");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 0;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionsConfigurationTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionsConfigurationTest.java
index ce9f1c5..2d79d48 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionsConfigurationTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionsConfigurationTest.java
@@ -10,9 +10,12 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.AssertionsConfiguration;
+import com.android.tools.r8.D8TestBuilder;
+import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.rewrite.assertions.testclasses.TestClassForInnerClass;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ThrowingConsumer;
@@ -21,7 +24,6 @@
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.google.common.collect.ImmutableList;
import java.util.List;
-import java.util.function.Function;
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -65,15 +67,14 @@
}
private void runD8Test(
- Function<AssertionsConfiguration.Builder, AssertionsConfiguration>
- assertionsConfigurationBuilder,
+ ThrowableConsumer<D8TestBuilder> builderConsumer,
ThrowingConsumer<CodeInspector, RuntimeException> inspector,
List<String> outputLines)
throws Exception {
testForD8()
.addProgramClasses(testClasses)
.setMinApi(parameters.getApiLevel())
- .addAssertionsConfiguration(assertionsConfigurationBuilder)
+ .apply(builderConsumer)
.compile()
.inspect(inspector)
.run(parameters.getRuntime(), TestClass.class)
@@ -81,17 +82,15 @@
}
public void runR8Test(
- Function<AssertionsConfiguration.Builder, AssertionsConfiguration>
- assertionsConfigurationBuilder,
+ ThrowableConsumer<R8FullTestBuilder> builderConsumer,
ThrowingConsumer<CodeInspector, RuntimeException> inspector,
List<String> outputLines)
throws Exception {
- runR8Test(assertionsConfigurationBuilder, inspector, outputLines, false);
+ runR8Test(builderConsumer, inspector, outputLines, false);
}
public void runR8Test(
- Function<AssertionsConfiguration.Builder, AssertionsConfiguration>
- assertionsConfigurationBuilder,
+ ThrowableConsumer<R8FullTestBuilder> builderConsumer,
ThrowingConsumer<CodeInspector, RuntimeException> inspector,
List<String> outputLines,
boolean enableJvmAssertions)
@@ -102,7 +101,7 @@
.addKeepMainRule(TestClass.class)
.addKeepClassAndMembersRules(class1, class2, subpackageClass1, subpackageClass2)
.setMinApi(parameters.getApiLevel())
- .addAssertionsConfiguration(assertionsConfigurationBuilder)
+ .apply(builderConsumer)
.compile()
.inspect(inspector)
.enableRuntimeAssertions(enableJvmAssertions)
@@ -110,18 +109,6 @@
.assertSuccessWithOutput(StringUtils.lines(outputLines));
}
- private AssertionsConfiguration enableAllAssertions(AssertionsConfiguration.Builder builder) {
- return builder.enable().build();
- }
-
- private AssertionsConfiguration disableAllAssertions(AssertionsConfiguration.Builder builder) {
- return builder.disable().build();
- }
-
- private AssertionsConfiguration leaveAllAssertions(AssertionsConfiguration.Builder builder) {
- return builder.passthrough().build();
- }
-
private List<String> allAssertionsExpectedLines() {
return ImmutableList.of(
"AssertionError in testclasses.Class1",
@@ -218,29 +205,53 @@
Assume.assumeTrue(parameters.isDexRuntime());
// Leaving assertions in or disabling them on Dalvik/Art means no assertions.
runD8Test(
- this::leaveAllAssertions, this::checkAssertionCodeLeft, noAllAssertionsExpectedLines());
+ builder ->
+ builder.addAssertionsConfiguration(
+ AssertionsConfiguration.Builder::passthroughAllAssertions),
+ this::checkAssertionCodeLeft,
+ noAllAssertionsExpectedLines());
runR8Test(
- this::leaveAllAssertions, this::checkAssertionCodeLeft, noAllAssertionsExpectedLines());
+ builder ->
+ builder.addAssertionsConfiguration(
+ AssertionsConfiguration.Builder::passthroughAllAssertions),
+ this::checkAssertionCodeLeft,
+ noAllAssertionsExpectedLines());
runD8Test(
- this::disableAllAssertions,
+ builder ->
+ builder.addAssertionsConfiguration(
+ AssertionsConfiguration.Builder::disableAllAssertions),
this::checkAssertionCodeRemoved,
noAllAssertionsExpectedLines());
runR8Test(
- this::disableAllAssertions,
+ builder ->
+ builder.addAssertionsConfiguration(
+ AssertionsConfiguration.Builder::disableAllAssertions),
this::checkAssertionCodeRemoved,
noAllAssertionsExpectedLines());
// Compile time enabling assertions gives assertions on Dalvik/Art.
runD8Test(
- this::enableAllAssertions, this::checkAssertionCodeEnabled, allAssertionsExpectedLines());
- runR8Test(
- this::enableAllAssertions, this::checkAssertionCodeEnabled, allAssertionsExpectedLines());
- // Enabling for the package should enable all.
- runD8Test(
- builder -> builder.enableForPackage(packageName).build(),
+ builder ->
+ builder.addAssertionsConfiguration(
+ AssertionsConfiguration.Builder::enableAllAssertions),
this::checkAssertionCodeEnabled,
allAssertionsExpectedLines());
runR8Test(
- builder -> builder.enableForPackage(packageName).build(),
+ builder ->
+ builder.addAssertionsConfiguration(
+ AssertionsConfiguration.Builder::enableAllAssertions),
+ this::checkAssertionCodeEnabled,
+ allAssertionsExpectedLines());
+ // Enabling for the package should enable all.
+ runD8Test(
+ builder ->
+ builder.addAssertionsConfiguration(
+ b -> b.setEnable().setScopePackage(packageName).build()),
+ this::checkAssertionCodeEnabled,
+ allAssertionsExpectedLines());
+ runR8Test(
+ builder ->
+ builder.addAssertionsConfiguration(
+ b -> b.setEnable().setScopePackage(packageName).build()),
this::checkAssertionCodeEnabled,
allAssertionsExpectedLines());
}
@@ -250,23 +261,42 @@
Assume.assumeTrue(parameters.isCfRuntime());
// Leaving assertion code means assertions are controlled by the -ea flag.
runR8Test(
- this::leaveAllAssertions, this::checkAssertionCodeLeft, noAllAssertionsExpectedLines());
+ builder ->
+ builder.addAssertionsConfiguration(
+ AssertionsConfiguration.Builder::passthroughAllAssertions),
+ this::checkAssertionCodeLeft,
+ noAllAssertionsExpectedLines());
runR8Test(
- this::leaveAllAssertions, this::checkAssertionCodeLeft, allAssertionsExpectedLines(), true);
+ builder ->
+ builder.addAssertionsConfiguration(
+ AssertionsConfiguration.Builder::passthroughAllAssertions),
+ this::checkAssertionCodeLeft,
+ allAssertionsExpectedLines(),
+ true);
// Compile time enabling or disabling assertions means the -ea flag has no effect.
runR8Test(
- this::enableAllAssertions, this::checkAssertionCodeEnabled, allAssertionsExpectedLines());
+ builder ->
+ builder.addAssertionsConfiguration(
+ AssertionsConfiguration.Builder::enableAllAssertions),
+ this::checkAssertionCodeEnabled,
+ allAssertionsExpectedLines());
runR8Test(
- this::enableAllAssertions,
+ builder ->
+ builder.addAssertionsConfiguration(
+ AssertionsConfiguration.Builder::enableAllAssertions),
this::checkAssertionCodeEnabled,
allAssertionsExpectedLines(),
true);
runR8Test(
- this::disableAllAssertions,
+ builder ->
+ builder.addAssertionsConfiguration(
+ AssertionsConfiguration.Builder::disableAllAssertions),
this::checkAssertionCodeRemoved,
noAllAssertionsExpectedLines());
runR8Test(
- this::disableAllAssertions,
+ builder ->
+ builder.addAssertionsConfiguration(
+ AssertionsConfiguration.Builder::disableAllAssertions),
this::checkAssertionCodeRemoved,
noAllAssertionsExpectedLines(),
true);
@@ -276,7 +306,9 @@
public void testEnableForPackageForDex() throws Exception {
Assume.assumeTrue(parameters.isDexRuntime());
runD8Test(
- builder -> builder.enableForPackage(subPackageName).build(),
+ builder ->
+ builder.addAssertionsConfiguration(
+ b -> b.setEnable().setScopePackage(subPackageName).build()),
inspector -> {
checkAssertionCodeEnabled(inspector, subpackageClass1);
checkAssertionCodeEnabled(inspector, subpackageClass2);
@@ -286,7 +318,9 @@
"AssertionError in testclasses.subpackage.Class2",
"DONE"));
runR8Test(
- builder -> builder.enableForPackage(subPackageName).build(),
+ builder ->
+ builder.addAssertionsConfiguration(
+ b -> b.setEnable().setScopePackage(subPackageName).build()),
inspector -> {
checkAssertionCodeEnabled(inspector, subpackageClass1);
checkAssertionCodeEnabled(inspector, subpackageClass2);
@@ -303,12 +337,13 @@
runD8Test(
builder ->
builder
- .enableForClass(class1.getCanonicalName())
- .enableForClass(subpackageClass2.getCanonicalName())
- .build(),
+ .addAssertionsConfiguration(
+ b -> b.setEnable().setScopeClass(class1.getCanonicalName()).build())
+ .addAssertionsConfiguration(
+ b -> b.setEnable().setScopeClass(subpackageClass2.getCanonicalName()).build()),
inspector -> {
- // checkAssertionCodeEnabled(inspector, class1);
- // checkAssertionCodeEnabled(inspector, subpackageClass2);
+ checkAssertionCodeEnabled(inspector, class1);
+ checkAssertionCodeEnabled(inspector, subpackageClass2);
},
ImmutableList.of(
"AssertionError in testclasses.Class1",
@@ -317,9 +352,10 @@
runR8Test(
builder ->
builder
- .enableForClass(class1.getCanonicalName())
- .enableForClass(subpackageClass2.getCanonicalName())
- .build(),
+ .addAssertionsConfiguration(
+ b -> b.setEnable().setScopeClass(class1.getCanonicalName()).build())
+ .addAssertionsConfiguration(
+ b -> b.setEnable().setScopeClass(subpackageClass2.getCanonicalName()).build()),
inspector -> {
checkAssertionCodeEnabled(inspector, class1);
checkAssertionCodeEnabled(inspector, subpackageClass2);
@@ -336,10 +372,11 @@
runD8Test(
builder ->
builder
- .enableForPackage(packageName)
- .disableForClass(class2.getCanonicalName())
- .disableForClass(subpackageClass1.getCanonicalName())
- .build(),
+ .addAssertionsConfiguration(b -> b.setEnable().setScopePackage(packageName).build())
+ .addAssertionsConfiguration(
+ b -> b.setDisable().setScopeClass(class2.getCanonicalName()).build())
+ .addAssertionsConfiguration(
+ b -> b.setDisable().setScopeClass(subpackageClass1.getCanonicalName()).build()),
inspector -> {
checkAssertionCodeEnabled(inspector, class1);
checkAssertionCodeRemoved(inspector, class2);
@@ -353,10 +390,11 @@
runR8Test(
builder ->
builder
- .enableForPackage(packageName)
- .disableForClass(class2.getCanonicalName())
- .disableForClass(subpackageClass1.getCanonicalName())
- .build(),
+ .addAssertionsConfiguration(b -> b.setEnable().setScopePackage(packageName).build())
+ .addAssertionsConfiguration(
+ b -> b.setDisable().setScopeClass(class2.getCanonicalName()).build())
+ .addAssertionsConfiguration(
+ b -> b.setDisable().setScopeClass(subpackageClass1.getCanonicalName()).build()),
inspector -> {
checkAssertionCodeEnabled(inspector, class1);
checkAssertionCodeRemoved(inspector, class2);
@@ -379,7 +417,7 @@
classInUnnamedPackage("Class1"),
classInUnnamedPackage("Class2"))
.setMinApi(parameters.getApiLevel())
- .addAssertionsConfiguration(builder -> builder.enableForPackage("").build())
+ .addAssertionsConfiguration(builder -> builder.setEnable().setScopePackage("").build())
.compile()
.inspect(
inspector -> {
@@ -427,7 +465,10 @@
.setMinApi(parameters.getApiLevel())
.addAssertionsConfiguration(
builder ->
- builder.enableForClass(TestClassForInnerClass.class.getCanonicalName()).build())
+ builder
+ .setEnable()
+ .setScopeClass(TestClassForInnerClass.class.getCanonicalName())
+ .build())
.compile()
.inspect(
inspector -> {
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java
index 27e53ba..bbcc8e2 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java
@@ -199,7 +199,8 @@
.debug()
.noTreeShaking()
.noMinification()
- .addAssertionsConfiguration(builder -> builder.setTransformation(transformation).build())
+ .addAssertionsConfiguration(
+ builder -> builder.setTransformation(transformation).setScopeAll().build())
.compile();
}
@@ -337,7 +338,8 @@
.addProgramClasses(ClassWithAssertions.class)
.debug()
.setMinApi(AndroidApiLevel.B)
- .addAssertionsConfiguration(builder -> builder.setTransformation(transformation).build())
+ .addAssertionsConfiguration(
+ builder -> builder.setTransformation(transformation).setScopeAll().build())
.compile();
}
@@ -357,7 +359,8 @@
.addProgramFiles(program)
.debug()
.setMinApi(AndroidApiLevel.B)
- .addAssertionsConfiguration(builder -> builder.setTransformation(transformation).build())
+ .addAssertionsConfiguration(
+ builder -> builder.setTransformation(transformation).setScopeAll().build())
.compile();
}
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index 3d653d0..598f6f1 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -166,6 +166,31 @@
});
}
+ /** Unconditionally replace the descriptor (ie, qualified name) of a class. */
+ public ClassFileTransformer setClassDescriptor(String descriptor) {
+ assert DescriptorUtils.isClassDescriptor(descriptor);
+ return addClassTransformer(
+ new ClassTransformer() {
+ @Override
+ public void visit(
+ int version,
+ int access,
+ String ignoredName,
+ String signature,
+ String superName,
+ String[] interfaces) {
+ super.visit(
+ version,
+ access,
+ DescriptorUtils.getBinaryNameFromDescriptor(descriptor),
+ signature,
+ superName,
+ interfaces);
+ }
+ });
+ }
+
+
public ClassFileTransformer setMinVersion(CfVm jdk) {
return setMinVersion(jdk.getClassfileVersion());
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index 071fb4f..1051e28 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -134,7 +134,7 @@
@Override
public boolean isVirtual() {
- return dexMethod.isVirtualMethod();
+ return dexMethod.isNonPrivateVirtualMethod();
}
@Override
diff --git a/tests/d8_api_usage_sample.jar b/tests/d8_api_usage_sample.jar
index 30b00cb..e47a3ad 100644
--- a/tests/d8_api_usage_sample.jar
+++ b/tests/d8_api_usage_sample.jar
Binary files differ
diff --git a/tests/r8_api_usage_sample.jar b/tests/r8_api_usage_sample.jar
index 4c4801c..4970fe1 100644
--- a/tests/r8_api_usage_sample.jar
+++ b/tests/r8_api_usage_sample.jar
Binary files differ
diff --git a/tools/retrace.py b/tools/retrace.py
index 6f3593f..e8fdf89 100755
--- a/tools/retrace.py
+++ b/tools/retrace.py
@@ -71,8 +71,13 @@
return 1
retrace_args = [
- jdk.GetJavaExecutable(), '-jar', utils.RETRACE_JAR, r8lib_map_path
+ jdk.GetJavaExecutable(),
+ '-cp',
+ utils.R8LIB_JAR,
+ 'com.android.tools.r8.retrace.Retrace',
+ r8lib_map_path
]
+
if args.stacktrace:
retrace_args.append(args.stacktrace)
diff --git a/tools/test_self_retrace.py b/tools/test_self_retrace.py
deleted file mode 100755
index d7ab9aa..0000000
--- a/tools/test_self_retrace.py
+++ /dev/null
@@ -1,77 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-# for details. All rights reserved. Use of this source code is governed by a
-# BSD-style license that can be found in the LICENSE file.
-
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
-import gradle
-import jdk
-import os
-import subprocess
-import sys
-
-import utils
-
-EXCEPTION_LINE = 'Intentional exception for testing retrace.'
-EXPECTED_LINES = [
- 'com.android.tools.r8.utils.SelfRetraceTest.foo3(SelfRetraceTest.java:13)',
- 'com.android.tools.r8.utils.SelfRetraceTest.foo2(SelfRetraceTest.java:17)',
- 'com.android.tools.r8.utils.SelfRetraceTest.foo1(SelfRetraceTest.java:21)',
- 'com.android.tools.r8.utils.SelfRetraceTest.test(SelfRetraceTest.java:26)',
- 'com.android.tools.r8.R8.run(R8.java:',
-]
-
-def main():
- args = sys.argv[1:]
- if len(args) == 0:
- gradle.RunGradle(['r8lib'])
- r8lib = utils.R8LIB_JAR
- r8map = utils.R8LIB + '.map'
- elif len(args) == 2:
- r8lib = args[0]
- r8map = args[1]
- elif len(args) == 1 and args[0] == '--help':
- print('Usage: test_self_retrace.py [<path-to-r8lib-jar> <path-to-r8lib-map]')
- print('If the path is missing the script builds and uses ' + utils.R8LIB_JAR)
- return
- else:
- raise Exception("Only two argument allowed, see '--help'.")
-
- # Run 'r8 --help' which throws an exception.
- cmd = [
- jdk.GetJavaExecutable(),'-cp', r8lib, 'com.android.tools.r8.R8', '--help'
- ]
- os.environ["R8_THROW_EXCEPTION_FOR_TESTING_RETRACE"] = "1"
- utils.PrintCmd(cmd)
- p = subprocess.Popen(cmd, stderr=subprocess.PIPE)
- _, stacktrace = p.communicate()
- assert(p.returncode != 0)
- assert(EXCEPTION_LINE in stacktrace)
- # r8lib must be minified, original class names must not be present.
- assert('SelfRetraceTest' not in stacktrace)
-
- # Run the retrace tool.
- cmd = [jdk.GetJavaExecutable(), '-jar', utils.RETRACE_JAR, r8map]
- utils.PrintCmd(cmd)
- p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
- retrace_stdout, _ = p.communicate(stacktrace)
- assert p.returncode == 0
- retrace_lines = retrace_stdout.splitlines()
- line_index = -1
- for line in retrace_lines:
- if line_index < 0:
- if 'java.lang.RuntimeException' in line:
- assert(EXCEPTION_LINE in line)
- line_index = 0;
- else:
- assert EXPECTED_LINES[line_index] in line
- line_index += 1
- if line_index >= len(EXPECTED_LINES):
- break
- assert(line_index >= 0)
-
-if __name__ == '__main__':
- sys.exit(main())
diff --git a/tools/utils.py b/tools/utils.py
index 51c5a12..6690ba2 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -60,12 +60,6 @@
GENERATED_LICENSE = os.path.join(GENERATED_LICENSE_DIR, 'LICENSE')
RT_JAR = os.path.join(REPO_ROOT, 'third_party/openjdk/openjdk-rt-1.8/rt.jar')
R8LIB_KEEP_RULES = os.path.join(REPO_ROOT, 'src/main/keep.txt')
-RETRACE_JAR = os.path.join(
- THIRD_PARTY,
- 'proguard',
- 'proguard6.0.1',
- 'lib',
- 'retrace.jar')
PROGUARD_JAR = os.path.join(
THIRD_PARTY,
'proguard',