Merge commit '6a1de57911d923d0ed10404e4e00ee5df5e807ec' into dev-release
diff --git a/build.gradle b/build.gradle
index be64cba..33dfc26 100644
--- a/build.gradle
+++ b/build.gradle
@@ -293,6 +293,7 @@
def r8LibGeneratedKeepRulesPath = "$buildDir/generated/keep.txt"
def r8LibTestPath = "$buildDir/classes/r8libtest"
def java11ClassFiles = "$buildDir/classes/java/mainJava11"
+def r8RetracePath = "$buildDir/libs/r8retrace.jar"
def osString = OperatingSystem.current().isLinux() ? "linux" :
OperatingSystem.current().isMacOsX() ? "mac" : "windows"
@@ -1070,6 +1071,17 @@
outputs.file r8DesugaredPath
}
+task R8Retrace {
+ dependsOn R8Lib
+ dependsOn r8LibCreateTask(
+ "Retrace",
+ ["src/main/keep_retrace.txt"],
+ R8Lib,
+ r8RetracePath,
+ ).dependsOn(R8Lib)
+ outputs.file r8RetracePath
+}
+
task sourceJar(type: Jar, dependsOn: classes) {
classifier = 'src'
from sourceSets.main.allSource
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index cae27a3..4def5d0 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -40,6 +40,7 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.DesugarState;
import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
@@ -142,8 +143,7 @@
*/
public static void main(String[] args) {
if (args.length == 0) {
- System.err.println(USAGE_MESSAGE);
- System.exit(ExceptionUtils.STATUS_ERROR);
+ throw new RuntimeException(StringUtils.joinLines("Invalid invocation.", USAGE_MESSAGE));
}
ExceptionUtils.withMainProgramHandler(() -> run(args));
}
@@ -183,6 +183,12 @@
AppView<AppInfo> appView = readApp(inputApp, options, executor, timing);
SyntheticItems.collectSyntheticInputs(appView);
+ if (!options.mainDexKeepRules.isEmpty()) {
+ new GenerateMainDexList(options)
+ .traceMainDex(
+ executor, appView.appInfo().app(), appView.appInfo().getMainDexClasses()::addAll);
+ }
+
final CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
if (AssertionsRewriter.isEnabled(options)) {
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index f720ee9..047df78 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -13,6 +13,11 @@
import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.shaking.ProguardConfigurationParser;
+import com.android.tools.r8.shaking.ProguardConfigurationRule;
+import com.android.tools.r8.shaking.ProguardConfigurationSource;
+import com.android.tools.r8.shaking.ProguardConfigurationSourceFile;
+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;
@@ -21,7 +26,11 @@
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
+import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.BiPredicate;
@@ -75,6 +84,7 @@
private boolean enableMainDexListCheck = true;
private boolean minimalMainDex = false;
private boolean skipDump = false;
+ private final List<ProguardConfigurationSource> mainDexRules = new ArrayList<>();
private Builder() {
this(new DefaultD8DiagnosticsHandler());
@@ -203,6 +213,26 @@
return self();
}
+ /** Add proguard configuration files with rules for automatic main-dex-list calculation. */
+ public Builder addMainDexRulesFiles(Path... paths) {
+ return addMainDexRulesFiles(Arrays.asList(paths));
+ }
+
+ /** Add proguard configuration files with rules for automatic main-dex-list calculation. */
+ public Builder addMainDexRulesFiles(Collection<Path> paths) {
+ guard(() -> paths.forEach(p -> mainDexRules.add(new ProguardConfigurationSourceFile(p))));
+ return self();
+ }
+
+ /** Add proguard rules for automatic main-dex-list calculation. */
+ public Builder addMainDexRules(List<String> lines, Origin origin) {
+ guard(
+ () ->
+ mainDexRules.add(
+ new ProguardConfigurationSourceStrings(lines, Paths.get("."), origin)));
+ return self();
+ }
+
@Override
void validate() {
if (isPrintHelp()) {
@@ -219,8 +249,20 @@
if (getProgramConsumer() instanceof DexFilePerClassFileConsumer) {
reporter.error("Option --main-dex-list cannot be used with --file-per-class");
}
- } else if (getMainDexListConsumer() != null) {
- reporter.error("Option --main-dex-list-output require --main-dex-list");
+ }
+ if (!mainDexRules.isEmpty()) {
+ if (intermediate) {
+ reporter.error("Option --main-dex-rules cannot be used with --intermediate");
+ }
+ if (getProgramConsumer() instanceof DexFilePerClassFileConsumer) {
+ reporter.error("Option --main-dex-rules cannot be used with --file-per-class");
+ }
+ }
+ if (getMainDexListConsumer() != null
+ && mainDexRules.isEmpty()
+ && !getAppBuilder().hasMainDexList()) {
+ reporter.error(
+ "Option --main-dex-list-output requires --main-dex-rules and/or --main-dex-list");
}
if (getMinApiLevel() >= AndroidApiLevel.L.getLevel()) {
if (getMainDexListConsumer() != null || getAppBuilder().hasMainDexList()) {
@@ -248,6 +290,9 @@
DesugaredLibraryConfiguration libraryConfiguration =
getDesugaredLibraryConfiguration(factory, false);
+ ImmutableList<ProguardConfigurationRule> mainDexKeepRules =
+ ProguardConfigurationParser.parse(mainDexRules, factory, getReporter());
+
return new D8Command(
getAppBuilder().build(),
getMode(),
@@ -269,6 +314,7 @@
skipDump,
enableMainDexListCheck,
minimalMainDex,
+ mainDexKeepRules,
getThreadCount(),
factory);
}
@@ -284,6 +330,7 @@
private final boolean skipDump;
private final boolean enableMainDexListCheck;
private final boolean minimalMainDex;
+ private final ImmutableList<ProguardConfigurationRule> mainDexKeepRules;
private final DexItemFactory factory;
public static Builder builder() {
@@ -347,6 +394,7 @@
boolean skipDump,
boolean enableMainDexListCheck,
boolean minimalMainDex,
+ ImmutableList<ProguardConfigurationRule> mainDexKeepRules,
int threadCount,
DexItemFactory factory) {
super(
@@ -371,6 +419,7 @@
this.skipDump = skipDump;
this.enableMainDexListCheck = enableMainDexListCheck;
this.minimalMainDex = minimalMainDex;
+ this.mainDexKeepRules = mainDexKeepRules;
this.factory = factory;
}
@@ -384,6 +433,7 @@
skipDump = false;
enableMainDexListCheck = true;
minimalMainDex = false;
+ mainDexKeepRules = null;
factory = null;
}
@@ -403,6 +453,7 @@
internal.intermediate = intermediate;
internal.readCompileTimeAnnotations = intermediate;
internal.desugarGraphConsumer = desugarGraphConsumer;
+ internal.mainDexKeepRules = mainDexKeepRules;
// Assert and fixup defaults.
assert !internal.isShrinking();
diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java
index d705c8e..8f90ade 100644
--- a/src/main/java/com/android/tools/r8/D8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
@@ -31,6 +31,7 @@
"--classpath",
"--pg-map",
MIN_API_FLAG,
+ "--main-dex-rules",
"--main-dex-list",
"--main-dex-list-output",
"--desugared-lib",
@@ -103,9 +104,9 @@
D8Command command = parse(args, Origin.root()).build();
if (command.isPrintHelp()) {
System.out.println(USAGE_MESSAGE);
- System.exit(1);
+ } else {
+ D8.run(command);
}
- D8.run(command);
}
static final String USAGE_MESSAGE =
@@ -139,6 +140,8 @@
" --no-desugaring # Force disable desugaring.",
" --desugared-lib <file> # Specify desugared library configuration.",
" # <file> is a desugared library configuration (json).",
+ " --main-dex-rules <file> # Proguard keep rules for classes to place in the",
+ " # primary dex file.",
" --main-dex-list <file> # List of classes to place in the primary dex file.",
" --main-dex-list-output <file>",
" # Output resulting main dex list in <file>."),
@@ -252,6 +255,8 @@
} catch (IOException e) {
builder.error(new ExceptionDiagnostic(e, new PathOrigin(file)));
}
+ } else if (arg.equals("--main-dex-rules")) {
+ builder.addMainDexRulesFiles(Paths.get(nextArg));
} else if (arg.equals("--main-dex-list")) {
builder.addMainDexListFiles(Paths.get(nextArg));
} else if (arg.equals("--main-dex-list-output")) {
diff --git a/src/main/java/com/android/tools/r8/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
index da40e79..740984c 100644
--- a/src/main/java/com/android/tools/r8/GenerateLintFiles.java
+++ b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
@@ -46,6 +46,7 @@
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.Sets;
import java.io.File;
@@ -794,9 +795,10 @@
new GenerateLintFiles(args[1], args[2], args[3]).generateDesugaredLibraryApisDocumetation();
return;
}
- System.out.println(
- "Usage: GenerateLineFiles [--generate-api-docs] "
- + "<desugar configuration> <desugar implementation> <output directory>");
- System.exit(1);
+ throw new RuntimeException(
+ StringUtils.joinLines(
+ "Invalid invocation.",
+ "Usage: GenerateLineFiles [--generate-api-docs] "
+ + "<desugar configuration> <desugar implementation> <output directory>"));
}
}
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index 04479b1..d260bcb 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -10,9 +10,9 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppServices;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.shaking.Enqueuer;
import com.android.tools.r8.shaking.EnqueuerFactory;
@@ -33,6 +33,7 @@
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
+import java.util.function.Consumer;
import java.util.stream.Collectors;
@Keep
@@ -40,78 +41,91 @@
private final Timing timing = new Timing("maindex");
private final InternalOptions options;
- private GenerateMainDexList(InternalOptions options) {
+ private List<String> result = null;
+
+ public GenerateMainDexList(InternalOptions options) {
this.options = options;
}
private List<String> run(AndroidApp app, ExecutorService executor)
throws IOException {
try {
- DirectMappedDexApplication application =
- new ApplicationReader(app, options, timing).read(executor).toDirect();
- AppView<? extends AppInfoWithClassHierarchy> appView = AppView.createForR8(application);
- appView.setAppServices(AppServices.builder(appView).build());
+ DexApplication application = new ApplicationReader(app, options, timing).read(executor);
+ traceMainDex(
+ executor,
+ application,
+ mainDexTracingResult -> {
+ result =
+ mainDexTracingResult.getClasses().stream()
+ .map(c -> c.toSourceString().replace('.', '/') + ".class")
+ .sorted()
+ .collect(Collectors.toList());
- MainDexListBuilder.checkForAssumedLibraryTypes(appView.appInfo());
-
- SubtypingInfo subtypingInfo = new SubtypingInfo(appView);
-
- RootSet mainDexRootSet =
- new RootSetBuilder(appView, subtypingInfo, options.mainDexKeepRules).run(executor);
-
- GraphConsumer graphConsumer = options.mainDexKeptGraphConsumer;
- WhyAreYouKeepingConsumer whyAreYouKeepingConsumer = null;
- if (!mainDexRootSet.reasonAsked.isEmpty()) {
- whyAreYouKeepingConsumer = new WhyAreYouKeepingConsumer(graphConsumer);
- graphConsumer = whyAreYouKeepingConsumer;
- }
-
- Enqueuer enqueuer =
- EnqueuerFactory.createForMainDexTracing(appView, subtypingInfo, graphConsumer);
- Set<DexProgramClass> liveTypes = enqueuer.traceMainDex(mainDexRootSet, executor, timing);
- // LiveTypes is the result.
- MainDexTracingResult mainDexTracingResult = new MainDexListBuilder(liveTypes, appView).run();
-
- List<String> result =
- mainDexTracingResult.getClasses().stream()
- .map(c -> c.toSourceString().replace('.', '/') + ".class")
- .sorted()
- .collect(Collectors.toList());
-
- if (options.mainDexListConsumer != null) {
- options.mainDexListConsumer.accept(String.join("\n", result), options.reporter);
- options.mainDexListConsumer.finished(options.reporter);
- }
-
- R8.processWhyAreYouKeepingAndCheckDiscarded(
- mainDexRootSet,
- () -> {
- ArrayList<DexProgramClass> classes = new ArrayList<>();
- // TODO(b/131668850): This is not a deterministic order!
- mainDexTracingResult
- .getClasses()
- .forEach(
- type -> {
- DexClass clazz = appView.definitionFor(type);
- assert clazz.isProgramClass();
- classes.add(clazz.asProgramClass());
- });
- return classes;
- },
- whyAreYouKeepingConsumer,
- appView,
- enqueuer,
- true,
- options,
- timing,
- executor);
-
+ if (options.mainDexListConsumer != null) {
+ options.mainDexListConsumer.accept(String.join("\n", result), options.reporter);
+ options.mainDexListConsumer.finished(options.reporter);
+ }
+ });
return result;
} catch (ExecutionException e) {
throw unwrapExecutionException(e);
}
}
+ public void traceMainDex(
+ ExecutorService executor,
+ DexApplication application,
+ Consumer<MainDexTracingResult> resultConsumer)
+ throws ExecutionException {
+ AppView<? extends AppInfoWithClassHierarchy> appView =
+ AppView.createForR8(application.toDirect());
+ appView.setAppServices(AppServices.builder(appView).build());
+
+ MainDexListBuilder.checkForAssumedLibraryTypes(appView.appInfo());
+
+ SubtypingInfo subtypingInfo = new SubtypingInfo(appView);
+
+ RootSet mainDexRootSet =
+ new RootSetBuilder(appView, subtypingInfo, options.mainDexKeepRules).run(executor);
+
+ GraphConsumer graphConsumer = options.mainDexKeptGraphConsumer;
+ WhyAreYouKeepingConsumer whyAreYouKeepingConsumer = null;
+ if (!mainDexRootSet.reasonAsked.isEmpty()) {
+ whyAreYouKeepingConsumer = new WhyAreYouKeepingConsumer(graphConsumer);
+ graphConsumer = whyAreYouKeepingConsumer;
+ }
+
+ Enqueuer enqueuer =
+ EnqueuerFactory.createForMainDexTracing(appView, subtypingInfo, graphConsumer);
+ Set<DexProgramClass> liveTypes = enqueuer.traceMainDex(mainDexRootSet, executor, timing);
+ // LiveTypes is the result.
+ MainDexTracingResult mainDexTracingResult = new MainDexListBuilder(liveTypes, appView).run();
+ resultConsumer.accept(mainDexTracingResult);
+
+ R8.processWhyAreYouKeepingAndCheckDiscarded(
+ mainDexRootSet,
+ () -> {
+ ArrayList<DexProgramClass> classes = new ArrayList<>();
+ // TODO(b/131668850): This is not a deterministic order!
+ mainDexTracingResult
+ .getClasses()
+ .forEach(
+ type -> {
+ DexClass clazz = appView.definitionFor(type);
+ assert clazz.isProgramClass();
+ classes.add(clazz.asProgramClass());
+ });
+ return classes;
+ },
+ whyAreYouKeepingConsumer,
+ appView,
+ enqueuer,
+ true,
+ options,
+ timing,
+ executor);
+ }
+
/**
* Main API entry for computing the main-dex list.
*
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java b/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
index 07569a7..b08a738 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
@@ -108,15 +108,8 @@
return new GenerateMainDexListCommand(isPrintHelp(), isPrintVersion());
}
- List<ProguardConfigurationRule> mainDexKeepRules;
- if (this.mainDexRules.isEmpty()) {
- mainDexKeepRules = ImmutableList.of();
- } else {
- ProguardConfigurationParser parser =
- new ProguardConfigurationParser(factory, getReporter());
- parser.parse(mainDexRules);
- mainDexKeepRules = parser.getConfig().getRules();
- }
+ List<ProguardConfigurationRule> mainDexKeepRules =
+ ProguardConfigurationParser.parse(mainDexRules, factory, getReporter());
return new GenerateMainDexListCommand(
factory,
diff --git a/src/main/java/com/android/tools/r8/JarDiff.java b/src/main/java/com/android/tools/r8/JarDiff.java
index 817b461..a268496 100644
--- a/src/main/java/com/android/tools/r8/JarDiff.java
+++ b/src/main/java/com/android/tools/r8/JarDiff.java
@@ -5,15 +5,14 @@
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.graph.ClassKind;
-import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.JarApplicationReader;
import com.android.tools.r8.graph.JarClassFileReader;
import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.StreamUtils;
-import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
@@ -21,7 +20,6 @@
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
-import java.util.function.Consumer;
import java.util.stream.Collectors;
/**
@@ -136,26 +134,11 @@
inputJar.getProgramResource(descriptor).getByteStream());
}
- private DexProgramClass getDexProgramClass(Path path, byte[] bytes) throws IOException {
-
- class Collector implements Consumer<DexClass> {
-
- private DexClass dexClass;
-
- @Override
- public void accept(DexClass dexClass) {
- this.dexClass = dexClass;
- }
-
- public DexClass get() {
- assert dexClass != null;
- return dexClass;
- }
- }
-
- Collector collector = new Collector();
- JarClassFileReader reader = new JarClassFileReader(applicationReader, collector);
- reader.read(new PathOrigin(path), ClassKind.PROGRAM, bytes);
+ private DexProgramClass getDexProgramClass(Path path, byte[] bytes) {
+ Box<DexProgramClass> collector = new Box<>();
+ JarClassFileReader<DexProgramClass> reader =
+ new JarClassFileReader<>(applicationReader, collector::set, ClassKind.PROGRAM);
+ reader.read(new PathOrigin(path), bytes);
return collector.get().asProgramClass();
}
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index 40f48d2..f610d83 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -26,6 +26,7 @@
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.StringUtils;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import java.io.IOException;
@@ -186,8 +187,7 @@
*/
public static void main(String[] args) {
if (args.length == 0) {
- System.err.println(USAGE_MESSAGE);
- System.exit(ExceptionUtils.STATUS_ERROR);
+ throw new RuntimeException(StringUtils.joinLines("Invalid invocation.", USAGE_MESSAGE));
}
ExceptionUtils.withMainProgramHandler(() -> run(args));
}
diff --git a/src/main/java/com/android/tools/r8/L8CommandParser.java b/src/main/java/com/android/tools/r8/L8CommandParser.java
index 0a57389..3dac9dc 100644
--- a/src/main/java/com/android/tools/r8/L8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/L8CommandParser.java
@@ -25,9 +25,9 @@
L8Command command = parse(args, Origin.root()).build();
if (command.isPrintHelp()) {
System.out.println(USAGE_MESSAGE);
- System.exit(1);
+ } else {
+ L8.run(command);
}
- L8.run(command);
}
static final String USAGE_MESSAGE =
diff --git a/src/main/java/com/android/tools/r8/PrintSeeds.java b/src/main/java/com/android/tools/r8/PrintSeeds.java
index b8a426c..212eb32 100644
--- a/src/main/java/com/android/tools/r8/PrintSeeds.java
+++ b/src/main/java/com/android/tools/r8/PrintSeeds.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import java.io.IOException;
@@ -39,21 +40,19 @@
public class PrintSeeds {
private static final String USAGE =
- "Arguments: <rt.jar> <r8.jar> <pg-conf.txt>\n"
- + "\n"
- + "PrintSeeds prints the classes, interfaces, methods and fields selected by\n"
- + "<pg-conf.txt> when compiling <r8.jar> alongside <rt.jar>.\n"
- + "\n"
- + "The output format is identical to what is printed when -printseeds is specified in\n"
- + "<pg-conf.txt>, but running PrintSeeds can be faster than running R8 with \n"
- + "-printseeds. See also the "
- + PrintUses.class.getSimpleName()
- + " program in R8.";
+ StringUtils.joinLines(
+ "Arguments: <rt.jar> <r8.jar> <pg-conf.txt>",
+ "",
+ "PrintSeeds prints the classes, interfaces, methods and fields selected by",
+ "<pg-conf.txt> when compiling <r8.jar> alongside <rt.jar>.",
+ "",
+ "The output format is identical to what is printed when -printseeds is specified in",
+ "<pg-conf.txt>, but running PrintSeeds can be faster than running R8 with",
+ "-printseeds. See also the " + PrintUses.class.getSimpleName() + " program in R8.");
public static void main(String[] args) throws Exception {
if (args.length != 3) {
- System.out.println(USAGE.replace("\n", System.lineSeparator()));
- System.exit(1);
+ throw new RuntimeException(StringUtils.joinLines("Invalid invocation.", USAGE));
}
Path rtJar = Paths.get(args[0]);
Path r8Jar = Paths.get(args[1]);
diff --git a/src/main/java/com/android/tools/r8/PrintUses.java b/src/main/java/com/android/tools/r8/PrintUses.java
index 3d039c9..ef8708f 100644
--- a/src/main/java/com/android/tools/r8/PrintUses.java
+++ b/src/main/java/com/android/tools/r8/PrintUses.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
@@ -60,17 +61,18 @@
public class PrintUses {
private static final String USAGE =
- "Arguments: [--keeprules, --keeprules-allowobfuscation] <rt.jar> <r8.jar> <sample.jar>\n"
- + "\n"
- + "PrintUses prints the classes, interfaces, methods and fields used by <sample.jar>,\n"
- + "restricted to classes and interfaces in <r8.jar> that are not in <sample.jar>.\n"
- + "<rt.jar> and <r8.jar> should point to libraries used by <sample.jar>.\n"
- + "\n"
- + "The output is in the same format as what is printed when specifying -printseeds in\n"
- + "a ProGuard configuration file. Use --keeprules or --keeprules-allowobfuscation for "
- + "outputting proguard keep rules. See also the "
- + PrintSeeds.class.getSimpleName()
- + " program in R8.";
+ StringUtils.joinLines(
+ "Arguments: [--keeprules, --keeprules-allowobfuscation] <rt.jar> <r8.jar> <sample.jar>",
+ "",
+ "PrintUses prints the classes, interfaces, methods and fields used by <sample.jar>,",
+ "restricted to classes and interfaces in <r8.jar> that are not in <sample.jar>.",
+ "<rt.jar> and <r8.jar> should point to libraries used by <sample.jar>.",
+ "",
+ "The output is in the same format as what is printed when specifying -printseeds in",
+ "a ProGuard configuration file. Use --keeprules or --keeprules-allowobfuscation for",
+ "outputting proguard keep rules. See also the "
+ + PrintSeeds.class.getSimpleName()
+ + " program in R8.");
private final Set<String> descriptors;
private final Printer printer;
@@ -137,9 +139,9 @@
@Override
public void registerInvokeSuper(DexMethod method) {
- DexEncodedMethod superTarget = appInfo.lookupSuperTarget(method, context);
+ DexClassAndMethod superTarget = appInfo.lookupSuperTarget(method, context);
if (superTarget != null) {
- addMethod(superTarget.method);
+ addMethod(superTarget.getReference());
} else {
addMethod(method);
}
@@ -247,12 +249,12 @@
}
private void registerMethod(ProgramMethod method) {
- DexEncodedMethod superTarget =
+ DexClassAndMethod superTarget =
appInfo
.resolveMethodOn(method.getHolder(), method.getReference())
.lookupInvokeSpecialTarget(context, appInfo);
if (superTarget != null) {
- addMethod(superTarget.method);
+ addMethod(superTarget.getReference());
}
for (DexType type : method.getDefinition().parameters().values) {
registerTypeReference(type);
@@ -305,8 +307,7 @@
public static void main(String... args) throws Exception {
if (args.length != 3 && args.length != 4 && args.length != 5) {
- System.out.println(USAGE.replace("\n", System.lineSeparator()));
- return;
+ throw new RuntimeException(StringUtils.joinLines("Invalid invocation.", USAGE));
}
int argumentIndex = 0;
boolean printKeep = false;
@@ -318,9 +319,9 @@
// Make sure there is only one argument that mentions --keeprules
for (int i = 1; i < args.length; i++) {
if (args[i].startsWith("-keeprules")) {
- System.out.println("Use either --keeprules or --keeprules-allowobfuscation, not both.");
- System.out.println(USAGE.replace("\n", System.lineSeparator()));
- return;
+ throw new RuntimeException(
+ StringUtils.joinLines(
+ "Use either --keeprules or --keeprules-allowobfuscation, not both.", USAGE));
}
}
}
@@ -342,8 +343,7 @@
printUses.analyze();
printUses.print();
if (printUses.errors > 0) {
- System.err.println(printUses.errors + " errors");
- System.exit(1);
+ throw new RuntimeException(printUses.errors + " errors");
}
}
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 1dc771e..9e7a967 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -117,6 +117,7 @@
import com.android.tools.r8.utils.LineNumberOptimizer;
import com.android.tools.r8.utils.SelfRetraceTest;
import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
@@ -1130,8 +1131,7 @@
*/
public static void main(String[] args) {
if (args.length == 0) {
- System.err.println(USAGE_MESSAGE);
- System.exit(ExceptionUtils.STATUS_ERROR);
+ throw new RuntimeException(StringUtils.joinLines("Invalid invocation.", USAGE_MESSAGE));
}
ExceptionUtils.withMainProgramHandler(() -> run(args));
}
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 29112ed..d6ab3a6 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -40,6 +40,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
@@ -182,28 +183,21 @@
/** Add proguard configuration files with rules for automatic main-dex-list calculation. */
public Builder addMainDexRulesFiles(Path... paths) {
- guard(() -> {
- for (Path path : paths) {
- mainDexRules.add(new ProguardConfigurationSourceFile(path));
- }
- });
- return self();
+ return addMainDexRulesFiles(Arrays.asList(paths));
}
/** Add proguard configuration files with rules for automatic main-dex-list calculation. */
public Builder addMainDexRulesFiles(Collection<Path> paths) {
- guard(() -> {
- for (Path path : paths) {
- mainDexRules.add(new ProguardConfigurationSourceFile(path));
- }
- });
+ guard(() -> paths.forEach(p -> mainDexRules.add(new ProguardConfigurationSourceFile(p))));
return self();
}
/** Add proguard rules for automatic main-dex-list calculation. */
public Builder addMainDexRules(List<String> lines, Origin origin) {
- guard(() -> mainDexRules.add(
- new ProguardConfigurationSourceStrings(lines, Paths.get("."), origin)));
+ guard(
+ () ->
+ mainDexRules.add(
+ new ProguardConfigurationSourceStrings(lines, Paths.get("."), origin)));
return self();
}
@@ -432,7 +426,7 @@
&& mainDexRules.isEmpty()
&& !getAppBuilder().hasMainDexList()) {
reporter.error(
- "Option --main-dex-list-output require --main-dex-rules and/or --main-dex-list");
+ "Option --main-dex-list-output requires --main-dex-rules and/or --main-dex-list");
}
if (!(getProgramConsumer() instanceof ClassFileConsumer)
&& getMinApiLevel() >= AndroidApiLevel.L.getLevel()) {
@@ -479,15 +473,8 @@
private R8Command makeR8Command() {
Reporter reporter = getReporter();
DexItemFactory factory = new DexItemFactory();
- List<ProguardConfigurationRule> mainDexKeepRules;
- if (this.mainDexRules.isEmpty()) {
- mainDexKeepRules = ImmutableList.of();
- } else {
- ProguardConfigurationParser parser =
- new ProguardConfigurationParser(factory, reporter);
- parser.parse(mainDexRules);
- mainDexKeepRules = parser.getConfig().getRules();
- }
+ List<ProguardConfigurationRule> mainDexKeepRules =
+ ProguardConfigurationParser.parse(mainDexRules, factory, reporter);
DesugaredLibraryConfiguration libraryConfiguration =
getDesugaredLibraryConfiguration(factory, false);
diff --git a/src/main/java/com/android/tools/r8/R8CommandParser.java b/src/main/java/com/android/tools/r8/R8CommandParser.java
index 3cb01e1..277bec8 100644
--- a/src/main/java/com/android/tools/r8/R8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/R8CommandParser.java
@@ -42,9 +42,9 @@
R8Command command = parse(args, Origin.root()).build();
if (command.isPrintHelp()) {
System.out.println(USAGE_MESSAGE);
- System.exit(1);
+ } else {
+ R8.run(command);
}
- R8.run(command);
}
// Internal state to verify parsing properties not enforced by the builder.
diff --git a/src/main/java/com/android/tools/r8/ReadMainDexList.java b/src/main/java/com/android/tools/r8/ReadMainDexList.java
index de34761..4061e68 100644
--- a/src/main/java/com/android/tools/r8/ReadMainDexList.java
+++ b/src/main/java/com/android/tools/r8/ReadMainDexList.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.Iterators;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -40,8 +41,9 @@
private void run(String[] args) throws Exception {
if (args.length < 1 || args.length > 3) {
- System.out.println("Usage: command [-k] <main_dex_list> [<proguard_map>]");
- System.exit(0);
+ throw new RuntimeException(
+ StringUtils.joinLines(
+ "Invalid invocation.", "Usage: command [-k] <main_dex_list> [<proguard_map>]"));
}
Iterator<String> arguments = Iterators.forArray(args);
diff --git a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
index 177f2ae..24b42d3 100644
--- a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
+++ b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
@@ -12,7 +12,7 @@
import com.android.tools.r8.Version;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.origin.CommandLineOrigin;
-import com.android.tools.r8.utils.AbortException;
+import com.android.tools.r8.utils.ExceptionUtils;
import com.google.common.collect.ImmutableList;
import java.nio.file.Paths;
import java.util.List;
@@ -211,12 +211,6 @@
}
public static void main(String[] args) {
- try {
- run(args);
- } catch (CompilationFailedException | AbortException e) {
- // Detail of the errors were already reported
- System.err.println("Compilation failed");
- System.exit(1);
- }
+ ExceptionUtils.withMainProgramHandler(() -> run(args));
}
}
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index b46cd9b..4b035d4 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -151,7 +151,7 @@
// TODO: try and preload less classes.
readProguardMap(proguardMap, builder, executorService, futures);
ClassReader classReader = new ClassReader(executorService, futures);
- JarClassFileReader jcf = classReader.readSources();
+ JarClassFileReader<DexProgramClass> jcf = classReader.readSources();
ThreadUtils.awaitFutures(futures);
classReader.initializeLazyClassCollection(builder);
for (ProgramResourceProvider provider : inputApp.getProgramResourceProviders()) {
@@ -296,46 +296,46 @@
this.futures = futures;
}
- private <T extends DexClass> void readDexSources(
- List<ProgramResource> dexSources, ClassKind classKind, Queue<T> classes)
+ private void readDexSources(List<ProgramResource> dexSources, Queue<DexProgramClass> classes)
throws IOException, ResourceException {
if (dexSources.size() > 0) {
- List<DexParser> dexParsers = new ArrayList<>(dexSources.size());
+ List<DexParser<DexProgramClass>> dexParsers = new ArrayList<>(dexSources.size());
int computedMinApiLevel = options.minApiLevel;
for (ProgramResource input : dexSources) {
DexReader dexReader = new DexReader(input);
if (options.passthroughDexCode) {
computedMinApiLevel = validateOrComputeMinApiLevel(computedMinApiLevel, dexReader);
}
- dexParsers.add(new DexParser(dexReader, classKind, options));
+ dexParsers.add(new DexParser<>(dexReader, PROGRAM, options));
}
options.minApiLevel = computedMinApiLevel;
- for (DexParser dexParser : dexParsers) {
+ for (DexParser<DexProgramClass> dexParser : dexParsers) {
dexParser.populateIndexTables();
}
// Read the DexCode items and DexProgramClass items in parallel.
if (!options.skipReadingDexCode) {
- for (DexParser dexParser : dexParsers) {
- futures.add(executorService.submit(() -> {
- dexParser.addClassDefsTo(
- classKind.bridgeConsumer(classes::add)); // Depends on Methods, Code items etc.
- }));
+ for (DexParser<DexProgramClass> dexParser : dexParsers) {
+ futures.add(
+ executorService.submit(
+ () -> {
+ dexParser.addClassDefsTo(classes::add); // Depends on Methods, Code items etc.
+ }));
}
}
}
}
- private <T extends DexClass> JarClassFileReader readClassSources(
- List<ProgramResource> classSources, ClassKind classKind, Queue<T> classes) {
- JarClassFileReader reader = new JarClassFileReader(
- application, classKind.bridgeConsumer(classes::add));
+ private JarClassFileReader<DexProgramClass> readClassSources(
+ List<ProgramResource> classSources, Queue<DexProgramClass> classes) {
+ JarClassFileReader<DexProgramClass> reader =
+ new JarClassFileReader<>(application, classes::add, PROGRAM);
// Read classes in parallel.
for (ProgramResource input : classSources) {
futures.add(
executorService.submit(
() -> {
- reader.read(input, classKind);
+ reader.read(input);
// No other way to have a void callable, but we want the IOException from read
// to be wrapped into an ExecutionException.
return null;
@@ -344,7 +344,7 @@
return reader;
}
- JarClassFileReader readSources() throws IOException, ResourceException {
+ JarClassFileReader<DexProgramClass> readSources() throws IOException, ResourceException {
Collection<ProgramResource> resources = inputApp.computeAllProgramResources();
List<ProgramResource> dexResources = new ArrayList<>(resources.size());
List<ProgramResource> cfResources = new ArrayList<>(resources.size());
@@ -356,12 +356,14 @@
cfResources.add(resource);
}
}
- readDexSources(dexResources, PROGRAM, programClasses);
- return readClassSources(cfResources, PROGRAM, programClasses);
+ readDexSources(dexResources, programClasses);
+ return readClassSources(cfResources, programClasses);
}
- private <T extends DexClass> ClassProvider<T> buildClassProvider(ClassKind classKind,
- Queue<T> preloadedClasses, List<ClassFileResourceProvider> resourceProviders,
+ private <T extends DexClass> ClassProvider<T> buildClassProvider(
+ ClassKind<T> classKind,
+ Queue<T> preloadedClasses,
+ List<ClassFileResourceProvider> resourceProviders,
JarApplicationReader reader) {
List<ClassProvider<T>> providers = new ArrayList<>();
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 2408b27..cd78995 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -82,14 +82,14 @@
import java.util.function.Consumer;
import java.util.function.Supplier;
-public class DexParser {
+public class DexParser<T extends DexClass> {
private final int NO_INDEX = -1;
private final Origin origin;
private DexReader dexReader;
private final DexSection[] dexSections;
private int[] stringIDs;
- private final ClassKind classKind;
+ private final ClassKind<T> classKind;
private final InternalOptions options;
private Object2LongMap<String> checksums;
@@ -102,7 +102,8 @@
}
private static DexSection[] parseMapFrom(DexReader dexReader) {
- DexParser dexParser = new DexParser(dexReader, ClassKind.PROGRAM, new InternalOptions());
+ DexParser<DexProgramClass> dexParser =
+ new DexParser<>(dexReader, ClassKind.PROGRAM, new InternalOptions());
return dexParser.dexSections;
}
@@ -127,7 +128,7 @@
// Factory to canonicalize certain dexitems.
private final DexItemFactory dexItemFactory;
- public DexParser(DexReader dexReader, ClassKind classKind, InternalOptions options) {
+ public DexParser(DexReader dexReader, ClassKind<T> classKind, InternalOptions options) {
assert dexReader.getOrigin() != null;
this.origin = dexReader.getOrigin();
this.dexReader = dexReader;
@@ -419,14 +420,14 @@
return result;
}
- private <T> Object cacheAt(int offset, Supplier<T> function, Supplier<T> defaultValue) {
+ private <S> Object cacheAt(int offset, Supplier<S> function, Supplier<S> defaultValue) {
if (offset == 0) {
return defaultValue.get();
}
return cacheAt(offset, function);
}
- private <T> Object cacheAt(int offset, Supplier<T> function) {
+ private <S> Object cacheAt(int offset, Supplier<S> function) {
if (offset == 0) {
return null; // return null for offset zero.
}
@@ -704,7 +705,7 @@
return methods;
}
- void addClassDefsTo(Consumer<DexClass> classCollection) {
+ void addClassDefsTo(Consumer<T> classCollection) {
final DexSection dexSection = lookupSection(Constants.TYPE_CLASS_DEF_ITEM);
final int length = dexSection.length;
indexedItems.initializeClasses(length);
@@ -796,7 +797,7 @@
ChecksumSupplier checksumSupplier =
finalChecksum == null ? DexProgramClass::invalidChecksumRequest : c -> finalChecksum;
- DexClass clazz =
+ T clazz =
classKind.create(
type,
Kind.DEX,
@@ -925,7 +926,7 @@
int hsize = dexReader.getSleb128();
int realHsize = Math.abs(hsize);
// - handlers encoded_type_addr_pair[abs(size)]
- TryHandler.TypeAddrPair pairs[] = new TryHandler.TypeAddrPair[realHsize];
+ TryHandler.TypeAddrPair[] pairs = new TryHandler.TypeAddrPair[realHsize];
for (int j = 0; j < realHsize; j++) {
int typeIdx = dexReader.getUleb128();
int addr = dexReader.getUleb128();
@@ -1415,7 +1416,7 @@
List<DexType> members = DexAnnotation.getMemberClassesFromAnnotation(annotation, factory);
if (memberClasses == null) {
memberClasses = members;
- } else {
+ } else if (members != null) {
memberClasses.addAll(members);
}
} else if (DexAnnotation.isSignatureAnnotation(annotation, factory)
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index bce34ff..917f0de 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -221,6 +221,7 @@
graphLens,
namingLens,
initClassLens,
+ transaction.rewriter,
indexedItems.classes,
indexedItems.protos,
indexedItems.types,
@@ -747,7 +748,7 @@
this.graphLens = graphLens;
this.initClassLens = initClassLens;
this.namingLens = namingLens;
- this.rewriter = new LensCodeRewriterUtils(appView);
+ this.rewriter = new LensCodeRewriterUtils(appView, true);
}
private <T extends DexItem> boolean maybeInsert(T item, Set<T> set, Set<T> baseSet) {
diff --git a/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java b/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java
index d9a5609..4ee416c 100644
--- a/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java
+++ b/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java
@@ -362,8 +362,7 @@
run(args);
} catch (FeatureMappingException e) {
// TODO(ricow): Report feature mapping errors via the reporter.
- System.err.println("Splitting failed: " + e.getMessage());
- System.exit(1);
+ throw new RuntimeException("Splitting failed: " + e.getMessage());
}
});
}
diff --git a/src/main/java/com/android/tools/r8/graph/AccessFlags.java b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
index 98806f6..70d7cdb 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
@@ -320,6 +320,33 @@
return self();
}
+ public B setPrivate(boolean value) {
+ if (value) {
+ flags.setPrivate();
+ } else {
+ flags.unsetPrivate();
+ }
+ return self();
+ }
+
+ public B setProtected(boolean value) {
+ if (value) {
+ flags.setProtected();
+ } else {
+ flags.unsetProtected();
+ }
+ return self();
+ }
+
+ public B setPublic(boolean value) {
+ if (value) {
+ flags.setPublic();
+ } else {
+ flags.unsetPublic();
+ }
+ return self();
+ }
+
public B setStatic() {
flags.setStatic();
return self();
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
index e747aa0..56b1a53 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -492,13 +492,13 @@
* @return The actual target for {@code method} or {@code null} if none found.
*/
// TODO(b/155968472): This should take a parameter `boolean isInterface` and use resolveMethod().
- public DexEncodedMethod lookupSuperTarget(DexMethod method, DexProgramClass context) {
+ public DexClassAndMethod lookupSuperTarget(DexMethod method, DexProgramClass context) {
assert checkIfObsolete();
return unsafeResolveMethodDueToDexFormat(method).lookupInvokeSuperTarget(context, this);
}
// TODO(b/155968472): This should take a parameter `boolean isInterface` and use resolveMethod().
- public DexEncodedMethod lookupSuperTarget(DexMethod method, ProgramMethod context) {
+ public DexClassAndMethod lookupSuperTarget(DexMethod method, ProgramMethod context) {
return lookupSuperTarget(method, context.getHolder());
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 6bedacf..531c78a 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -122,9 +122,9 @@
this.callSiteOptimizationInfoPropagator = null;
}
- this.libraryMemberOptimizer = new LibraryMemberOptimizer(this);
this.libraryMethodSideEffectModelCollection =
new LibraryMethodSideEffectModelCollection(dexItemFactory());
+ this.libraryMemberOptimizer = new LibraryMemberOptimizer(this);
if (enableWholeProgramOptimizations() && options().protoShrinking().isProtoShrinkingEnabled()) {
this.protoShrinker = new ProtoShrinker(withLiveness());
diff --git a/src/main/java/com/android/tools/r8/graph/ClassKind.java b/src/main/java/com/android/tools/r8/graph/ClassKind.java
index 3929cfb..baf5365 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassKind.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassKind.java
@@ -9,97 +9,97 @@
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.origin.Origin;
import java.util.List;
-import java.util.function.Consumer;
import java.util.function.Predicate;
/** Kind of the application class. Can be program, classpath or library. */
-public enum ClassKind {
- PROGRAM(DexProgramClass::new, DexClass::isProgramClass),
- CLASSPATH(
- (type,
- kind,
- origin,
- accessFlags,
- superType,
- interfaces,
- sourceFile,
- nestHost,
- nestMembers,
- enclosingMember,
- innerClasses,
- classSignature,
- annotations,
- staticFields,
- instanceFields,
- directMethods,
- virtualMethods,
- skipNameValidationForTesting,
- checksumSupplier) -> {
- return new DexClasspathClass(
- type,
- kind,
- origin,
- accessFlags,
- superType,
- interfaces,
- sourceFile,
- nestHost,
- nestMembers,
- enclosingMember,
- innerClasses,
- classSignature,
- annotations,
- staticFields,
- instanceFields,
- directMethods,
- virtualMethods,
- skipNameValidationForTesting);
- },
- DexClass::isClasspathClass),
- LIBRARY(
- (type,
- kind,
- origin,
- accessFlags,
- superType,
- interfaces,
- sourceFile,
- nestHost,
- nestMembers,
- enclosingMember,
- innerClasses,
- classSignature,
- annotations,
- staticFields,
- instanceFields,
- directMethods,
- virtualMethods,
- skipNameValidationForTesting,
- checksumSupplier) -> {
- return new DexLibraryClass(
- type,
- kind,
- origin,
- accessFlags,
- superType,
- interfaces,
- sourceFile,
- nestHost,
- nestMembers,
- enclosingMember,
- innerClasses,
- classSignature,
- annotations,
- staticFields,
- instanceFields,
- directMethods,
- virtualMethods,
- skipNameValidationForTesting);
- },
- DexClass::isLibraryClass);
+public class ClassKind<C extends DexClass> {
+ public static ClassKind<DexProgramClass> PROGRAM =
+ new ClassKind<>(DexProgramClass::new, DexClass::isProgramClass);
+ public static ClassKind<DexClasspathClass> CLASSPATH =
+ new ClassKind<>(
+ (type,
+ kind,
+ origin,
+ accessFlags,
+ superType,
+ interfaces,
+ sourceFile,
+ nestHost,
+ nestMembers,
+ enclosingMember,
+ innerClasses,
+ classSignature,
+ annotations,
+ staticFields,
+ instanceFields,
+ directMethods,
+ virtualMethods,
+ skipNameValidationForTesting,
+ checksumSupplier) ->
+ new DexClasspathClass(
+ type,
+ kind,
+ origin,
+ accessFlags,
+ superType,
+ interfaces,
+ sourceFile,
+ nestHost,
+ nestMembers,
+ enclosingMember,
+ innerClasses,
+ classSignature,
+ annotations,
+ staticFields,
+ instanceFields,
+ directMethods,
+ virtualMethods,
+ skipNameValidationForTesting),
+ DexClass::isClasspathClass);
+ public static final ClassKind<DexLibraryClass> LIBRARY =
+ new ClassKind<>(
+ (type,
+ kind,
+ origin,
+ accessFlags,
+ superType,
+ interfaces,
+ sourceFile,
+ nestHost,
+ nestMembers,
+ enclosingMember,
+ innerClasses,
+ classSignature,
+ annotations,
+ staticFields,
+ instanceFields,
+ directMethods,
+ virtualMethods,
+ skipNameValidationForTesting,
+ checksumSupplier) ->
+ new DexLibraryClass(
+ type,
+ kind,
+ origin,
+ accessFlags,
+ superType,
+ interfaces,
+ sourceFile,
+ nestHost,
+ nestMembers,
+ enclosingMember,
+ innerClasses,
+ classSignature,
+ annotations,
+ staticFields,
+ instanceFields,
+ directMethods,
+ virtualMethods,
+ skipNameValidationForTesting),
+ DexClass::isLibraryClass);
- private interface Factory {
- DexClass create(
+ private interface Factory<C extends DexClass> {
+ C create(
DexType type,
Kind kind,
Origin origin,
@@ -121,15 +121,15 @@
ChecksumSupplier checksumSupplier);
}
- private final Factory factory;
+ private final Factory<C> factory;
private final Predicate<DexClass> check;
- ClassKind(Factory factory, Predicate<DexClass> check) {
+ ClassKind(Factory<C> factory, Predicate<DexClass> check) {
this.factory = factory;
this.check = check;
}
- public DexClass create(
+ public C create(
DexType type,
Kind kind,
Origin origin,
@@ -174,12 +174,4 @@
public boolean isOfKind(DexClass clazz) {
return check.test(clazz);
}
-
- public <T extends DexClass> Consumer<DexClass> bridgeConsumer(Consumer<T> consumer) {
- return clazz -> {
- assert isOfKind(clazz);
- @SuppressWarnings("unchecked") T specialized = (T) clazz;
- consumer.accept(specialized);
- };
- }
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 82faf34..70bad80 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -470,6 +470,15 @@
}
/** Find method in this class matching {@param method}. */
+ public DexClassAndMethod lookupClassMethod(DexMethod method) {
+ return toClassMethodOrNull(methodCollection.getMethod(method));
+ }
+
+ private DexClassAndMethod toClassMethodOrNull(DexEncodedMethod method) {
+ return method != null ? DexClassAndMethod.create(this, method) : null;
+ }
+
+ /** Find method in this class matching {@param method}. */
public DexEncodedMethod lookupMethod(DexMethod method) {
return methodCollection.getMethod(method);
}
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 4acd029..4fcd92b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -330,6 +330,10 @@
assert parameterAnnotationsList != null;
}
+ public static DexEncodedMethod toMethodDefinitionOrNull(DexClassAndMethod method) {
+ return method != null ? method.getDefinition() : null;
+ }
+
public boolean isDeprecated() {
return deprecated;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index a93000e..385fb58 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -23,7 +23,6 @@
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.ReferenceTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
-import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring;
@@ -453,13 +452,16 @@
public final StringBuildingMethods stringBufferMethods =
new StringBuildingMethods(stringBufferType);
public final BooleanMembers booleanMembers = new BooleanMembers();
+ public final ByteMembers byteMembers = new ByteMembers();
+ public final CharMembers charMembers = new CharMembers();
public final FloatMembers floatMembers = new FloatMembers();
public final IntegerMembers integerMembers = new IntegerMembers();
+ public final LongMembers longMembers = new LongMembers();
public final ObjectsMethods objectsMethods = new ObjectsMethods();
public final ObjectMembers objectMembers = new ObjectMembers();
+ public final ShortMembers shortMembers = new ShortMembers();
public final StringMembers stringMembers = new StringMembers();
- public final LongMembers longMembers = new LongMembers();
- public final DoubleMethods doubleMethods = new DoubleMethods();
+ public final DoubleMembers doubleMembers = new DoubleMembers();
public final ThrowableMethods throwableMethods = new ThrowableMethods();
public final AssertionErrorMethods assertionErrorMethods = new AssertionErrorMethods();
public final ClassMethods classMethods = new ClassMethods();
@@ -715,7 +717,22 @@
public Set<DexType> libraryClassesWithoutStaticInitialization =
ImmutableSet.of(
- boxedBooleanType, enumType, npeType, objectType, stringBufferType, stringBuilderType);
+ boxedBooleanType,
+ boxedByteType,
+ boxedCharType,
+ boxedDoubleType,
+ boxedFloatType,
+ boxedIntType,
+ boxedLongType,
+ boxedNumberType,
+ boxedShortType,
+ boxedVoidType,
+ enumType,
+ npeType,
+ objectType,
+ stringBufferType,
+ stringBuilderType,
+ stringType);
private boolean skipNameValidationForTesting = false;
@@ -731,35 +748,12 @@
return dexMethod == metafactoryMethod || dexMethod == metafactoryAltMethod;
}
- public interface LibraryMembers {
+ public abstract static class LibraryMembers {
- void forEachFinalField(Consumer<DexField> consumer);
+ public void forEachFinalField(Consumer<DexField> consumer) {}
}
- public class BooleanMembers implements LibraryMembers {
-
- public final DexField FALSE = createField(boxedBooleanType, boxedBooleanType, "FALSE");
- public final DexField TRUE = createField(boxedBooleanType, boxedBooleanType, "TRUE");
- public final DexField TYPE = createField(boxedBooleanType, classType, "TYPE");
-
- public final DexMethod booleanValue =
- createMethod(boxedBooleanType, createProto(booleanType), "booleanValue");
- public final DexMethod parseBoolean =
- createMethod(boxedBooleanType, createProto(booleanType, stringType), "parseBoolean");
- public final DexMethod valueOf =
- createMethod(boxedBooleanType, createProto(boxedBooleanType, booleanType), "valueOf");
-
- private BooleanMembers() {}
-
- @Override
- public void forEachFinalField(Consumer<DexField> consumer) {
- consumer.accept(FALSE);
- consumer.accept(TRUE);
- consumer.accept(TYPE);
- }
- }
-
- public class AndroidOsBuildMembers implements LibraryMembers {
+ public class AndroidOsBuildMembers extends LibraryMembers {
public final DexField BOOTLOADER = createField(androidOsBuildType, stringType, "BOOTLOADER");
public final DexField BRAND = createField(androidOsBuildType, stringType, "BRAND");
@@ -805,7 +799,7 @@
}
}
- public class AndroidOsBuildVersionMembers implements LibraryMembers {
+ public class AndroidOsBuildVersionMembers extends LibraryMembers {
public final DexField CODENAME = createField(androidOsBuildVersionType, stringType, "CODENAME");
public final DexField RELEASE = createField(androidOsBuildVersionType, stringType, "RELEASE");
@@ -824,7 +818,7 @@
}
}
- public class AndroidOsBundleMembers implements LibraryMembers {
+ public class AndroidOsBundleMembers extends LibraryMembers {
public final DexField CREATOR =
createField(androidOsBundleType, androidOsParcelableCreatorType, "CREATOR");
@@ -837,7 +831,7 @@
}
}
- public class AndroidSystemOsConstantsMembers implements LibraryMembers {
+ public class AndroidSystemOsConstantsMembers extends LibraryMembers {
public final DexField S_IRUSR = createField(androidSystemOsConstantsType, intType, "S_IRUSR");
public final DexField S_IXUSR = createField(androidSystemOsConstantsType, intType, "S_IXUSR");
@@ -849,7 +843,7 @@
}
}
- public class AndroidViewViewMembers implements LibraryMembers {
+ public class AndroidViewViewMembers extends LibraryMembers {
public final DexField TRANSLATION_Z =
createField(androidViewViewType, androidUtilPropertyType, "TRANSLATION_Z");
@@ -872,10 +866,54 @@
}
}
- public class FloatMembers implements LibraryMembers {
+ public class BooleanMembers extends LibraryMembers {
+
+ public final DexField FALSE = createField(boxedBooleanType, boxedBooleanType, "FALSE");
+ public final DexField TRUE = createField(boxedBooleanType, boxedBooleanType, "TRUE");
+ public final DexField TYPE = createField(boxedBooleanType, classType, "TYPE");
+
+ public final DexMethod booleanValue =
+ createMethod(boxedBooleanType, createProto(booleanType), "booleanValue");
+ public final DexMethod parseBoolean =
+ createMethod(boxedBooleanType, createProto(booleanType, stringType), "parseBoolean");
+ public final DexMethod valueOf =
+ createMethod(boxedBooleanType, createProto(boxedBooleanType, booleanType), "valueOf");
+ public final DexMethod toString =
+ createMethod(boxedBooleanType, createProto(stringType), "toString");
+
+ private BooleanMembers() {}
+
+ @Override
+ public void forEachFinalField(Consumer<DexField> consumer) {
+ consumer.accept(FALSE);
+ consumer.accept(TRUE);
+ consumer.accept(TYPE);
+ }
+ }
+
+ public class ByteMembers extends LibraryMembers {
+
+ public final DexMethod toString =
+ createMethod(boxedByteType, createProto(stringType), "toString");
+
+ private ByteMembers() {}
+ }
+
+ public class CharMembers extends LibraryMembers {
+
+ public final DexMethod toString =
+ createMethod(boxedCharType, createProto(stringType), "toString");
+
+ private CharMembers() {}
+ }
+
+ public class FloatMembers extends LibraryMembers {
public final DexField TYPE = createField(boxedFloatType, classType, "TYPE");
+ public final DexMethod toString =
+ createMethod(boxedFloatType, createProto(stringType), "toString");
+
private FloatMembers() {}
@Override
@@ -884,7 +922,7 @@
}
}
- public class JavaIoFileMembers implements LibraryMembers {
+ public class JavaIoFileMembers extends LibraryMembers {
public final DexField pathSeparator = createField(javaIoFileType, stringType, "pathSeparator");
public final DexField separator = createField(javaIoFileType, stringType, "separator");
@@ -896,7 +934,7 @@
}
}
- public class JavaMathBigIntegerMembers implements LibraryMembers {
+ public class JavaMathBigIntegerMembers extends LibraryMembers {
public final DexField ONE = createField(javaMathBigIntegerType, javaMathBigIntegerType, "ONE");
public final DexField ZERO =
@@ -909,7 +947,7 @@
}
}
- public class JavaNioByteOrderMembers implements LibraryMembers {
+ public class JavaNioByteOrderMembers extends LibraryMembers {
public final DexField LITTLE_ENDIAN =
createField(javaNioByteOrderType, javaNioByteOrderType, "LITTLE_ENDIAN");
@@ -937,7 +975,7 @@
}
}
- public class JavaUtilComparatorMembers implements LibraryMembers {
+ public class JavaUtilComparatorMembers extends LibraryMembers {
public final DexField EMPTY_LIST =
createField(javaUtilCollectionsType, javaUtilListType, "EMPTY_LIST");
@@ -951,7 +989,7 @@
}
}
- public class JavaUtilConcurrentTimeUnitMembers implements LibraryMembers {
+ public class JavaUtilConcurrentTimeUnitMembers extends LibraryMembers {
public final DexField DAYS =
createField(javaUtilConcurrentTimeUnitType, javaUtilConcurrentTimeUnitType, "DAYS");
@@ -980,7 +1018,7 @@
}
}
- public class JavaUtilLocaleMembers implements LibraryMembers {
+ public class JavaUtilLocaleMembers extends LibraryMembers {
public final DexField ENGLISH = createField(javaUtilLocaleType, javaUtilLocaleType, "ENGLISH");
public final DexField ROOT = createField(javaUtilLocaleType, javaUtilLocaleType, "ROOT");
@@ -994,7 +1032,7 @@
}
}
- public class JavaUtilLoggingLevelMembers implements LibraryMembers {
+ public class JavaUtilLoggingLevelMembers extends LibraryMembers {
public final DexField CONFIG =
createField(javaUtilLoggingLevelType, javaUtilLoggingLevelType, "CONFIG");
@@ -1020,11 +1058,13 @@
}
}
- public class LongMembers implements LibraryMembers {
+ public class LongMembers extends LibraryMembers {
public final DexField TYPE = createField(boxedLongType, classType, "TYPE");
public final DexMethod compare;
+ public final DexMethod toString =
+ createMethod(boxedLongType, createProto(stringType), "toString");
private LongMembers() {
compare = createMethod(boxedLongDescriptor,
@@ -1037,11 +1077,14 @@
}
}
- public class DoubleMethods {
+ public class DoubleMembers {
public final DexMethod isNaN;
- private DoubleMethods() {
+ public final DexMethod toString =
+ createMethod(boxedDoubleType, createProto(stringType), "toString");
+
+ private DoubleMembers() {
isNaN =
createMethod(
boxedDoubleDescriptor,
@@ -1051,10 +1094,13 @@
}
}
- public class IntegerMembers implements LibraryMembers {
+ public class IntegerMembers extends LibraryMembers {
public final DexField TYPE = createField(boxedIntType, classType, "TYPE");
+ public final DexMethod toString =
+ createMethod(boxedIntType, createProto(stringType), "toString");
+
@Override
public void forEachFinalField(Consumer<DexField> consumer) {
consumer.accept(TYPE);
@@ -1445,7 +1491,15 @@
}
}
- public class StringMembers implements LibraryMembers {
+ public class ShortMembers extends LibraryMembers {
+
+ public final DexMethod toString =
+ createMethod(boxedShortType, createProto(stringType), "toString");
+
+ private ShortMembers() {}
+ }
+
+ public class StringMembers extends LibraryMembers {
public final DexField CASE_INSENSITIVE_ORDER =
createField(stringType, javaUtilComparatorType, "CASE_INSENSITIVE_ORDER");
@@ -1454,6 +1508,8 @@
public final DexMethod length;
public final DexMethod concat;
+ public final DexMethod constructor =
+ createMethod(stringType, createProto(voidType, stringType), constructorMethodName);
public final DexMethod contains;
public final DexMethod startsWith;
public final DexMethod endsWith;
@@ -1625,22 +1681,22 @@
return constructorMethods.contains(method);
}
- public boolean constructorInvokeIsSideEffectFree(InvokeMethod invoke) {
- DexMethod invokedMethod = invoke.getInvokedMethod();
- if (invokedMethod == charSequenceConstructor) {
- // Performs callbacks on the given CharSequence, which may have side effects.
- TypeElement charSequenceType = invoke.getArgument(1).getType();
- return charSequenceType.isClassType()
- && charSequenceType.asClassType().getClassType() == stringType;
- }
-
+ public boolean constructorInvokeIsSideEffectFree(
+ DexMethod invokedMethod, List<Value> arguments) {
if (invokedMethod == defaultConstructor) {
return true;
}
+ if (invokedMethod == charSequenceConstructor) {
+ // Performs callbacks on the given CharSequence, which may have side effects.
+ TypeElement charSequenceType = arguments.get(1).getType();
+ return charSequenceType.isClassType()
+ && charSequenceType.asClassType().getClassType() == stringType;
+ }
+
if (invokedMethod == intConstructor) {
// NegativeArraySizeException - if the capacity argument is less than 0.
- Value capacityValue = invoke.inValues().get(1);
+ Value capacityValue = arguments.get(1);
if (capacityValue.hasValueRange()) {
return capacityValue.getValueRange().getMin() >= 0;
}
@@ -1649,7 +1705,7 @@
if (invokedMethod == stringConstructor) {
// NullPointerException - if str is null.
- Value strValue = invoke.inValues().get(1);
+ Value strValue = arguments.get(1);
return !strValue.getType().isNullable();
}
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index fabdde7..88596f9 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -66,10 +66,8 @@
import org.objectweb.asm.Type;
import org.objectweb.asm.TypePath;
-/**
- * Java/Jar class reader for constructing dex/graph structure.
- */
-public class JarClassFileReader {
+/** Java/Jar class reader for constructing dex/graph structure. */
+public class JarClassFileReader<T extends DexClass> {
private static final byte[] CLASSFILE_HEADER = ByteBuffer.allocate(4).putInt(0xCAFEBABE).array();
@@ -77,24 +75,25 @@
private static final int ACC_SYNTHETIC_ATTRIBUTE = 0x40000;
private final JarApplicationReader application;
- private final Consumer<DexClass> classConsumer;
+ private final Consumer<T> classConsumer;
+ private final ClassKind<T> classKind;
public JarClassFileReader(
- JarApplicationReader application, Consumer<DexClass> classConsumer) {
+ JarApplicationReader application, Consumer<T> classConsumer, ClassKind<T> classKind) {
this.application = application;
this.classConsumer = classConsumer;
+ this.classKind = classKind;
}
- public void read(ProgramResource resource, ClassKind classKind) throws ResourceException {
- read(resource.getOrigin(), classKind, resource.getBytes());
+ public void read(ProgramResource resource) throws ResourceException {
+ read(resource.getOrigin(), resource.getBytes());
}
- public void read(Origin origin, ClassKind classKind, byte[] bytes) {
- ExceptionUtils.withOriginAttachmentHandler(
- origin, () -> internalRead(origin, classKind, bytes));
+ public void read(Origin origin, byte[] bytes) {
+ ExceptionUtils.withOriginAttachmentHandler(origin, () -> internalRead(origin, bytes));
}
- public void internalRead(Origin origin, ClassKind classKind, byte[] bytes) {
+ public void internalRead(Origin origin, byte[] bytes) {
if (bytes.length < CLASSFILE_HEADER.length) {
throw new CompilationError("Invalid empty classfile", origin);
}
@@ -118,7 +117,7 @@
}
}
reader.accept(
- new CreateDexClassVisitor(origin, classKind, reader.b, application, classConsumer),
+ new CreateDexClassVisitor<>(origin, classKind, reader.b, application, classConsumer),
parsingOptions);
// Read marker.
@@ -188,12 +187,12 @@
return new DexEncodedAnnotation(application.getTypeFromDescriptor(desc), elements);
}
- private static class CreateDexClassVisitor extends ClassVisitor {
+ private static class CreateDexClassVisitor<T extends DexClass> extends ClassVisitor {
private final Origin origin;
- private final ClassKind classKind;
+ private final ClassKind<T> classKind;
private final JarApplicationReader application;
- private final Consumer<DexClass> classConsumer;
+ private final Consumer<T> classConsumer;
private final ReparseContext context = new ReparseContext();
// DexClass data.
@@ -221,10 +220,10 @@
public CreateDexClassVisitor(
Origin origin,
- ClassKind classKind,
+ ClassKind<T> classKind,
byte[] classCache,
JarApplicationReader application,
- Consumer<DexClass> classConsumer) {
+ Consumer<T> classConsumer) {
super(ASM_VERSION);
this.origin = origin;
this.classKind = classKind;
@@ -350,7 +349,7 @@
accessFlags,
name,
version,
- "must extend class java.lang.Object. Found: " + Objects.toString(superName)),
+ "must extend class java.lang.Object. Found: " + superName),
origin);
}
checkName(name);
@@ -420,7 +419,7 @@
type, defaultAnnotations, application.getFactory()));
}
checkReachabilitySensitivity();
- DexClass clazz =
+ T clazz =
classKind.create(
type,
Kind.CF,
@@ -483,7 +482,7 @@
classConsumer.accept(clazz);
}
- private ChecksumSupplier getChecksumSupplier(ClassKind classKind) {
+ private ChecksumSupplier getChecksumSupplier(ClassKind<T> classKind) {
if (application.options.encodeChecksums && classKind == ClassKind.PROGRAM) {
CRC32 crc = new CRC32();
crc.update(this.context.classCache, 0, this.context.classCache.length);
@@ -567,7 +566,7 @@
private static class CreateFieldVisitor extends FieldVisitor {
- private final CreateDexClassVisitor parent;
+ private final CreateDexClassVisitor<?> parent;
private final int access;
private final String name;
private final String desc;
@@ -575,8 +574,13 @@
private FieldTypeSignature fieldSignature;
private List<DexAnnotation> annotations = null;
- public CreateFieldVisitor(CreateDexClassVisitor parent,
- int access, String name, String desc, String signature, Object value) {
+ public CreateFieldVisitor(
+ CreateDexClassVisitor<?> parent,
+ int access,
+ String name,
+ String desc,
+ String signature,
+ Object value) {
super(ASM_VERSION);
this.parent = parent;
this.access = access;
@@ -670,9 +674,6 @@
throw new Unreachable("Unexpected static-value type " + type);
}
- private void addAnnotation(DexAnnotation annotation) {
- getAnnotations().add(annotation);
- }
private List<DexAnnotation> getAnnotations() {
if (annotations == null) {
annotations = new ArrayList<>();
@@ -684,7 +685,7 @@
private static class CreateMethodVisitor extends MethodVisitor {
private final String name;
- final CreateDexClassVisitor parent;
+ final CreateDexClassVisitor<?> parent;
private final int parameterCount;
private List<DexAnnotation> annotations = null;
private DexValue defaultAnnotation = null;
@@ -698,8 +699,13 @@
final boolean deprecated;
Code code = null;
- public CreateMethodVisitor(int access, String name, String desc, String signature,
- String[] exceptions, CreateDexClassVisitor parent) {
+ public CreateMethodVisitor(
+ int access,
+ String name,
+ String desc,
+ String signature,
+ String[] exceptions,
+ CreateDexClassVisitor<?> parent) {
super(ASM_VERSION);
this.name = name;
this.parent = parent;
diff --git a/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java b/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
index 61e54e9..7d6eac0 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
@@ -151,6 +151,10 @@
set(Constants.ACC_VARARGS);
}
+ public void unsetVarargs() {
+ unset(Constants.ACC_VARARGS);
+ }
+
public boolean isNative() {
return isSet(Constants.ACC_NATIVE);
}
@@ -179,6 +183,10 @@
set(Constants.ACC_STRICT);
}
+ public void unsetStrict() {
+ unset(Constants.ACC_STRICT);
+ }
+
public boolean isConstructor() {
return isSet(Constants.ACC_CONSTRUCTOR);
}
@@ -207,7 +215,7 @@
set(Constants.ACC_DECLARED_SYNCHRONIZED);
}
- private void unsetDeclaredSynchronized() {
+ public void unsetDeclaredSynchronized() {
unset(Constants.ACC_DECLARED_SYNCHRONIZED);
}
@@ -222,6 +230,24 @@
return this;
}
+ public Builder setStrict(boolean value) {
+ if (value) {
+ flags.setStrict();
+ } else {
+ flags.unsetStrict();
+ }
+ return this;
+ }
+
+ public Builder setSynchronized(boolean value) {
+ if (value) {
+ flags.setSynchronized();
+ } else {
+ flags.unsetSynchronized();
+ }
+ return this;
+ }
+
@Override
public Builder self() {
return this;
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
index f315247..196bc31 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
@@ -53,6 +53,7 @@
GraphLens graphLens,
NamingLens namingLens,
InitClassLens initClassLens,
+ LensCodeRewriterUtils lensCodeRewriter,
Collection<DexProgramClass> classes,
Collection<DexProto> protos,
Collection<DexType> types,
@@ -76,7 +77,7 @@
this.graphLens = graphLens;
this.namingLens = namingLens;
this.initClassLens = initClassLens;
- this.lensCodeRewriter = new LensCodeRewriterUtils(appView);
+ this.lensCodeRewriter = lensCodeRewriter;
timing.begin("Sort strings");
this.strings = createSortedMap(strings, DexString::compareTo, this::setFirstJumboString);
CompareToVisitor visitor =
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 09533b0..edb5e46 100644
--- a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
@@ -80,11 +80,11 @@
public abstract boolean isVirtualTarget();
/** Lookup the single target of an invoke-special on this resolution result if possible. */
- public abstract DexEncodedMethod lookupInvokeSpecialTarget(
+ public abstract DexClassAndMethod lookupInvokeSpecialTarget(
DexProgramClass context, AppInfoWithClassHierarchy appInfo);
/** Lookup the single target of an invoke-super on this resolution result if possible. */
- public abstract DexEncodedMethod lookupInvokeSuperTarget(
+ public abstract DexClassAndMethod lookupInvokeSuperTarget(
DexProgramClass context, AppInfoWithClassHierarchy appInfo);
/** Lookup the single target of an invoke-direct on this resolution result if possible. */
@@ -222,7 +222,7 @@
* and comments below for deviations due to diverging behavior on actual JVMs.
*/
@Override
- public DexEncodedMethod lookupInvokeSpecialTarget(
+ public DexClassAndMethod lookupInvokeSpecialTarget(
DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
// If the resolution is non-accessible then no target exists.
if (isAccessibleFrom(context, appInfo).isPossiblyTrue()) {
@@ -248,7 +248,7 @@
* @return The actual target for the invoke-super or {@code null} if no valid target is found.
*/
@Override
- public DexEncodedMethod lookupInvokeSuperTarget(
+ public DexClassAndMethod lookupInvokeSuperTarget(
DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
if (resolvedMethod.isInstanceInitializer()
|| (initialResolutionHolder != context
@@ -305,7 +305,7 @@
return null;
}
- private DexEncodedMethod internalInvokeSpecialOrSuper(
+ private DexClassAndMethod internalInvokeSpecialOrSuper(
DexProgramClass context,
AppInfoWithClassHierarchy appInfo,
BiPredicate<DexClass, DexClass> isSuperclass) {
@@ -342,10 +342,10 @@
}
// 1-3. Search the initial class and its supers in order for a matching instance method.
DexMethod method = getResolvedMethod().method;
- DexEncodedMethod target = null;
+ DexClassAndMethod target = null;
DexClass current = initialType;
while (current != null) {
- target = current.lookupMethod(method);
+ target = current.lookupClassMethod(method);
if (target != null) {
break;
}
@@ -353,29 +353,26 @@
}
// 4. Otherwise, it is the single maximally specific method:
if (target == null) {
- DexClassAndMethod result = appInfo.lookupMaximallySpecificMethod(initialType, method);
- if (result != null) {
- target = result.getDefinition();
- }
+ target = appInfo.lookupMaximallySpecificMethod(initialType, method);
}
if (target == null) {
return null;
}
// Linking exceptions:
// A non-instance method throws IncompatibleClassChangeError.
- if (target.isStatic()) {
+ if (target.getAccessFlags().isStatic()) {
return null;
}
// An instance initializer that is not to the symbolic reference throws NoSuchMethodError.
// It appears as if this check is also in place for non-initializer methods too.
// See NestInvokeSpecialMethodAccessWithIntermediateTest.
- if ((target.isInstanceInitializer() || target.isPrivateMethod())
+ if ((target.getDefinition().isInstanceInitializer() || target.getAccessFlags().isPrivate())
&& target.getHolderType() != symbolicReference.type) {
return null;
}
// Runtime exceptions:
// An abstract method throws AbstractMethodError.
- if (target.isAbstract()) {
+ if (target.getAccessFlags().isAbstract()) {
return null;
}
return target;
@@ -696,13 +693,13 @@
abstract static class EmptyResult extends ResolutionResult {
@Override
- public final DexEncodedMethod lookupInvokeSpecialTarget(
+ public final DexClassAndMethod lookupInvokeSpecialTarget(
DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
return null;
}
@Override
- public DexEncodedMethod lookupInvokeSuperTarget(
+ public DexClassAndMethod lookupInvokeSuperTarget(
DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
index 24a1b84..e8130ed 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -116,16 +116,29 @@
}
private MethodAccessFlags getAccessFlags() {
- // TODO(b/164998929): ensure this behaviour is correct, should probably calculate upper bound
- MethodAccessFlags flags = methods.iterator().next().getAccessFlags().copy();
- if (flags.isAbstract()
- && Iterables.any(methods, method -> !method.getAccessFlags().isAbstract())) {
- flags.unsetAbstract();
+ Iterable<MethodAccessFlags> allFlags =
+ Iterables.transform(methods, ProgramMethod::getAccessFlags);
+ MethodAccessFlags result = allFlags.iterator().next().copy();
+ assert Iterables.all(allFlags, flags -> !flags.isNative());
+ assert !result.isStrict() || Iterables.all(allFlags, MethodAccessFlags::isStrict);
+ assert !result.isSynchronized() || Iterables.all(allFlags, MethodAccessFlags::isSynchronized);
+ if (result.isAbstract() && Iterables.any(allFlags, flags -> !flags.isAbstract())) {
+ result.unsetAbstract();
}
- if (flags.isFinal() && Iterables.any(methods, method -> !method.getAccessFlags().isFinal())) {
- flags.unsetFinal();
+ if (result.isBridge() && Iterables.any(allFlags, flags -> !flags.isBridge())) {
+ result.unsetBridge();
}
- return flags;
+ if (result.isFinal() && Iterables.any(allFlags, flags -> !flags.isFinal())) {
+ result.unsetFinal();
+ }
+ if (result.isSynthetic() && Iterables.any(allFlags, flags -> !flags.isSynthetic())) {
+ result.unsetSynthetic();
+ }
+ if (result.isVarargs() && Iterables.any(allFlags, flags -> !flags.isVarargs())) {
+ result.unsetVarargs();
+ }
+ result.unsetDeclaredSynchronized();
+ return result;
}
private DexMethod getNewMethodReference() {
@@ -181,19 +194,24 @@
}
}
+ DexEncodedMethod newMethod;
if (representative.getHolder() == group.getTarget()) {
- classMethodsBuilder.addVirtualMethod(representative.getDefinition());
+ newMethod = representative.getDefinition();
} else {
// If the method is not in the target type, move it.
OptionalBool isLibraryMethodOverride =
representative.getDefinition().isLibraryMethodOverride();
- classMethodsBuilder.addVirtualMethod(
+ newMethod =
representative
.getDefinition()
.toTypeSubstitutedMethod(
newMethodReference,
- builder -> builder.setIsLibraryMethodOverrideIfKnown(isLibraryMethodOverride)));
+ builder -> builder.setIsLibraryMethodOverrideIfKnown(isLibraryMethodOverride));
}
+
+ newMethod.getAccessFlags().unsetFinal();
+
+ classMethodsBuilder.addVirtualMethod(newMethod);
}
public void merge(
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
index 5165bd9..431fdb9 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethodSignature;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.horizontalclassmerging.MergeGroup;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
import com.android.tools.r8.utils.OptionalBool;
@@ -26,17 +27,24 @@
static class MethodCharacteristics {
+ private final MethodAccessFlags accessFlags;
private final OptionalBool isLibraryMethodOverride;
- private final int visibilityOrdinal;
private MethodCharacteristics(DexEncodedMethod method) {
+ this.accessFlags =
+ MethodAccessFlags.builder()
+ .setPrivate(method.getAccessFlags().isPrivate())
+ .setProtected(method.getAccessFlags().isProtected())
+ .setPublic(method.getAccessFlags().isPublic())
+ .setStrict(method.getAccessFlags().isStrict())
+ .setSynchronized(method.getAccessFlags().isSynchronized())
+ .build();
this.isLibraryMethodOverride = method.isLibraryMethodOverride();
- this.visibilityOrdinal = method.getAccessFlags().getVisibilityOrdinal();
}
@Override
public int hashCode() {
- return (visibilityOrdinal << 2) | isLibraryMethodOverride.ordinal();
+ return (accessFlags.hashCode() << 2) | isLibraryMethodOverride.ordinal();
}
@Override
@@ -49,7 +57,7 @@
}
MethodCharacteristics characteristics = (MethodCharacteristics) obj;
return isLibraryMethodOverride == characteristics.isLibraryMethodOverride
- && visibilityOrdinal == characteristics.visibilityOrdinal;
+ && accessFlags.equals(characteristics.accessFlags);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
index 095333f..a2c8d57 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
@@ -274,6 +274,15 @@
return false;
}
+ public final boolean isClassType(DexType type) {
+ assert type.isClassType();
+ return isClassType() && asClassType().getClassType() == type;
+ }
+
+ public final boolean isStringType(DexItemFactory dexItemFactory) {
+ return isClassType(dexItemFactory.stringType);
+ }
+
public ClassTypeElement asClassType() {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/If.java b/src/main/java/com/android/tools/r8/ir/code/If.java
index 706d470..62cb5a2 100644
--- a/src/main/java/com/android/tools/r8/ir/code/If.java
+++ b/src/main/java/com/android/tools/r8/ir/code/If.java
@@ -224,6 +224,12 @@
return targetFromCondition(1);
}
+ public BasicBlock targetFromNullObject() {
+ assert isZeroTest();
+ assert inValues.get(0).outType().isObject();
+ return targetFromCondition(0);
+ }
+
public BasicBlock targetFromCondition(int cond) {
assert Integer.signum(cond) == cond;
switch (type) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 7fc0d77..e8ba6bd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -129,6 +129,10 @@
return outValue != null;
}
+ public boolean hasUnusedOutValue() {
+ return !hasOutValue() || !outValue().hasAnyUsers();
+ }
+
public Value outValue() {
return outValue;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
index 8ce0b70..0a0461d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
@@ -3,13 +3,11 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
-import static com.android.tools.r8.graph.DexEncodedMethod.asDexClassAndMethodOrNull;
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.code.InvokeSuperRange;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
@@ -114,8 +112,7 @@
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
AppInfoWithLiveness appInfo = appViewWithLiveness.appInfo();
if (appInfo.isSubtype(context.getHolderType(), getInvokedMethod().holder)) {
- DexEncodedMethod result = appInfo.lookupSuperTarget(getInvokedMethod(), context);
- return asDexClassAndMethodOrNull(result, appView);
+ return appInfo.lookupSuperTarget(getInvokedMethod(), context);
}
}
return null;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
index ae5e3f9..4bc8714 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
@@ -39,16 +39,26 @@
private final Map<DexProto, DexProto> protoFixupCache = new ConcurrentHashMap<>();
+ // Map from original call sites to rewritten call sites to lookup call-sites when writing.
+ // TODO(b/176885013): This is redundant if we canonicalize call sites.
+ private final Map<DexCallSite, DexCallSite> rewrittenCallSiteCache;
+
public LensCodeRewriterUtils(AppView<?> appView) {
+ this(appView, false);
+ }
+
+ public LensCodeRewriterUtils(AppView<?> appView, boolean enableCallSiteCaching) {
this.appView = appView;
this.definitions = appView;
this.graphLens = null;
+ this.rewrittenCallSiteCache = enableCallSiteCaching ? new ConcurrentHashMap<>() : null;
}
public LensCodeRewriterUtils(DexDefinitionSupplier definitions, GraphLens graphLens) {
this.appView = null;
this.definitions = definitions;
this.graphLens = graphLens;
+ this.rewrittenCallSiteCache = null;
}
private GraphLens graphLens() {
@@ -56,6 +66,14 @@
}
public DexCallSite rewriteCallSite(DexCallSite callSite, ProgramMethod context) {
+ if (rewrittenCallSiteCache == null) {
+ return rewriteCallSiteInternal(callSite, context);
+ }
+ return rewrittenCallSiteCache.computeIfAbsent(
+ callSite, ignored -> rewriteCallSiteInternal(callSite, context));
+ }
+
+ private DexCallSite rewriteCallSiteInternal(DexCallSite callSite, ProgramMethod context) {
DexItemFactory dexItemFactory = definitions.dexItemFactory();
DexProto newMethodProto = rewriteProto(callSite.methodProto);
DexMethodHandle newBootstrapMethod =
@@ -96,11 +114,15 @@
// MethodHandles that are not arguments to a lambda metafactory will not be desugared
// away. Therefore they could flow to a MethodHandle.invokeExact call which means that
// we cannot member rebind. We therefore keep the receiver and also pin the receiver
- // with a keep rule (see Enqueuer.registerMethodHandle).
+ // with a keep rule (see Enqueuer.traceMethodHandle).
+ // Note that the member can be repackaged or minified.
actualTarget =
definitions
.dexItemFactory()
- .createMethod(invokedMethod.holder, rewrittenTarget.proto, rewrittenTarget.name);
+ .createMethod(
+ graphLens().lookupType(invokedMethod.holder),
+ rewrittenTarget.proto,
+ rewrittenTarget.name);
newType = oldType;
if (oldType.isInvokeDirect()) {
// For an invoke direct, the rewritten target must have the same holder as the original.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index d6adc3c..40b18c9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -1312,15 +1312,20 @@
// TODO(b/174453232): Remove this after the configuration file format has bee updated
// with the "rewrite_method" section.
if (generator.method.getHolderType() == appView.dexItemFactory().objectsType) {
- // Still backport the new API level 30 methods.
+ // Still backport the new API level 30 methods and Objects.requireNonNull taking
+ // one argument.
String methodName = generator.method.getName().toString();
- if (!methodName.equals("requireNonNullElse")
+ if (!methodName.equals("requireNonNull")
+ && !methodName.equals("requireNonNullElse")
&& !methodName.equals("requireNonNullElseGet")
&& !methodName.equals("checkIndex")
&& !methodName.equals("checkFromToIndex")
&& !methodName.equals("checkFromIndexSize")) {
return;
}
+ if (methodName.equals("requireNonNull") && generator.method.getArity() != 1) {
+ return;
+ }
}
}
MethodProvider replaced = rewritable.put(generator.method, generator);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
index f5ff0b0..d19fd84 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
@@ -182,6 +183,10 @@
return null;
}
+ public DexMethod retargetMethod(DexClassAndMethod method, AppView<?> appView) {
+ return retargetMethod(method.getDefinition(), appView);
+ }
+
public Map<DexString, Map<DexType, DexType>> getRetargetCoreLibMember() {
return retargetCoreLibMember;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
index e04505d..c2b151c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexApplication.Builder;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
@@ -48,6 +49,7 @@
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.List;
@@ -179,8 +181,7 @@
synthesizedMembers
.computeIfAbsent(
newClass,
- ignore ->
- new TreeSet<>((x, y) -> x.getReference().compareTo(y.getReference())))
+ ignore -> new TreeSet<>(Comparator.comparing(DexEncodedMethod::getReference)))
.add(
new DexEncodedMethod(
retargetMethod,
@@ -279,17 +280,14 @@
// Due to emulated dispatch, we have to rewrite invoke-super differently or we end up in
// infinite loops. We do direct resolution. This is a very uncommon case.
if (invoke.isInvokeSuper() && matchesNonFinalHolderRewrite(invoke.getInvokedMethod())) {
- DexEncodedMethod dexEncodedMethod =
+ DexClassAndMethod superTarget =
appView
.appInfoForDesugaring()
.lookupSuperTarget(invoke.getInvokedMethod(), code.context());
// Final methods can be rewritten as a normal invoke.
- if (dexEncodedMethod != null && !dexEncodedMethod.isFinal()) {
+ if (superTarget != null && !superTarget.getAccessFlags().isFinal()) {
DexMethod retargetMethod =
- appView
- .options()
- .desugaredLibraryConfiguration
- .retargetMethod(dexEncodedMethod, appView);
+ appView.options().desugaredLibraryConfiguration.retargetMethod(superTarget, appView);
if (retargetMethod != null) {
iterator.replaceCurrentInstruction(
new InvokeStatic(retargetMethod, invoke.outValue(), invoke.arguments()));
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
index 0159139..eddbf6d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
@@ -99,6 +99,7 @@
// }
public class DesugaredLibraryWrapperSynthesizer {
+ public static final String WRAPPER_PACKAGE = "wrappers/";
public static final String WRAPPER_PREFIX = "$r8$wrapper$";
public static final String TYPE_WRAPPER_SUFFIX = "$-WRP";
public static final String VIVIFIED_TYPE_WRAPPER_SUFFIX = "$-V-WRP";
@@ -125,6 +126,7 @@
.options()
.desugaredLibraryConfiguration
.getSynthesizedLibraryClassesPackagePrefix()
+ + WRAPPER_PACKAGE
+ WRAPPER_PREFIX;
dexWrapperPrefixDexString = factory.createString(dexWrapperPrefixString);
this.converter = converter;
@@ -183,7 +185,7 @@
}
private DexClass generateTypeWrapper(
- ClassKind classKind, DexClass dexClass, DexType typeWrapperType) {
+ ClassKind<?> classKind, DexClass dexClass, DexType typeWrapperType) {
DexType type = dexClass.type;
DexEncodedField wrapperField = synthesizeWrappedValueEncodedField(typeWrapperType, type);
return synthesizeWrapper(
@@ -196,7 +198,7 @@
}
private DexClass generateVivifiedTypeWrapper(
- ClassKind classKind, DexClass dexClass, DexType vivifiedTypeWrapperType) {
+ ClassKind<?> classKind, DexClass dexClass, DexType vivifiedTypeWrapperType) {
DexType type = dexClass.type;
DexEncodedField wrapperField =
synthesizeWrappedValueEncodedField(vivifiedTypeWrapperType, vivifiedTypeFor(type));
@@ -210,7 +212,7 @@
}
private DexClass synthesizeWrapper(
- ClassKind classKind,
+ ClassKind<?> classKind,
DexType wrappingType,
DexClass clazz,
DexEncodedMethod[] virtualMethods,
@@ -398,7 +400,7 @@
for (DexEncodedMethod alreadyImplementedMethod : implementedMethods) {
if (alreadyImplementedMethod.method.match(virtualMethod.method)) {
alreadyAdded = true;
- continue;
+ break;
}
}
if (!alreadyAdded) {
@@ -503,7 +505,7 @@
}
private void generateWrappers(
- ClassKind classKind,
+ ClassKind<?> classKind,
Set<DexType> synthesized,
BiConsumer<DexType, DexClass> generatedCallback) {
while (synthesized.size() != typeWrappers.size() + vivifiedTypeWrappers.size()) {
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 bff68b1..47793a8 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
@@ -377,14 +377,14 @@
if (dexType == null) {
if (clazz.isInterface()
&& appView.rewritePrefix.hasRewrittenType(clazz.type, appView)) {
- DexEncodedMethod target =
+ DexClassAndMethod target =
appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, code.context());
- if (target != null && target.isDefaultMethod()) {
- DexClass holder = appView.definitionFor(target.getHolderType());
+ if (target != null && target.getDefinition().isDefaultMethod()) {
+ DexClass holder = target.getHolder();
if (holder.isLibraryClass() && holder.isInterface()) {
instructions.replaceCurrentInstruction(
new InvokeStatic(
- defaultAsMethodOfCompanionClass(target.method, factory),
+ defaultAsMethodOfCompanionClass(target.getReference(), factory),
invokeSuper.outValue(),
invokeSuper.arguments()));
}
@@ -395,37 +395,33 @@
// since it's in the emulated interface. We need to force resolution. If it resolves
// to a library method, then it needs to be rewritten.
// If it resolves to a program overrides, the invoke-super can remain.
- DexEncodedMethod dexEncodedMethod =
+ DexClassAndMethod superTarget =
appView
.appInfoForDesugaring()
.lookupSuperTarget(invokeSuper.getInvokedMethod(), code.context());
- if (dexEncodedMethod != null) {
- DexClass dexClass = appView.definitionFor(dexEncodedMethod.getHolderType());
- if (dexClass != null && dexClass.isLibraryClass()) {
- // Rewriting is required because the super invoke resolves into a missing
- // method (method is on desugared library). Find out if it needs to be
- // retarget or if it just calls a companion class method and rewrite.
- DexMethod retargetMethod =
- options.desugaredLibraryConfiguration.retargetMethod(
- dexEncodedMethod, appView);
- if (retargetMethod == null) {
- DexMethod originalCompanionMethod =
- instanceAsMethodOfCompanionClass(
- dexEncodedMethod.method, DEFAULT_METHOD_PREFIX, factory);
- DexMethod companionMethod =
- factory.createMethod(
- getCompanionClassType(dexType),
- factory.protoWithDifferentFirstParameter(
- originalCompanionMethod.proto, dexType),
- originalCompanionMethod.name);
- instructions.replaceCurrentInstruction(
- new InvokeStatic(
- companionMethod, invokeSuper.outValue(), invokeSuper.arguments()));
- } else {
- instructions.replaceCurrentInstruction(
- new InvokeStatic(
- retargetMethod, invokeSuper.outValue(), invokeSuper.arguments()));
- }
+ if (superTarget != null && superTarget.isLibraryMethod()) {
+ // Rewriting is required because the super invoke resolves into a missing
+ // method (method is on desugared library). Find out if it needs to be
+ // retarget or if it just calls a companion class method and rewrite.
+ DexMethod retargetMethod =
+ options.desugaredLibraryConfiguration.retargetMethod(superTarget, appView);
+ if (retargetMethod == null) {
+ DexMethod originalCompanionMethod =
+ instanceAsMethodOfCompanionClass(
+ superTarget.getReference(), DEFAULT_METHOD_PREFIX, factory);
+ DexMethod companionMethod =
+ factory.createMethod(
+ getCompanionClassType(dexType),
+ factory.protoWithDifferentFirstParameter(
+ originalCompanionMethod.proto, dexType),
+ originalCompanionMethod.name);
+ instructions.replaceCurrentInstruction(
+ new InvokeStatic(
+ companionMethod, invokeSuper.outValue(), invokeSuper.arguments()));
+ } else {
+ instructions.replaceCurrentInstruction(
+ new InvokeStatic(
+ retargetMethod, invokeSuper.outValue(), invokeSuper.arguments()));
}
}
}
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 6b6e4b0..eb42231 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
@@ -172,6 +172,8 @@
protected abstract boolean shouldProcessClassInNest(DexClass clazz, List<DexType> nest);
private DexProgramClass createNestAccessConstructor() {
+ // TODO(b/176900254): The class generated should be in a package that has lower change of
+ // collisions (and perhaps be unique by some hash from the nest).
return new DexProgramClass(
appView.dexItemFactory().nestConstructorType,
null,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java b/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
index 8ad22c7..b03cab4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.DescriptorUtils;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.util.Map;
import java.util.Set;
@@ -47,6 +48,8 @@
public abstract boolean isRewriting();
+ public abstract boolean shouldRewriteTypeName(String typeName);
+
public abstract void forAllRewrittenTypes(Consumer<DexType> consumer);
public static class DesugarPrefixRewritingMapper extends PrefixRewritingMapper {
@@ -54,6 +57,7 @@
private final Set<DexType> notRewritten = Sets.newConcurrentHashSet();
private final Map<DexType, DexType> rewritten = new ConcurrentHashMap<>();
private final Map<DexString, DexString> initialPrefixes;
+ private final Set<String> initialPrefixStrings;
private final DexItemFactory factory;
private final boolean l8Compilation;
@@ -62,10 +66,13 @@
this.factory = itemFactory;
this.l8Compilation = libraryCompilation;
ImmutableMap.Builder<DexString, DexString> builder = ImmutableMap.builder();
+ ImmutableSet.Builder<String> prefixStringBuilder = ImmutableSet.builder();
for (String key : prefixes.keySet()) {
+ prefixStringBuilder.add(key);
builder.put(toDescriptorPrefix(key), toDescriptorPrefix(prefixes.get(key)));
}
this.initialPrefixes = builder.build();
+ this.initialPrefixStrings = prefixStringBuilder.build();
validatePrefixes(prefixes);
}
@@ -184,6 +191,17 @@
public boolean isRewriting() {
return true;
}
+
+ @Override
+ public boolean shouldRewriteTypeName(String typeName) {
+ // TODO(b/154800164): We could use tries instead of looking-up everywhere.
+ for (DexString prefix : initialPrefixes.keySet()) {
+ if (typeName.startsWith(prefix.toString())) {
+ return true;
+ }
+ }
+ return false;
+ }
}
public static class EmptyPrefixRewritingMapper extends PrefixRewritingMapper {
@@ -202,6 +220,11 @@
}
@Override
+ public boolean shouldRewriteTypeName(String typeName) {
+ return false;
+ }
+
+ @Override
public void forAllRewrittenTypes(Consumer<DexType> consumer) {}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index d765d76..89d9eb7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -2460,13 +2460,35 @@
assert code.isConsistentSSA();
}
+ static class ControlFlowSimplificationResult {
+ private boolean anyAffectedValues;
+ private boolean anySimplifications;
+
+ private ControlFlowSimplificationResult(boolean anyAffectedValues, boolean anySimplifications) {
+ assert !anyAffectedValues || anySimplifications;
+ this.anyAffectedValues = anyAffectedValues;
+ this.anySimplifications = anySimplifications;
+ }
+
+ public boolean anyAffectedValues() {
+ return anyAffectedValues;
+ }
+
+ public boolean anySimplifications() {
+ return anySimplifications;
+ }
+ }
+
public boolean simplifyControlFlow(IRCode code) {
boolean anyAffectedValues = rewriteSwitch(code);
- anyAffectedValues |= simplifyIf(code);
+ anyAffectedValues |= simplifyIf(code).anyAffectedValues();
return anyAffectedValues;
}
- public boolean simplifyIf(IRCode code) {
+ public ControlFlowSimplificationResult simplifyIf(IRCode code) {
+ BasicBlockBehavioralSubsumption behavioralSubsumption =
+ new BasicBlockBehavioralSubsumption(appView, code);
+ boolean simplified = false;
for (BasicBlock block : code.blocks) {
// Skip removed (= unreachable) blocks.
if (block.getNumber() != 0 && block.getPredecessors().isEmpty()) {
@@ -2474,154 +2496,36 @@
}
if (block.exit().isIf()) {
flipIfBranchesIfNeeded(code, block);
- rewriteIfWithConstZero(code, block);
+ if (rewriteIfWithConstZero(code, block)) {
+ simplified = true;
+ }
if (simplifyKnownBooleanCondition(code, block)) {
- continue;
+ simplified = true;
+ if (!block.exit().isIf()) {
+ continue;
+ }
}
// Simplify if conditions when possible.
If theIf = block.exit().asIf();
- Value lhs = theIf.lhs();
- Value rhs = theIf.isZeroTest() ? null : theIf.rhs();
+ if (theIf.isZeroTest()) {
+ if (simplifyIfZeroTest(code, block, theIf)) {
+ simplified = true;
+ continue;
+ }
+ } else {
+ if (simplifyNonIfZeroTest(code, block, theIf)) {
+ simplified = true;
+ continue;
+ }
+ }
- if (lhs.isConstNumber() && (theIf.isZeroTest() || rhs.isConstNumber())) {
- // Zero test with a constant of comparison between between two constants.
- if (theIf.isZeroTest()) {
- ConstNumber cond = lhs.getConstInstruction().asConstNumber();
- BasicBlock target = theIf.targetFromCondition(cond);
- simplifyIfWithKnownCondition(code, block, theIf, target);
- } else {
- ConstNumber left = lhs.getConstInstruction().asConstNumber();
- ConstNumber right = rhs.getConstInstruction().asConstNumber();
- BasicBlock target = theIf.targetFromCondition(left, right);
- simplifyIfWithKnownCondition(code, block, theIf, target);
- }
- } else if (lhs.hasValueRange() && (theIf.isZeroTest() || rhs.hasValueRange())) {
- // Zero test with a value range, or comparison between between two values,
- // each with a value ranges.
- if (theIf.isZeroTest()) {
- LongInterval interval = lhs.getValueRange();
- if (!interval.containsValue(0)) {
- // Interval doesn't contain zero at all.
- int sign = Long.signum(interval.getMin());
- simplifyIfWithKnownCondition(code, block, theIf, sign);
- } else {
- // Interval contains zero.
- switch (theIf.getType()) {
- case GE:
- case LT:
- // [a, b] >= 0 is always true if a >= 0.
- // [a, b] < 0 is always false if a >= 0.
- // In both cases a zero condition takes the right branch.
- if (interval.getMin() == 0) {
- simplifyIfWithKnownCondition(code, block, theIf, 0);
- }
- break;
- case LE:
- case GT:
- // [a, b] <= 0 is always true if b <= 0.
- // [a, b] > 0 is always false if b <= 0.
- if (interval.getMax() == 0) {
- simplifyIfWithKnownCondition(code, block, theIf, 0);
- }
- break;
- case EQ:
- case NE:
- // Only a single element interval [0, 0] can be dealt with here.
- // Such intervals should have been replaced by constants.
- assert !interval.isSingleValue();
- break;
- }
- }
- } else {
- LongInterval leftRange = lhs.getValueRange();
- LongInterval rightRange = rhs.getValueRange();
- // Two overlapping ranges. Check for single point overlap.
- if (!leftRange.overlapsWith(rightRange)) {
- // No overlap.
- int cond = Long.signum(leftRange.getMin() - rightRange.getMin());
- simplifyIfWithKnownCondition(code, block, theIf, cond);
- } else {
- // The two intervals overlap. We can simplify if they overlap at the end points.
- switch (theIf.getType()) {
- case LT:
- case GE:
- // [a, b] < [c, d] is always false when a == d.
- // [a, b] >= [c, d] is always true when a == d.
- // In both cases 0 condition will choose the right branch.
- if (leftRange.getMin() == rightRange.getMax()) {
- simplifyIfWithKnownCondition(code, block, theIf, 0);
- }
- break;
- case GT:
- case LE:
- // [a, b] > [c, d] is always false when b == c.
- // [a, b] <= [c, d] is always true when b == c.
- // In both cases 0 condition will choose the right branch.
- if (leftRange.getMax() == rightRange.getMin()) {
- simplifyIfWithKnownCondition(code, block, theIf, 0);
- }
- break;
- case EQ:
- case NE:
- // Since there is overlap EQ and NE cannot be determined.
- break;
- }
- }
- }
- } else if (theIf.getType() == Type.EQ || theIf.getType() == Type.NE) {
- if (theIf.isZeroTest()) {
- if (!lhs.isConstNumber()) {
- TypeElement l = lhs.getType();
- if (l.isReferenceType() && lhs.isNeverNull()) {
- simplifyIfWithKnownCondition(code, block, theIf, 1);
- } else {
- if (!l.isPrimitiveType() && !l.isNullable()) {
- simplifyIfWithKnownCondition(code, block, theIf, 1);
- }
- }
- }
- } else {
- ProgramMethod context = code.context();
- AbstractValue abstractValue = lhs.getAbstractValue(appView, context);
- if (abstractValue.isSingleConstClassValue()) {
- AbstractValue otherAbstractValue = rhs.getAbstractValue(appView, context);
- if (otherAbstractValue.isSingleConstClassValue()) {
- SingleConstClassValue singleConstClassValue =
- abstractValue.asSingleConstClassValue();
- SingleConstClassValue otherSingleConstClassValue =
- otherAbstractValue.asSingleConstClassValue();
- simplifyIfWithKnownCondition(
- code,
- block,
- theIf,
- BooleanUtils.intValue(
- singleConstClassValue.getType() != otherSingleConstClassValue.getType()));
- }
- } else if (abstractValue.isSingleFieldValue()) {
- AbstractValue otherAbstractValue = rhs.getAbstractValue(appView, context);
- if (otherAbstractValue.isSingleFieldValue()) {
- SingleFieldValue singleFieldValue = abstractValue.asSingleFieldValue();
- SingleFieldValue otherSingleFieldValue = otherAbstractValue.asSingleFieldValue();
- if (singleFieldValue.getField() == otherSingleFieldValue.getField()) {
- simplifyIfWithKnownCondition(code, block, theIf, 0);
- } else {
- DexClass holder = appView.definitionForHolder(singleFieldValue.getField());
- DexEncodedField field = singleFieldValue.getField().lookupOnClass(holder);
- if (field != null && field.isEnum()) {
- DexClass otherHolder =
- appView.definitionForHolder(otherSingleFieldValue.getField());
- DexEncodedField otherField =
- otherSingleFieldValue.getField().lookupOnClass(otherHolder);
- if (otherField != null && otherField.isEnum()) {
- simplifyIfWithKnownCondition(code, block, theIf, 1);
- }
- }
- }
- }
- }
- }
+ // Unable to determine which branch will be taken. Check if the true target can safely be
+ // rewritten to the false target.
+ if (behavioralSubsumption.isSubsumedBy(theIf.getTrueTarget(), theIf.fallthroughBlock())) {
+ simplifyIfWithKnownCondition(code, block, theIf, theIf.fallthroughBlock());
+ simplified = true;
}
}
}
@@ -2630,7 +2534,193 @@
new TypeAnalysis(appView).narrowing(affectedValues);
}
assert code.isConsistentSSA();
- return !affectedValues.isEmpty();
+ return new ControlFlowSimplificationResult(!affectedValues.isEmpty(), simplified);
+ }
+
+ private boolean simplifyIfZeroTest(IRCode code, BasicBlock block, If theIf) {
+ Value lhs = theIf.lhs();
+ Value lhsRoot = lhs.getAliasedValue();
+ if (lhsRoot.isConstNumber()) {
+ ConstNumber cond = lhsRoot.getConstInstruction().asConstNumber();
+ BasicBlock target = theIf.targetFromCondition(cond);
+ simplifyIfWithKnownCondition(code, block, theIf, target);
+ return true;
+ }
+
+ if (theIf.isNullTest()) {
+ assert theIf.getType() == Type.EQ || theIf.getType() == Type.NE;
+
+ if (lhs.isAlwaysNull(appView)) {
+ simplifyIfWithKnownCondition(code, block, theIf, theIf.targetFromNullObject());
+ return true;
+ }
+
+ if (lhs.isNeverNull()) {
+ simplifyIfWithKnownCondition(code, block, theIf, theIf.targetFromNonNullObject());
+ return true;
+ }
+ }
+
+ if (lhs.hasValueRange()) {
+ LongInterval interval = lhs.getValueRange();
+ if (!interval.containsValue(0)) {
+ // Interval doesn't contain zero at all.
+ int sign = Long.signum(interval.getMin());
+ simplifyIfWithKnownCondition(code, block, theIf, sign);
+ return true;
+ }
+
+ // Interval contains zero.
+ switch (theIf.getType()) {
+ case GE:
+ case LT:
+ // [a, b] >= 0 is always true if a >= 0.
+ // [a, b] < 0 is always false if a >= 0.
+ // In both cases a zero condition takes the right branch.
+ if (interval.getMin() == 0) {
+ simplifyIfWithKnownCondition(code, block, theIf, 0);
+ return true;
+ }
+ break;
+
+ case LE:
+ case GT:
+ // [a, b] <= 0 is always true if b <= 0.
+ // [a, b] > 0 is always false if b <= 0.
+ // In both cases a zero condition takes the right branch.
+ if (interval.getMax() == 0) {
+ simplifyIfWithKnownCondition(code, block, theIf, 0);
+ return true;
+ }
+ break;
+
+ case EQ:
+ case NE:
+ // Only a single element interval [0, 0] can be dealt with here.
+ // Such intervals should have been replaced by constants.
+ assert !interval.isSingleValue();
+ break;
+ }
+ }
+ return false;
+ }
+
+ private boolean simplifyNonIfZeroTest(IRCode code, BasicBlock block, If theIf) {
+ Value lhs = theIf.lhs();
+ Value lhsRoot = lhs.getAliasedValue();
+ Value rhs = theIf.rhs();
+ Value rhsRoot = rhs.getAliasedValue();
+ if (lhsRoot == rhsRoot) {
+ // Comparing the same value.
+ simplifyIfWithKnownCondition(code, block, theIf, theIf.targetFromCondition(0));
+ return true;
+ }
+
+ if (lhsRoot.isDefinedByInstructionSatisfying(Instruction::isCreatingInstanceOrArray)
+ && rhsRoot.isDefinedByInstructionSatisfying(Instruction::isCreatingInstanceOrArray)) {
+ // Comparing two newly created objects.
+ assert theIf.getType() == Type.EQ || theIf.getType() == Type.NE;
+ simplifyIfWithKnownCondition(code, block, theIf, theIf.targetFromCondition(1));
+ return true;
+ }
+
+ if (lhsRoot.isConstNumber() && rhsRoot.isConstNumber()) {
+ // Zero test with a constant of comparison between between two constants.
+ ConstNumber left = lhsRoot.getConstInstruction().asConstNumber();
+ ConstNumber right = rhsRoot.getConstInstruction().asConstNumber();
+ BasicBlock target = theIf.targetFromCondition(left, right);
+ simplifyIfWithKnownCondition(code, block, theIf, target);
+ return true;
+ }
+
+ if (lhs.hasValueRange() && rhs.hasValueRange()) {
+ // Zero test with a value range, or comparison between between two values,
+ // each with a value ranges.
+ LongInterval leftRange = lhs.getValueRange();
+ LongInterval rightRange = rhs.getValueRange();
+ // Two overlapping ranges. Check for single point overlap.
+ if (!leftRange.overlapsWith(rightRange)) {
+ // No overlap.
+ int cond = Long.signum(leftRange.getMin() - rightRange.getMin());
+ simplifyIfWithKnownCondition(code, block, theIf, cond);
+ return true;
+ }
+
+ // The two intervals overlap. We can simplify if they overlap at the end points.
+ switch (theIf.getType()) {
+ case LT:
+ case GE:
+ // [a, b] < [c, d] is always false when a == d.
+ // [a, b] >= [c, d] is always true when a == d.
+ // In both cases 0 condition will choose the right branch.
+ if (leftRange.getMin() == rightRange.getMax()) {
+ simplifyIfWithKnownCondition(code, block, theIf, 0);
+ return true;
+ }
+ break;
+ case GT:
+ case LE:
+ // [a, b] > [c, d] is always false when b == c.
+ // [a, b] <= [c, d] is always true when b == c.
+ // In both cases 0 condition will choose the right branch.
+ if (leftRange.getMax() == rightRange.getMin()) {
+ simplifyIfWithKnownCondition(code, block, theIf, 0);
+ return true;
+ }
+ break;
+ case EQ:
+ case NE:
+ // Since there is overlap EQ and NE cannot be determined.
+ break;
+ }
+ }
+
+ if (theIf.getType() == Type.EQ || theIf.getType() == Type.NE) {
+ ProgramMethod context = code.context();
+ AbstractValue abstractValue = lhs.getAbstractValue(appView, context);
+ if (abstractValue.isSingleConstClassValue()) {
+ AbstractValue otherAbstractValue = rhs.getAbstractValue(appView, context);
+ if (otherAbstractValue.isSingleConstClassValue()) {
+ SingleConstClassValue singleConstClassValue = abstractValue.asSingleConstClassValue();
+ SingleConstClassValue otherSingleConstClassValue =
+ otherAbstractValue.asSingleConstClassValue();
+ simplifyIfWithKnownCondition(
+ code,
+ block,
+ theIf,
+ BooleanUtils.intValue(
+ singleConstClassValue.getType() != otherSingleConstClassValue.getType()));
+ return true;
+ }
+ return false;
+ }
+
+ if (abstractValue.isSingleFieldValue()) {
+ AbstractValue otherAbstractValue = rhs.getAbstractValue(appView, context);
+ if (otherAbstractValue.isSingleFieldValue()) {
+ SingleFieldValue singleFieldValue = abstractValue.asSingleFieldValue();
+ SingleFieldValue otherSingleFieldValue = otherAbstractValue.asSingleFieldValue();
+ if (singleFieldValue.getField() == otherSingleFieldValue.getField()) {
+ simplifyIfWithKnownCondition(code, block, theIf, 0);
+ return true;
+ }
+
+ DexClass holder = appView.definitionForHolder(singleFieldValue.getField());
+ DexEncodedField field = singleFieldValue.getField().lookupOnClass(holder);
+ if (field != null && field.isEnum()) {
+ DexClass otherHolder = appView.definitionForHolder(otherSingleFieldValue.getField());
+ DexEncodedField otherField =
+ otherSingleFieldValue.getField().lookupOnClass(otherHolder);
+ if (otherField != null && otherField.isEnum()) {
+ simplifyIfWithKnownCondition(code, block, theIf, 1);
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
}
private void simplifyIfWithKnownCondition(
@@ -3069,6 +3159,7 @@
rewriteIfToGoto(code, block, theIf, trueBlock, falseBlock);
return true;
}
+ return deadPhis > 0;
}
}
}
@@ -3154,30 +3245,31 @@
assert block.exit().asGoto().getTarget() == target;
}
- private void rewriteIfWithConstZero(IRCode code, BasicBlock block) {
+ private boolean rewriteIfWithConstZero(IRCode code, BasicBlock block) {
If theIf = block.exit().asIf();
if (theIf.isZeroTest()) {
- return;
+ return false;
}
- List<Value> inValues = theIf.inValues();
- Value leftValue = inValues.get(0);
- Value rightValue = inValues.get(1);
+ Value leftValue = theIf.lhs();
+ Value rightValue = theIf.rhs();
if (leftValue.isConstNumber() || rightValue.isConstNumber()) {
if (leftValue.isConstNumber()) {
if (leftValue.getConstInstruction().asConstNumber().isZero()) {
If ifz = new If(theIf.getType().forSwappedOperands(), rightValue);
block.replaceLastInstruction(ifz, code);
assert block.exit() == ifz;
+ return true;
}
- } else {
- if (rightValue.getConstInstruction().asConstNumber().isZero()) {
- If ifz = new If(theIf.getType(), leftValue);
- block.replaceLastInstruction(ifz, code);
- assert block.exit() == ifz;
- }
+ } else if (rightValue.getConstInstruction().asConstNumber().isZero()) {
+ If ifz = new If(theIf.getType(), leftValue);
+ block.replaceLastInstruction(ifz, code);
+ assert block.exit() == ifz;
+ return true;
}
}
+
+ return false;
}
private boolean flipIfBranchesIfNeeded(IRCode code, BasicBlock block) {
@@ -3709,7 +3801,7 @@
&& value.definition.asNumberConversion().to == NumericType.DOUBLE) {
InvokeStatic invokeIsNaN =
new InvokeStatic(
- dexItemFactory.doubleMethods.isNaN, null, ImmutableList.of(value));
+ dexItemFactory.doubleMembers.isNaN, null, ImmutableList.of(value));
invokeIsNaN.setPosition(instruction.getPosition());
// Insert the invoke before the current instruction.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
index 26aa681..0ecfc91 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
@@ -53,7 +53,8 @@
removeDeadInstructions(worklist, code, block);
removeDeadPhis(worklist, code, block);
}
- } while (removeUnneededCatchHandlers(code));
+ } while (codeRewriter.simplifyIf(code).anySimplifications()
+ || removeUnneededCatchHandlers(code));
assert code.isConsistentSSA();
timing.end();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index 4534c9b..79f36ad 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -439,7 +439,10 @@
@Override
public boolean canInlineInstanceInitializer(
- IRCode inlinee, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
+ IRCode code,
+ IRCode inlinee,
+ InvokeDirect invoke,
+ WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
// In the Java VM Specification section "4.10.2.4. Instance Initialization Methods and
// Newly Created Objects" it says:
//
@@ -451,9 +454,12 @@
// is expected to adhere to the VM specification.
DexType callerMethodHolder = method.getHolderType();
DexType calleeMethodHolder = inlinee.method().getHolderType();
- // Calling a constructor on the same class from a constructor can always be inlined.
+
+ // Forwarding constructor calls that target a constructor in the same class can always be
+ // inlined.
if (method.getDefinition().isInstanceInitializer()
- && callerMethodHolder == calleeMethodHolder) {
+ && callerMethodHolder == calleeMethodHolder
+ && invoke.getReceiver() == code.getThis()) {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
index f4232a2..674488f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
@@ -310,24 +309,19 @@
/** This rebinds invoke-super instructions to their most specific target. */
private DexClassAndMethod rebindSuperInvokeToMostSpecific(
DexMethod target, ProgramMethod context) {
- DexEncodedMethod definition = appView.appInfo().lookupSuperTarget(target, context);
- if (definition == null) {
+ DexClassAndMethod method = appView.appInfo().lookupSuperTarget(target, context);
+ if (method == null) {
return null;
}
- DexClass holder = appView.definitionFor(definition.getHolderType());
- if (holder == null) {
- assert false;
- return null;
- }
-
- if (holder.isInterface() && holder.getType() != context.getHolder().superType) {
+ if (method.getHolder().isInterface()
+ && method.getHolderType() != context.getHolder().superType) {
// Not allowed.
return null;
}
- DexClassAndMethod method = DexClassAndMethod.create(holder, definition);
- if (AccessControl.isMemberAccessible(method, holder, context, appView).isPossiblyFalse()) {
+ if (AccessControl.isMemberAccessible(method, method.getHolder(), context, appView)
+ .isPossiblyFalse()) {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
index eea6b7c..c25755c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
import com.android.tools.r8.ir.optimize.Inliner.InlineeWithReason;
@@ -107,7 +108,10 @@
@Override
public boolean canInlineInstanceInitializer(
- IRCode code, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
+ IRCode code,
+ IRCode inlinee,
+ InvokeDirect invoke,
+ WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 96c9234..8f510fb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -1049,7 +1049,7 @@
assert !singleTargetMethod.isClassInitializer();
if (singleTargetMethod.isInstanceInitializer()
&& !strategy.canInlineInstanceInitializer(
- inlinee.code, whyAreYouNotInliningReporter)) {
+ code, inlinee.code, invoke.asInvokeDirect(), whyAreYouNotInliningReporter)) {
assert whyAreYouNotInliningReporter.unsetReasonHasBeenReportedFlag();
continue;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java
index cda0a6b..1dc076f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
import com.android.tools.r8.ir.optimize.Inliner.InlineeWithReason;
@@ -22,7 +23,10 @@
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter);
boolean canInlineInstanceInitializer(
- IRCode code, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter);
+ IRCode code,
+ IRCode inlinee,
+ InvokeDirect invoke,
+ WhyAreYouNotInliningReporter whyAreYouNotInliningReporter);
/** Return true if there is still budget for inlining into this method. */
boolean stillHasBudget(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
index ed477a6..e62b2c1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -122,7 +122,7 @@
@Override
public void methodMayNotHaveSideEffects(DexEncodedMethod method) {
- // Ignored.
+ method.getMutableOptimizationInfo().markMayNotHaveSideEffects();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
index 25916f9..01c4cf7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
@@ -135,6 +135,13 @@
continue;
}
+ if (invoke.hasUnusedOutValue()
+ && !singleTarget.getDefinition().isInstanceInitializer()
+ && !invoke.instructionMayHaveSideEffects(appView, code.context())) {
+ instructionIterator.removeOrReplaceByDebugLocalRead();
+ continue;
+ }
+
LibraryMethodModelCollection.State optimizationState =
optimizationStates.computeIfAbsent(
optimizer,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodSideEffectModelCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodSideEffectModelCollection.java
index 70ace2b..b1890af 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodSideEffectModelCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodSideEffectModelCollection.java
@@ -4,41 +4,41 @@
package com.android.tools.r8.ir.optimize.library;
-import static com.google.common.base.Predicates.alwaysFalse;
-import static com.google.common.base.Predicates.alwaysTrue;
-
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.LibraryMethod;
import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.BiPredicateUtils;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.function.Predicate;
+import java.util.function.BiPredicate;
+import java.util.function.Consumer;
public class LibraryMethodSideEffectModelCollection {
- private final Map<DexMethod, Predicate<InvokeMethod>> finalMethodsWithoutSideEffects;
+ private final Map<DexMethod, BiPredicate<DexMethod, List<Value>>> finalMethodsWithoutSideEffects;
+ private final Set<DexMethod> unconditionalFinalMethodsWithoutSideEffects;
+
private final Set<DexMethod> nonFinalMethodsWithoutSideEffects;
public LibraryMethodSideEffectModelCollection(DexItemFactory dexItemFactory) {
finalMethodsWithoutSideEffects = buildFinalMethodsWithoutSideEffects(dexItemFactory);
+ unconditionalFinalMethodsWithoutSideEffects =
+ buildUnconditionalFinalMethodsWithoutSideEffects(dexItemFactory);
nonFinalMethodsWithoutSideEffects = buildNonFinalMethodsWithoutSideEffects(dexItemFactory);
}
- private static Map<DexMethod, Predicate<InvokeMethod>> buildFinalMethodsWithoutSideEffects(
- DexItemFactory dexItemFactory) {
- ImmutableMap.Builder<DexMethod, Predicate<InvokeMethod>> builder =
- ImmutableMap.<DexMethod, Predicate<InvokeMethod>>builder()
- .put(dexItemFactory.enumMembers.constructor, alwaysTrue())
- .put(dexItemFactory.npeMethods.init, alwaysTrue())
- .put(dexItemFactory.npeMethods.initWithMessage, alwaysTrue())
- .put(dexItemFactory.objectMembers.constructor, alwaysTrue())
- .put(dexItemFactory.objectMembers.getClass, alwaysTrue())
- .put(dexItemFactory.stringBuilderMethods.toString, alwaysTrue())
- .put(dexItemFactory.stringMembers.hashCode, alwaysTrue());
- putAll(builder, dexItemFactory.classMethods.getNames, alwaysTrue());
+ private static Map<DexMethod, BiPredicate<DexMethod, List<Value>>>
+ buildFinalMethodsWithoutSideEffects(DexItemFactory dexItemFactory) {
+ ImmutableMap.Builder<DexMethod, BiPredicate<DexMethod, List<Value>>> builder =
+ ImmutableMap.<DexMethod, BiPredicate<DexMethod, List<Value>>>builder()
+ .put(
+ dexItemFactory.stringMembers.constructor,
+ (method, arguments) -> arguments.get(1).isNeverNull());
putAll(
builder,
dexItemFactory.stringBufferMethods.constructorMethods,
@@ -47,10 +47,34 @@
builder,
dexItemFactory.stringBuilderMethods.constructorMethods,
dexItemFactory.stringBuilderMethods::constructorInvokeIsSideEffectFree);
- putAll(builder, dexItemFactory.boxedValueOfMethods(), alwaysTrue());
return builder.build();
}
+ private static Set<DexMethod> buildUnconditionalFinalMethodsWithoutSideEffects(
+ DexItemFactory dexItemFactory) {
+ return ImmutableSet.<DexMethod>builder()
+ .add(dexItemFactory.booleanMembers.toString)
+ .add(dexItemFactory.byteMembers.toString)
+ .add(dexItemFactory.charMembers.toString)
+ .add(dexItemFactory.doubleMembers.toString)
+ .add(dexItemFactory.enumMembers.constructor)
+ .add(dexItemFactory.floatMembers.toString)
+ .add(dexItemFactory.integerMembers.toString)
+ .add(dexItemFactory.longMembers.toString)
+ .add(dexItemFactory.npeMethods.init)
+ .add(dexItemFactory.npeMethods.initWithMessage)
+ .add(dexItemFactory.objectMembers.constructor)
+ .add(dexItemFactory.objectMembers.getClass)
+ .add(dexItemFactory.shortMembers.toString)
+ .add(dexItemFactory.stringBufferMethods.toString)
+ .add(dexItemFactory.stringBuilderMethods.toString)
+ .add(dexItemFactory.stringMembers.hashCode)
+ .add(dexItemFactory.stringMembers.toString)
+ .addAll(dexItemFactory.classMethods.getNames)
+ .addAll(dexItemFactory.boxedValueOfMethods())
+ .build();
+ }
+
private static Set<DexMethod> buildNonFinalMethodsWithoutSideEffects(
DexItemFactory dexItemFactory) {
return ImmutableSet.of(
@@ -59,25 +83,31 @@
dexItemFactory.objectMembers.toString);
}
- private static void putAll(
- ImmutableMap.Builder<DexMethod, Predicate<InvokeMethod>> builder,
- Iterable<DexMethod> methods,
- Predicate<InvokeMethod> predicate) {
- for (DexMethod method : methods) {
- builder.put(method, predicate);
+ private static <K, V> void putAll(ImmutableMap.Builder<K, V> builder, Iterable<K> keys, V value) {
+ for (K key : keys) {
+ builder.put(key, value);
}
}
+ public void forEachSideEffectFreeFinalMethod(Consumer<DexMethod> consumer) {
+ unconditionalFinalMethodsWithoutSideEffects.forEach(consumer);
+ }
+
public boolean isCallToSideEffectFreeFinalMethod(InvokeMethod invoke) {
- return finalMethodsWithoutSideEffects
- .getOrDefault(invoke.getInvokedMethod(), alwaysFalse())
- .test(invoke);
+ return isSideEffectFreeFinalMethod(invoke.getInvokedMethod(), invoke.arguments());
+ }
+
+ public boolean isSideEffectFreeFinalMethod(DexMethod method, List<Value> arguments) {
+ return unconditionalFinalMethodsWithoutSideEffects.contains(method)
+ || finalMethodsWithoutSideEffects
+ .getOrDefault(method, BiPredicateUtils.alwaysFalse())
+ .test(method, arguments);
}
// This intentionally takes the invoke instruction since the determination of whether a library
// method has side effects may depend on the arguments.
public boolean isSideEffectFree(InvokeMethod invoke, LibraryMethod singleTarget) {
- return isCallToSideEffectFreeFinalMethod(invoke)
+ return isSideEffectFreeFinalMethod(singleTarget.getReference(), invoke.arguments())
|| nonFinalMethodsWithoutSideEffects.contains(singleTarget.getReference());
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
index 9b41f18..ecbb3e4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
@@ -47,6 +47,7 @@
modelStaticFinalLibraryFields(finalLibraryFields);
modelLibraryMethodsReturningNonNull();
modelLibraryMethodsReturningReceiver();
+ modelLibraryMethodsWithoutSideEffects();
modelRequireNonNullMethods();
}
@@ -113,6 +114,18 @@
}
}
+ private void modelLibraryMethodsWithoutSideEffects() {
+ appView
+ .getLibraryMethodSideEffectModelCollection()
+ .forEachSideEffectFreeFinalMethod(
+ method -> {
+ DexEncodedMethod definition = lookupMethod(method);
+ if (definition != null) {
+ feedback.methodMayNotHaveSideEffects(definition);
+ }
+ });
+ }
+
private void modelRequireNonNullMethods() {
for (DexMethod requireNonNullMethod : dexItemFactory.objectsMethods.requireNonNullMethods()) {
DexEncodedMethod definition = lookupMethod(requireNonNullMethod);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java
index 9d0c8c8..a303082 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java
@@ -5,14 +5,19 @@
package com.android.tools.r8.ir.optimize.library;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexItemFactory.ObjectsMethods;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.Set;
public class ObjectsMethodOptimizer extends StatelessLibraryMethodModelCollection {
@@ -67,11 +72,62 @@
InvokeMethod invoke,
Set<Value> affectedValues) {
Value object = invoke.getFirstArgument();
- if (object.getType().isDefinitelyNull()) {
+ TypeElement type = object.getType();
+
+ // Optimize Objects.toString(null) into "null".
+ if (type.isDefinitelyNull()) {
instructionIterator.replaceCurrentInstructionWithConstString(appView, code, "null");
if (invoke.hasOutValue()) {
affectedValues.addAll(invoke.outValue().affectedValues());
}
+ return;
+ }
+
+ // Optimize Objects.toString(nonNullString) into nonNullString.
+ if (type.isDefinitelyNotNull() && type.isStringType(dexItemFactory)) {
+ if (invoke.hasOutValue()) {
+ affectedValues.addAll(invoke.outValue().affectedValues());
+ invoke.outValue().replaceUsers(object);
+ }
+ instructionIterator.removeOrReplaceByDebugLocalRead();
+ return;
+ }
+
+ // Remove Objects.toString() if it is unused and does not have side effects.
+ if (!invoke.hasOutValue() || !invoke.outValue().hasNonDebugUsers()) {
+ // Calling toString() on an array does not call toString() on the array elements.
+ if (type.isArrayType()) {
+ instructionIterator.removeOrReplaceByDebugLocalRead();
+ return;
+ }
+
+ assert type.isClassType();
+
+ // Check if this is a library class with a toString() method that does not have side effects.
+ DexType classType = type.asClassType().getClassType();
+ DexMethod toStringMethodReference =
+ dexItemFactory.objectMembers.toString.withHolder(classType, dexItemFactory);
+ if (appView
+ .getLibraryMethodSideEffectModelCollection()
+ .isSideEffectFreeFinalMethod(toStringMethodReference, invoke.arguments())) {
+ instructionIterator.removeOrReplaceByDebugLocalRead();
+ return;
+ }
+
+ // Check if this is a program class with a toString() method that does not have side effects.
+ AppInfoWithLiveness appInfo = appView.appInfo().withLiveness();
+ if (appInfo != null) {
+ DexClass clazz = appInfo.definitionFor(classType, code.context());
+ if (clazz != null && clazz.isEffectivelyFinal(appView)) {
+ SingleResolutionResult resolutionResult =
+ appInfo.resolveMethodOn(clazz, toStringMethodReference).asSingleResolution();
+ if (resolutionResult != null
+ && !resolutionResult.getResolvedMethod().getOptimizationInfo().mayHaveSideEffects()) {
+ instructionIterator.removeOrReplaceByDebugLocalRead();
+ return;
+ }
+ }
+ }
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/StringBuilderMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/StringBuilderMethodOptimizer.java
index 9cccb5d..99b1bda 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/StringBuilderMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/StringBuilderMethodOptimizer.java
@@ -4,7 +4,10 @@
package com.android.tools.r8.ir.optimize.library;
+import static com.android.tools.r8.ir.code.Opcodes.ASSUME;
+import static com.android.tools.r8.ir.code.Opcodes.IF;
import static com.android.tools.r8.ir.code.Opcodes.INVOKE_DIRECT;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_STATIC;
import static com.android.tools.r8.ir.code.Opcodes.INVOKE_VIRTUAL;
import static com.android.tools.r8.ir.code.Opcodes.NEW_INSTANCE;
@@ -29,6 +32,7 @@
import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations.UtilityMethodForCodeOptimizations;
import com.android.tools.r8.ir.optimize.library.StringBuilderMethodOptimizer.State;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ValueUtils;
import com.android.tools.r8.utils.WorkList;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.objects.Reference2BooleanMap;
@@ -73,6 +77,8 @@
InvokeMethodWithReceiver invokeWithReceiver = invoke.asInvokeMethodWithReceiver();
if (stringBuilderMethods.isAppendMethod(singleTarget.getReference())) {
optimizeAppend(code, instructionIterator, invokeWithReceiver, singleTarget, state);
+ } else if (singleTarget.getReference() == dexItemFactory.stringBuilderMethods.toString) {
+ optimizeToString(instructionIterator, invokeWithReceiver);
}
}
}
@@ -130,6 +136,16 @@
}
}
+ private void optimizeToString(
+ InstructionListIterator instructionIterator, InvokeMethodWithReceiver invoke) {
+ // Optimize StringBuilder.toString() if unused.
+ if (ValueUtils.isNonNullStringBuilder(invoke.getReceiver(), dexItemFactory)) {
+ if (!invoke.hasOutValue() || !invoke.outValue().hasNonDebugUsers()) {
+ instructionIterator.removeOrReplaceByDebugLocalRead();
+ }
+ }
+ }
+
class State implements LibraryMethodModelCollection.State {
final MethodProcessor methodProcessor;
@@ -185,6 +201,10 @@
Instruction definition = alias.definition;
switch (definition.opcode()) {
+ case ASSUME:
+ worklist.addIfNotSeen(definition.inValues());
+ break;
+
case NEW_INSTANCE:
assert definition.asNewInstance().clazz == dexItemFactory.stringBuilderType;
break;
@@ -208,6 +228,14 @@
// Analyze all users.
for (Instruction user : alias.uniqueUsers()) {
switch (user.opcode()) {
+ case ASSUME:
+ worklist.addIfNotSeen(user.outValue());
+ break;
+
+ case IF:
+ // StringBuilder null check.
+ break;
+
case INVOKE_DIRECT:
{
InvokeDirect invoke = user.asInvokeDirect();
@@ -224,6 +252,25 @@
}
break;
+ case INVOKE_STATIC:
+ {
+ InvokeStatic invoke = user.asInvokeStatic();
+ DexMethod invokedMethod = invoke.getInvokedMethod();
+
+ // Allow calls to Objects.toString(Object) and String.valueOf(Object).
+ if (invokedMethod == dexItemFactory.objectsMethods.toStringWithObject
+ || invokedMethod == dexItemFactory.stringMembers.valueOf) {
+ // Only allow unused StringBuilders.
+ if (invoke.hasOutValue() && invoke.outValue().hasNonDebugUsers()) {
+ return false;
+ }
+ break;
+ }
+
+ // Invoke to unhandled method, give up.
+ return false;
+ }
+
case INVOKE_VIRTUAL:
{
InvokeVirtual invoke = user.asInvokeVirtual();
@@ -245,7 +292,8 @@
}
// Allow calls to toString().
- if (invokedMethod == stringBuilderMethods.toString) {
+ if (invokedMethod == dexItemFactory.objectMembers.toString
+ || invokedMethod == stringBuilderMethods.toString) {
// Only allow unused StringBuilders.
if (invoke.hasOutValue() && invoke.outValue().hasNonDebugUsers()) {
return false;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java
index c8d04ae..a8fabe3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
@@ -15,7 +16,10 @@
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.ValueUtils;
import java.util.Set;
public class StringMethodOptimizer extends StatelessLibraryMethodModelCollection {
@@ -40,17 +44,20 @@
InvokeMethod invoke,
DexClassAndMethod singleTarget,
Set<Value> affectedValues) {
- if (singleTarget.getReference() == dexItemFactory.stringMembers.equals) {
- optimizeEquals(code, instructionIterator, invoke);
+ DexMethod singleTargetReference = singleTarget.getReference();
+ if (singleTargetReference == dexItemFactory.stringMembers.equals) {
+ optimizeEquals(code, instructionIterator, invoke.asInvokeVirtual());
+ } else if (singleTargetReference == dexItemFactory.stringMembers.valueOf) {
+ optimizeValueOf(instructionIterator, invoke.asInvokeStatic());
}
}
private void optimizeEquals(
- IRCode code, InstructionListIterator instructionIterator, InvokeMethod invoke) {
+ IRCode code, InstructionListIterator instructionIterator, InvokeVirtual invoke) {
if (appView.appInfo().hasLiveness()) {
ProgramMethod context = code.context();
- Value first = invoke.arguments().get(0).getAliasedValue();
- Value second = invoke.arguments().get(1).getAliasedValue();
+ Value first = invoke.getReceiver().getAliasedValue();
+ Value second = invoke.getArgument(1).getAliasedValue();
if (isPrunedClassNameComparison(first, second, context)
|| isPrunedClassNameComparison(second, first, context)) {
instructionIterator.replaceCurrentInstructionWithConstInt(code, 0);
@@ -58,6 +65,15 @@
}
}
+ private void optimizeValueOf(InstructionListIterator instructionIterator, InvokeStatic invoke) {
+ // Optimize String.valueOf(stringBuilder) if unused.
+ if (ValueUtils.isNonNullStringBuilder(invoke.getFirstArgument(), dexItemFactory)) {
+ if (!invoke.hasOutValue() || !invoke.outValue().hasNonDebugUsers()) {
+ instructionIterator.removeOrReplaceByDebugLocalRead();
+ }
+ }
+ }
+
/**
* Returns true if {@param classNameValue} is defined by calling {@link Class#getName()} and
* {@param constStringValue} is a constant string that is identical to the name of a class that
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
index a61662c..3864fb8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
@@ -23,7 +23,9 @@
import com.android.tools.r8.ir.code.ConstString;
import com.android.tools.r8.ir.code.DominatorTree;
import com.android.tools.r8.ir.code.DominatorTree.Assumption;
+import com.android.tools.r8.ir.code.Goto;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeDirect;
@@ -35,6 +37,7 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
@@ -731,13 +734,34 @@
if (deadBuilders.isEmpty() && simplifiedBuilders.isEmpty()) {
return;
}
- Set<Value> buildersToRemove = Sets.union(deadBuilders, simplifiedBuilders);
+ Set<Value> affectedValues = Sets.newIdentityHashSet();
+ Set<Value> buildersToRemove = SetUtils.newIdentityHashSet(deadBuilders, simplifiedBuilders);
+ Set<Value> buildersUsedInIf = Sets.newIdentityHashSet();
// All instructions that refer to dead/simplified builders are dead.
// Here, we remove toString() calls, append(...) calls, <init>, and new-instance in order.
InstructionListIterator it = code.instructionListIterator();
+ boolean shouldRemoveUnreachableBlocks = false;
while (it.hasNext()) {
Instruction instr = it.next();
- if (instr.isInvokeMethod()) {
+ if (instr.isIf()) {
+ If theIf = instr.asIf();
+ Value lhs = theIf.lhs().getAliasedValue();
+ if (theIf.isZeroTest()) {
+ if (buildersToRemove.contains(lhs)) {
+ theIf.targetFromNullObject().unlinkSinglePredecessorSiblingsAllowed();
+ it.replaceCurrentInstruction(new Goto());
+ shouldRemoveUnreachableBlocks = true;
+ }
+ } else {
+ Value rhs = theIf.rhs().getAliasedValue();
+ if (buildersToRemove.contains(lhs)) {
+ buildersUsedInIf.add(lhs);
+ }
+ if (buildersToRemove.contains(rhs)) {
+ buildersUsedInIf.add(rhs);
+ }
+ }
+ } else if (instr.isInvokeMethod()) {
InvokeMethod invoke = instr.asInvokeMethod();
DexMethod invokedMethod = invoke.getInvokedMethod();
if (optimizationConfiguration.isToStringMethod(invokedMethod)
@@ -746,6 +770,10 @@
}
}
}
+ if (shouldRemoveUnreachableBlocks) {
+ affectedValues.addAll(code.removeUnreachableBlocks());
+ }
+ buildersToRemove.removeAll(buildersUsedInIf);
// append(...) and <init> don't have out values, so removing them won't bother each other.
it = code.instructionListIterator();
while (it.hasNext()) {
@@ -787,6 +815,9 @@
it.removeOrReplaceByDebugLocalRead();
}
}
+ if (!affectedValues.isEmpty()) {
+ new TypeAnalysis(appView).narrowing(affectedValues);
+ }
assert code.isConsistentSSA();
}
}
@@ -852,7 +883,8 @@
@Override
public boolean isToStringMethod(DexMethod method) {
- return method == factory.stringBuilderMethods.toString
+ return method == factory.objectMembers.toString
+ || method == factory.stringBuilderMethods.toString
|| method == factory.stringBufferMethods.toString
|| method == factory.stringMembers.valueOf;
}
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
index b9fc4cb..a775a72 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
@@ -16,7 +16,6 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexItemBasedValueString;
import com.android.tools.r8.graph.DexValue.DexValueString;
@@ -25,7 +24,6 @@
import com.android.tools.r8.utils.ThreadUtils;
import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -104,21 +102,10 @@
}
private DexString getRenamedStringLiteral(DexString originalLiteral) {
- String originalString = originalLiteral.toString();
- Map<String, DexType> renamedYetMatchedTypes =
- lens.getRenamedItems(
- DexType.class,
- type -> type.toSourceString().equals(originalString),
- DexType::toSourceString);
- DexType type = renamedYetMatchedTypes.get(originalString);
- if (type != null) {
- DexString renamed = lens.lookupDescriptor(type);
- // Create a new DexString only when the corresponding string literal will be replaced.
- if (renamed != originalLiteral) {
- return appView.dexItemFactory().createString(descriptorToJavaType(renamed.toString()));
- }
- }
- return originalLiteral;
+ DexString rewrittenString = lens.lookupDescriptorForJavaTypeName(originalLiteral.toString());
+ return rewrittenString == null
+ ? originalLiteral
+ : appView.dexItemFactory().createString(descriptorToJavaType(rewrittenString.toString()));
}
private void replaceDexItemBasedConstString(ExecutorService executorService)
diff --git a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
index b7bfcdb..eb4e9ca 100644
--- a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
@@ -20,13 +20,10 @@
import com.android.tools.r8.naming.NamingLens.NonIdentityNamingLens;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.collect.ImmutableMap;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
-import java.util.function.Function;
-import java.util.function.Predicate;
class MinifiedRenaming extends NonIdentityNamingLens {
@@ -39,7 +36,7 @@
ClassRenaming classRenaming,
MethodRenaming methodRenaming,
FieldRenaming fieldRenaming) {
- super(appView.dexItemFactory());
+ super(appView.dexItemFactory(), classRenaming.classRenaming);
this.appView = appView;
this.packageRenaming = classRenaming.packageRenaming;
renaming.putAll(classRenaming.classRenaming);
@@ -123,15 +120,6 @@
return renaming.getOrDefault(field, field.name);
}
- @Override
- public <T extends DexItem> Map<String, T> getRenamedItems(
- Class<T> clazz, Predicate<T> predicate, Function<T, String> namer) {
- return renaming.keySet().stream()
- .filter(item -> (clazz.isInstance(item) && predicate.test(clazz.cast(item))))
- .map(clazz::cast)
- .collect(ImmutableMap.toImmutableMap(namer, i -> i));
- }
-
/**
* Checks that the renaming of the method reference {@param method} is consistent with the
* renaming of the resolution target of {@param method}.
diff --git a/src/main/java/com/android/tools/r8/naming/NamingLens.java b/src/main/java/com/android/tools/r8/naming/NamingLens.java
index 9147b84..bdd0e70 100644
--- a/src/main/java/com/android/tools/r8/naming/NamingLens.java
+++ b/src/main/java/com/android/tools/r8/naming/NamingLens.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
@@ -20,13 +19,11 @@
import com.android.tools.r8.optimize.MemberRebindingAnalysis;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.Map;
import java.util.Set;
-import java.util.function.Function;
-import java.util.function.Predicate;
/**
* Implements a translation of the Dex graph from original names to new names produced by the {@link
@@ -45,6 +42,8 @@
public abstract DexString lookupDescriptor(DexType type);
+ public abstract DexString lookupDescriptorForJavaTypeName(String typeName);
+
public DexString lookupClassDescriptor(DexType type) {
assert type.isClassType();
return internalLookupClassDescriptor(type);
@@ -147,9 +146,6 @@
return DescriptorUtils.descriptorToInternalName(lookupDescriptor(type).toString());
}
- public abstract <T extends DexItem> Map<String, T> getRenamedItems(
- Class<T> clazz, Predicate<T> predicate, Function<T, String> namer);
-
/**
* Checks whether the target will be translated properly by this lens.
*
@@ -189,9 +185,13 @@
public abstract static class NonIdentityNamingLens extends NamingLens {
private final DexItemFactory dexItemFactory;
+ private final Map<String, DexString> typeStringMapping;
- protected NonIdentityNamingLens(DexItemFactory dexItemFactory) {
+ protected NonIdentityNamingLens(
+ DexItemFactory dexItemFactory, Map<DexType, DexString> typeMapping) {
this.dexItemFactory = dexItemFactory;
+ typeStringMapping = new HashMap<>();
+ typeMapping.forEach((k, v) -> typeStringMapping.put(k.toSourceString(), v));
}
protected DexItemFactory dexItemFactory() {
@@ -211,6 +211,11 @@
assert type.isClassType();
return lookupClassDescriptor(type);
}
+
+ @Override
+ public DexString lookupDescriptorForJavaTypeName(String typeName) {
+ return typeStringMapping.get(typeName);
+ }
}
private static final class IdentityLens extends NamingLens {
@@ -225,6 +230,11 @@
}
@Override
+ public DexString lookupDescriptorForJavaTypeName(String typeName) {
+ return null;
+ }
+
+ @Override
protected DexString internalLookupClassDescriptor(DexType type) {
return type.descriptor;
}
@@ -250,12 +260,6 @@
}
@Override
- public <T extends DexItem> Map<String, T> getRenamedItems(
- Class<T> clazz, Predicate<T> predicate, Function<T, String> namer) {
- return ImmutableMap.of();
- }
-
- @Override
public boolean verifyRenamingConsistentWithResolution(DexMethod item) {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java b/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
index e987def..04f235f 100644
--- a/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
+++ b/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
@@ -6,20 +6,13 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.naming.NamingLens.NonIdentityNamingLens;
import com.android.tools.r8.utils.InternalOptions;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.function.Function;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
+import java.util.IdentityHashMap;
// Naming lens for rewriting type prefixes.
public class PrefixRewritingNamingLens extends NonIdentityNamingLens {
@@ -41,7 +34,7 @@
}
public PrefixRewritingNamingLens(NamingLens namingLens, AppView<?> appView) {
- super(appView.dexItemFactory());
+ super(appView.dexItemFactory(), new IdentityHashMap<>());
this.appView = appView;
this.namingLens = namingLens;
this.options = appView.options();
@@ -103,6 +96,18 @@
}
@Override
+ public DexString lookupDescriptorForJavaTypeName(String typeName) {
+ if (appView.rewritePrefix.shouldRewriteTypeName(typeName)) {
+ DexType rewrittenType =
+ appView.rewritePrefix.rewrittenType(dexItemFactory().createType(typeName), appView);
+ if (rewrittenType != null) {
+ return rewrittenType.descriptor;
+ }
+ }
+ return namingLens.lookupDescriptorForJavaTypeName(typeName);
+ }
+
+ @Override
public String lookupPackageName(String packageName) {
// Used for resource shrinking.
// Desugared libraries do not have resources.
@@ -122,30 +127,6 @@
}
@Override
- public <T extends DexItem> Map<String, T> getRenamedItems(
- Class<T> clazz, Predicate<T> predicate, Function<T, String> namer) {
- Map<String, T> renamedItemsPrefixRewritting;
- if (clazz == DexType.class) {
- renamedItemsPrefixRewritting = new HashMap<>();
- appView.rewritePrefix.forAllRewrittenTypes(
- item -> {
- T cast = clazz.cast(item);
- if (predicate.test(cast)) {
- renamedItemsPrefixRewritting.put(namer.apply(cast), cast);
- }
- });
- } else {
- renamedItemsPrefixRewritting = Collections.emptyMap();
- }
- Map<String, T> renamedItemsMinifier = namingLens.getRenamedItems(clazz, predicate, namer);
- // The Collector throws an exception for duplicated keys.
- return Stream.concat(
- renamedItemsPrefixRewritting.entrySet().stream(),
- renamedItemsMinifier.entrySet().stream())
- .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
- }
-
- @Override
public boolean verifyRenamingConsistentWithResolution(DexMethod item) {
return namingLens.verifyRenamingConsistentWithResolution(item);
}
diff --git a/src/main/java/com/android/tools/r8/relocator/RelocatorCommandLine.java b/src/main/java/com/android/tools/r8/relocator/RelocatorCommandLine.java
index f255aee..dc11daa 100644
--- a/src/main/java/com/android/tools/r8/relocator/RelocatorCommandLine.java
+++ b/src/main/java/com/android/tools/r8/relocator/RelocatorCommandLine.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.Version;
import com.android.tools.r8.origin.CommandLineOrigin;
import com.android.tools.r8.utils.ExceptionUtils;
+import com.android.tools.r8.utils.StringUtils;
public class RelocatorCommandLine {
@@ -21,8 +22,7 @@
*/
public static void main(String[] args) {
if (args.length == 0) {
- System.err.println(USAGE_MESSAGE);
- System.exit(ExceptionUtils.STATUS_ERROR);
+ throw new RuntimeException(StringUtils.joinLines("Invalid invocation.", USAGE_MESSAGE));
}
ExceptionUtils.withMainProgramHandler(() -> run(args));
}
diff --git a/src/main/java/com/android/tools/r8/relocator/SimplePackagesRewritingMapper.java b/src/main/java/com/android/tools/r8/relocator/SimplePackagesRewritingMapper.java
index 09ca5bd..3a5902a 100644
--- a/src/main/java/com/android/tools/r8/relocator/SimplePackagesRewritingMapper.java
+++ b/src/main/java/com/android/tools/r8/relocator/SimplePackagesRewritingMapper.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
@@ -22,8 +21,6 @@
import com.google.common.collect.ImmutableMap;
import java.util.IdentityHashMap;
import java.util.Map;
-import java.util.function.Function;
-import java.util.function.Predicate;
class SimplePackagesRewritingMapper {
@@ -105,7 +102,7 @@
Map<DexType, DexString> typeMappings,
Map<String, String> packageMappings,
DexItemFactory factory) {
- super(factory);
+ super(factory, typeMappings);
this.typeMappings = typeMappings;
this.packageMappings = packageMappings;
}
@@ -136,15 +133,6 @@
}
@Override
- public <T extends DexItem> Map<String, T> getRenamedItems(
- Class<T> clazz, Predicate<T> predicate, Function<T, String> namer) {
- return typeMappings.keySet().stream()
- .filter(item -> (clazz.isInstance(item) && predicate.test(clazz.cast(item))))
- .map(clazz::cast)
- .collect(ImmutableMap.toImmutableMap(namer, i -> i));
- }
-
- @Override
public boolean verifyRenamingConsistentWithResolution(DexMethod item) {
return true;
}
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 d202954..0d66a64 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.retrace;
import static com.android.tools.r8.retrace.internal.RetraceUtils.firstNonWhiteSpaceCharacterFromIndex;
-import static com.android.tools.r8.utils.ExceptionUtils.STATUS_ERROR;
import static com.android.tools.r8.utils.ExceptionUtils.failWithFakeEntry;
import com.android.tools.r8.Diagnostic;
@@ -358,13 +357,9 @@
action.run();
} catch (RetraceFailedException | RetraceAbortException e) {
// Detail of the errors were already reported
- System.err.println("Retrace failed");
- System.exit(STATUS_ERROR);
- throw null;
+ throw new RuntimeException("Retrace failed", e);
} catch (Throwable t) {
- System.err.println("Retrace failed with an internal error.");
- t.printStackTrace();
- System.exit(STATUS_ERROR);
+ throw new RuntimeException("Retrace failed with an internal error.", t);
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/PlainStackTraceVisitor.java b/src/main/java/com/android/tools/r8/retrace/internal/PlainStackTraceVisitor.java
index 9272f58..a632f1c 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/PlainStackTraceVisitor.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/PlainStackTraceVisitor.java
@@ -123,8 +123,8 @@
if (parensStart >= line.length()) {
return null;
}
- int parensEnd = firstCharFromIndex(line, parensStart, ')');
- if (parensEnd >= line.length()) {
+ int parensEnd = line.lastIndexOf(')');
+ if (parensEnd <= parensStart) {
return null;
}
if (firstNonWhiteSpaceCharacterFromIndex(line, parensEnd) == line.length()) {
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceRegularExpression.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceRegularExpression.java
index fb79984..5790d52 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceRegularExpression.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceRegularExpression.java
@@ -276,10 +276,10 @@
@Override
String subExpression() {
- String anyNonDigitSourceFileChar = anyNonDigitLetterCharWithMarkers + "_ \\.";
- String anyChar = anyNonDigitSourceFileChar + anyDigit;
- String colonWithNonDigitSuffix = ":[" + anyNonDigitSourceFileChar + ":" + "]";
- return "((?:(?:(?:" + colonWithNonDigitSuffix + "))|(?:[" + anyChar + "]))+)?";
+ String anyNonDigitNonColonChar = "^\\d:";
+ String anyNonColonChar = "^:";
+ String colonWithNonDigitSuffix = ":+[" + anyNonDigitNonColonChar + "]";
+ return "((?:(?:(?:" + colonWithNonDigitSuffix + "))|(?:[" + anyNonColonChar + "]))+)?";
}
@Override
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceUtils.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceUtils.java
index cd3e12d..02166f9 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceUtils.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceUtils.java
@@ -116,7 +116,7 @@
if (newFileName.endsWith("Kt") && (extension.isEmpty() || extension.equals("kt"))) {
newFileName = newFileName.substring(0, newFileName.length() - 2);
extension = "kt";
- } else if (extension.isEmpty()) {
+ } else if (!extension.equals("kt")) {
extension = "java";
}
return newFileName + "." + extension;
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
index 9706eab..762617e 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -86,7 +86,8 @@
assert !DexAnnotation.isMemberClassesAnnotation(annotation, dexItemFactory);
assert !DexAnnotation.isEnclosingMethodAnnotation(annotation, dexItemFactory);
assert !DexAnnotation.isEnclosingClassAnnotation(annotation, dexItemFactory);
- assert !DexAnnotation.isSignatureAnnotation(annotation, dexItemFactory);
+ assert appView.options().passthroughDexCode
+ || !DexAnnotation.isSignatureAnnotation(annotation, dexItemFactory);
if (config.exceptions && DexAnnotation.isThrowingAnnotation(annotation, dexItemFactory)) {
return true;
}
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 855e24c..b374e58 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.shaking;
import static com.android.tools.r8.graph.DexEncodedMethod.asProgramMethodOrNull;
+import static com.android.tools.r8.graph.DexEncodedMethod.toMethodDefinitionOrNull;
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult.isOverriding;
@@ -1066,7 +1067,7 @@
case STATIC:
return lookupStaticTarget(target, context);
case SUPER:
- return lookupSuperTarget(target, context);
+ return toMethodDefinitionOrNull(lookupSuperTarget(target, context));
default:
return null;
}
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 72d5af2..c15fdb8 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -2858,28 +2858,29 @@
reason);
}
// If invoke target is invalid (inaccessible or not an instance-method) record it and stop.
- DexEncodedMethod target = resolution.lookupInvokeSuperTarget(from.getHolder(), appInfo);
+ DexClassAndMethod target = resolution.lookupInvokeSuperTarget(from.getHolder(), appInfo);
if (target == null) {
failedResolutionTargets.add(resolution.getResolvedMethod().method);
return;
}
- DexProgramClass clazz = getProgramClassOrNull(target.getHolderType(), from);
+ DexProgramClass clazz = target.getHolder().asProgramClass();
if (clazz == null) {
return;
}
- ProgramMethod method = new ProgramMethod(clazz, target);
+ ProgramMethod method = target.asProgramMethod();
if (Log.ENABLED) {
- Log.verbose(getClass(), "Adding super constraint from `%s` to `%s`", from, target.method);
+ Log.verbose(
+ getClass(), "Adding super constraint from `%s` to `%s`", from, target.getReference());
}
if (superInvokeDependencies
.computeIfAbsent(from.getDefinition(), ignore -> ProgramMethodSet.create())
.add(method)) {
if (liveMethods.contains(from)) {
markMethodAsTargeted(method, KeepReason.invokedViaSuperFrom(from));
- if (!target.accessFlags.isAbstract()) {
+ if (!target.getAccessFlags().isAbstract()) {
markVirtualMethodAsLive(method, KeepReason.invokedViaSuperFrom(from));
}
}
@@ -3671,24 +3672,22 @@
if (rootSet.noShrinking.containsMethod(singleTarget.getReference())) {
return;
}
- if (methodToKeep != singleTarget) {
+ if (methodToKeep != singleTarget
+ && !syntheticInterfaceMethodBridges.containsKey(methodToKeep.getDefinition().method)) {
+ syntheticInterfaceMethodBridges.put(methodToKeep.getDefinition().method, methodToKeep);
assert null == methodToKeep.getHolder().lookupMethod(methodToKeep.getDefinition().method);
- ProgramMethod old =
- syntheticInterfaceMethodBridges.put(methodToKeep.getDefinition().method, methodToKeep);
- if (old == null) {
- if (singleTargetMethod.isLibraryMethodOverride().isTrue()) {
- methodToKeep.getDefinition().setLibraryMethodOverride(OptionalBool.TRUE);
- }
- DexProgramClass singleTargetHolder = singleTarget.getHolder();
- assert singleTargetHolder.isInterface();
- markVirtualMethodAsReachable(
- singleTargetMethod.method,
- singleTargetHolder.isInterface(),
- singleTarget,
- graphReporter.fakeReportShouldNotBeUsed());
- enqueueMarkMethodLiveAction(
- singleTarget, singleTarget, graphReporter.fakeReportShouldNotBeUsed());
+ if (singleTargetMethod.isLibraryMethodOverride().isTrue()) {
+ methodToKeep.getDefinition().setLibraryMethodOverride(OptionalBool.TRUE);
}
+ DexProgramClass singleTargetHolder = singleTarget.getHolder();
+ assert singleTargetHolder.isInterface();
+ markVirtualMethodAsReachable(
+ singleTargetMethod.method,
+ singleTargetHolder.isInterface(),
+ singleTarget,
+ graphReporter.fakeReportShouldNotBeUsed());
+ enqueueMarkMethodLiveAction(
+ singleTarget, singleTarget, graphReporter.fakeReportShouldNotBeUsed());
}
action.getAction().accept(builder);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassFilter.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassFilter.java
index d63374c..88c13ec 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassFilter.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassFilter.java
@@ -73,6 +73,9 @@
return TraversalContinuation.CONTINUE;
},
not(ProguardTypeMatcher::hasSpecificType));
+ if (traversalContinuation.shouldBreak()) {
+ break;
+ }
}
if (traversalContinuation.shouldContinue()) {
nonMatches.add(type);
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 8294be0..47ac1e3 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -104,6 +104,16 @@
private static final List<String> UNSUPPORTED_FLAG_OPTIONS =
ImmutableList.of("skipnonpubliclibraryclasses");
+ public static ImmutableList<ProguardConfigurationRule> parse(
+ List<ProguardConfigurationSource> sources, DexItemFactory factory, Reporter reporter) {
+ if (sources.isEmpty()) {
+ return ImmutableList.of();
+ }
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(factory, reporter);
+ parser.parse(sources);
+ return ImmutableList.copyOf(parser.getConfig().getRules());
+ }
+
public ProguardConfigurationParser(
DexItemFactory dexItemFactory, Reporter reporter) {
this(dexItemFactory, reporter, false);
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
index 8c5cce7..b5c6375 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
@@ -16,6 +16,7 @@
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.StringUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
@@ -92,8 +93,8 @@
*/
public static void main(String[] args) {
if (args.length == 0) {
- System.err.println(TraceReferencesCommandParser.USAGE_MESSAGE);
- System.exit(ExceptionUtils.STATUS_ERROR);
+ throw new RuntimeException(
+ StringUtils.joinLines("Invalid invocation.", TraceReferencesCommandParser.USAGE_MESSAGE));
}
ExceptionUtils.withMainProgramHandler(() -> run(args));
}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
index ddd3f78..895f31e 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
@@ -411,9 +412,9 @@
@Override
public void registerInvokeSuper(DexMethod method) {
- DexEncodedMethod superTarget = appInfo.lookupSuperTarget(method, context);
+ DexClassAndMethod superTarget = appInfo.lookupSuperTarget(method, context);
if (superTarget != null) {
- addMethod(superTarget.method);
+ addMethod(superTarget.getReference());
} else {
addMethod(method);
}
@@ -459,12 +460,12 @@
}
private void registerMethod(ProgramMethod method) {
- DexEncodedMethod superTarget =
+ DexClassAndMethod superTarget =
appInfo
.resolveMethodOn(method.getHolder(), method.getReference())
.lookupInvokeSpecialTarget(context, appInfo);
if (superTarget != null) {
- addMethod(superTarget.method);
+ addMethod(superTarget.getReference());
}
for (DexType type : method.getDefinition().parameters().values) {
registerTypeReference(type);
diff --git a/src/main/java/com/android/tools/r8/utils/BiPredicateUtils.java b/src/main/java/com/android/tools/r8/utils/BiPredicateUtils.java
new file mode 100644
index 0000000..f470d35
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/BiPredicateUtils.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2021, 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 java.util.function.BiPredicate;
+
+public class BiPredicateUtils {
+
+ public static <S, T> BiPredicate<S, T> alwaysFalse() {
+ return (s, t) -> false;
+ }
+
+ public static <S, T> BiPredicate<S, T> alwaysTrue() {
+ return (s, t) -> true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/ClassMap.java b/src/main/java/com/android/tools/r8/utils/ClassMap.java
index 474f5d0..3c7b99b 100644
--- a/src/main/java/com/android/tools/r8/utils/ClassMap.java
+++ b/src/main/java/com/android/tools/r8/utils/ClassMap.java
@@ -71,10 +71,8 @@
*/
abstract Supplier<T> getTransparentSupplier(T clazz);
- /**
- * Kind of the classes supported by this collection.
- */
- abstract ClassKind getClassKind();
+ /** Kind of the classes supported by this collection. */
+ abstract ClassKind<?> getClassKind();
@Override
public String toString() {
diff --git a/src/main/java/com/android/tools/r8/utils/ClassProvider.java b/src/main/java/com/android/tools/r8/utils/ClassProvider.java
index 40d5087..c048529 100644
--- a/src/main/java/com/android/tools/r8/utils/ClassProvider.java
+++ b/src/main/java/com/android/tools/r8/utils/ClassProvider.java
@@ -23,14 +23,14 @@
/** Represents a provider for classes loaded from different sources. */
public abstract class ClassProvider<T extends DexClass> {
- private final ClassKind classKind;
+ private final ClassKind<T> classKind;
- ClassProvider(ClassKind classKind) {
+ ClassProvider(ClassKind<T> classKind) {
this.classKind = classKind;
}
/** The kind of the classes created by the provider. */
- final ClassKind getClassKind() {
+ final ClassKind<T> getClassKind() {
return classKind;
}
@@ -56,13 +56,13 @@
/** Create class provider for java class resource provider. */
public static <T extends DexClass> ClassProvider<T> forClassFileResources(
- ClassKind classKind, ClassFileResourceProvider provider, JarApplicationReader reader) {
+ ClassKind<T> classKind, ClassFileResourceProvider provider, JarApplicationReader reader) {
return new ClassFileResourceReader<>(classKind, provider, reader);
}
/** Create class provider for preloaded classes, classes may have conflicting names. */
public static <T extends DexClass> ClassProvider<T> forPreloadedClasses(
- ClassKind classKind, Collection<T> classes) {
+ ClassKind<T> classKind, Collection<T> classes) {
ImmutableListMultimap.Builder<DexType, T> builder = ImmutableListMultimap.builder();
for (T clazz : classes) {
builder.put(clazz.type, clazz);
@@ -72,17 +72,17 @@
/** Create class provider for preloaded classes. */
public static <T extends DexClass> ClassProvider<T> combine(
- ClassKind classKind, List<ClassProvider<T>> providers) {
+ ClassKind<T> classKind, List<ClassProvider<T>> providers) {
return new CombinedClassProvider<>(classKind, providers);
}
private static class ClassFileResourceReader<T extends DexClass> extends ClassProvider<T> {
- private final ClassKind classKind;
+ private final ClassKind<T> classKind;
private final ClassFileResourceProvider provider;
private final JarApplicationReader reader;
private ClassFileResourceReader(
- ClassKind classKind, ClassFileResourceProvider provider, JarApplicationReader reader) {
+ ClassKind<T> classKind, ClassFileResourceProvider provider, JarApplicationReader reader) {
super(classKind);
this.classKind = classKind;
this.provider = provider;
@@ -95,9 +95,9 @@
ProgramResource resource = provider.getProgramResource(descriptor);
if (resource != null) {
try {
- JarClassFileReader classReader =
- new JarClassFileReader(reader, classKind.bridgeConsumer(classConsumer));
- classReader.read(resource, classKind);
+ JarClassFileReader<T> classReader =
+ new JarClassFileReader<>(reader, classConsumer, classKind);
+ classReader.read(resource);
} catch (ResourceException e) {
throw new CompilationError("Failed to load class: " + descriptor, e);
}
@@ -122,7 +122,7 @@
private static class PreloadedClassProvider<T extends DexClass> extends ClassProvider<T> {
private final Multimap<DexType, T> classes;
- private PreloadedClassProvider(ClassKind classKind, Multimap<DexType, T> classes) {
+ private PreloadedClassProvider(ClassKind<T> classKind, Multimap<DexType, T> classes) {
super(classKind);
this.classes = classes;
}
@@ -148,7 +148,7 @@
private static class CombinedClassProvider<T extends DexClass> extends ClassProvider<T> {
private final List<ClassProvider<T>> providers;
- private CombinedClassProvider(ClassKind classKind, List<ClassProvider<T>> providers) {
+ private CombinedClassProvider(ClassKind<T> classKind, List<ClassProvider<T>> providers) {
super(classKind);
this.providers = providers;
}
diff --git a/src/main/java/com/android/tools/r8/utils/ClasspathClassCollection.java b/src/main/java/com/android/tools/r8/utils/ClasspathClassCollection.java
index 45002d0..28eb17b 100644
--- a/src/main/java/com/android/tools/r8/utils/ClasspathClassCollection.java
+++ b/src/main/java/com/android/tools/r8/utils/ClasspathClassCollection.java
@@ -25,7 +25,7 @@
}
@Override
- ClassKind getClassKind() {
+ ClassKind<DexClasspathClass> getClassKind() {
return ClassKind.CLASSPATH;
}
diff --git a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
index 61aa232..8c4da03 100644
--- a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
@@ -25,8 +25,6 @@
public abstract class ExceptionUtils {
- public static final int STATUS_ERROR = 1;
-
public static void withConsumeResourceHandler(
Reporter reporter, StringConsumer consumer, String data) {
withConsumeResourceHandler(reporter, handler -> consumer.accept(data, handler));
@@ -176,23 +174,19 @@
try {
action.run();
} catch (CompilationFailedException e) {
- throw exitWithError(e, e.getCause());
+ printExitMessage(e.getCause());
+ throw new RuntimeException(e);
} catch (RuntimeException e) {
- throw exitWithError(e, e);
+ printExitMessage(e);
+ throw e;
}
}
- private static RuntimeException exitWithError(Throwable e, Throwable cause) {
- if (isExpectedException(cause)) {
- // Detail of the errors were already reported
- System.err.println("Compilation failed");
- System.exit(STATUS_ERROR);
- throw null;
- }
- System.err.println("Compilation failed with an internal error.");
- e.printStackTrace();
- System.exit(STATUS_ERROR);
- throw null;
+ private static void printExitMessage(Throwable cause) {
+ System.err.println(
+ isExpectedException(cause)
+ ? "Compilation failed"
+ : "Compilation failed with an internal error.");
}
private static boolean isExpectedException(Throwable e) {
diff --git a/src/main/java/com/android/tools/r8/utils/LibraryClassCollection.java b/src/main/java/com/android/tools/r8/utils/LibraryClassCollection.java
index 863cce0..d661a87 100644
--- a/src/main/java/com/android/tools/r8/utils/LibraryClassCollection.java
+++ b/src/main/java/com/android/tools/r8/utils/LibraryClassCollection.java
@@ -30,7 +30,7 @@
}
@Override
- ClassKind getClassKind() {
+ ClassKind<DexLibraryClass> getClassKind() {
return ClassKind.LIBRARY;
}
diff --git a/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java b/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java
index 68fa8f3..fff5425 100644
--- a/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java
+++ b/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java
@@ -53,7 +53,7 @@
}
@Override
- ClassKind getClassKind() {
+ ClassKind<DexProgramClass> getClassKind() {
return ClassKind.PROGRAM;
}
diff --git a/src/main/java/com/android/tools/r8/utils/ValueUtils.java b/src/main/java/com/android/tools/r8/utils/ValueUtils.java
new file mode 100644
index 0000000..8121493
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ValueUtils.java
@@ -0,0 +1,46 @@
+// Copyright (c) 2021, 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.graph.DexItemFactory;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.NewInstance;
+import com.android.tools.r8.ir.code.Value;
+
+public class ValueUtils {
+
+ public static boolean isStringBuilder(Value value, DexItemFactory dexItemFactory) {
+ TypeElement type = value.getType();
+ return type.isClassType()
+ && type.asClassType().getClassType() == dexItemFactory.stringBuilderType;
+ }
+
+ public static boolean isNonNullStringBuilder(Value value, DexItemFactory dexItemFactory) {
+ while (true) {
+ if (value.isPhi()) {
+ return false;
+ }
+
+ Instruction definition = value.getDefinition();
+ if (definition.isNewInstance()) {
+ NewInstance newInstance = definition.asNewInstance();
+ return newInstance.clazz == dexItemFactory.stringBuilderType;
+ }
+
+ if (definition.isInvokeVirtual()) {
+ InvokeVirtual invoke = definition.asInvokeVirtual();
+ if (dexItemFactory.stringBuilderMethods.isAppendMethod(invoke.getInvokedMethod())) {
+ value = invoke.getReceiver();
+ continue;
+ }
+ }
+
+ // Unhandled definition.
+ return false;
+ }
+ }
+}
diff --git a/src/main/keep_retrace.txt b/src/main/keep_retrace.txt
new file mode 100644
index 0000000..6a71f9e
--- /dev/null
+++ b/src/main/keep_retrace.txt
@@ -0,0 +1,15 @@
+# Copyright (c) 2021, 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.
+
+# The retrace api is separated out without repackaging which is why this broad
+# rule is used.
+-keep public class com.android.tools.r8.retrace.* {
+ public <methods>;
+ public <fields>;
+ }
+-keepattributes SourceFile, LineNumberTable, InnerClasses, EnclosingMethod, Exceptions, Signature
+-keepparameternames
+# This is run on r8lib so keep everything in lib that is traced. That way
+# we only need a single mapping file
+-keep,allowshrinking class * { *; }
\ No newline at end of file
diff --git a/src/test/apiUsageSample/com/android/tools/apiusagesample/D8ApiUsageSample.java b/src/test/apiUsageSample/com/android/tools/apiusagesample/D8ApiUsageSample.java
index 642a4ab..27dbc0f 100644
--- a/src/test/apiUsageSample/com/android/tools/apiusagesample/D8ApiUsageSample.java
+++ b/src/test/apiUsageSample/com/android/tools/apiusagesample/D8ApiUsageSample.java
@@ -71,6 +71,7 @@
List<Path> libraries = new ArrayList<>(1);
List<Path> classpath = new ArrayList<>(args.length);
List<Path> mainDexList = new ArrayList<>(1);
+ List<Path> mainDexRules = new ArrayList<>(1);
List<Path> inputs = new ArrayList<>(args.length);
for (int i = 0; i < args.length; i++) {
if (args[i].equals("--lib")) {
@@ -79,6 +80,8 @@
classpath.add(Paths.get(args[++i]));
} else if (args[i].equals("--main-dex-list")) {
mainDexList.add(Paths.get(args[++i]));
+ } else if (args[i].equals("--main-dex-rules")) {
+ mainDexRules.add(Paths.get(args[++i]));
} else if (isArchive(args[i]) || isClassFile(args[i])) {
inputs.add(Paths.get(args[i]));
}
@@ -98,6 +101,9 @@
if (mainDexList.isEmpty()) {
throw new RuntimeException("Must supply main-dex-list inputs");
}
+ if (mainDexRules.isEmpty()) {
+ throw new RuntimeException("Must supply main-dex-rules inputs");
+ }
useProgramFileList(CompilationMode.DEBUG, minApiLevel, libraries, classpath, inputs);
useProgramFileList(CompilationMode.RELEASE, minApiLevel, libraries, classpath, inputs);
@@ -106,6 +112,8 @@
useLibraryAndClasspathProvider(minApiLevel, libraries, classpath, inputs);
useMainDexListFiles(minApiLevel, libraries, classpath, inputs, mainDexList);
useMainDexClasses(minApiLevel, libraries, classpath, inputs, mainDexList);
+ useMainDexRulesFiles(minApiLevel, libraries, classpath, inputs, mainDexRules);
+ useMainDexRules(minApiLevel, libraries, classpath, inputs, mainDexRules);
useAssertionConfig(minApiLevel, libraries, classpath, inputs);
useVArgVariants(minApiLevel, libraries, classpath, inputs, mainDexList);
incrementalCompileAndMerge(minApiLevel, libraries, classpath, inputs);
@@ -283,6 +291,53 @@
}
}
+ private static void useMainDexRulesFiles(
+ int minApiLevel,
+ Collection<Path> libraries,
+ Collection<Path> classpath,
+ Collection<Path> inputs,
+ Collection<Path> mainDexRules) {
+ try {
+ D8.run(
+ D8Command.builder(handler)
+ .setMinApiLevel(minApiLevel)
+ .setProgramConsumer(new EnsureOutputConsumer())
+ .addLibraryFiles(libraries)
+ .addClasspathFiles(classpath)
+ .addProgramFiles(inputs)
+ .addMainDexRulesFiles(mainDexRules)
+ .build());
+ } catch (CompilationFailedException e) {
+ throw new RuntimeException("Unexpected compilation exceptions", e);
+ }
+ }
+
+ private static void useMainDexRules(
+ int minApiLevel,
+ Collection<Path> libraries,
+ Collection<Path> classpath,
+ Collection<Path> inputs,
+ Collection<Path> mainDexRulesFiles) {
+ try {
+ D8Command.Builder builder =
+ D8Command.builder(handler)
+ .setMinApiLevel(minApiLevel)
+ .setProgramConsumer(new EnsureOutputConsumer())
+ .addLibraryFiles(libraries)
+ .addClasspathFiles(classpath)
+ .addProgramFiles(inputs);
+ for (Path mainDexRulesFile : mainDexRulesFiles) {
+ builder.addMainDexRules(
+ Files.readAllLines(mainDexRulesFile), new PathOrigin(mainDexRulesFile));
+ }
+ D8.run(builder.build());
+ } catch (CompilationFailedException e) {
+ throw new RuntimeException("Unexpected compilation exceptions", e);
+ } catch (IOException e) {
+ throw new RuntimeException("Unexpected IO exception", e);
+ }
+ }
+
private static void useAssertionConfig(
int minApiLevel,
Collection<Path> libraries,
diff --git a/src/test/java/com/android/tools/r8/D8ApiBinaryCompatibilityTests.java b/src/test/java/com/android/tools/r8/D8ApiBinaryCompatibilityTests.java
index 2c53667..8e33480 100644
--- a/src/test/java/com/android/tools/r8/D8ApiBinaryCompatibilityTests.java
+++ b/src/test/java/com/android/tools/r8/D8ApiBinaryCompatibilityTests.java
@@ -58,6 +58,9 @@
Path mainDexList = temp.getRoot().toPath().resolve("maindexlist.txt");
FileUtils.writeTextFile(mainDexList, "desugaringwithmissingclasstest1/Main.class");
+ Path mainDexRules = temp.getRoot().toPath().resolve("maindex.rules");
+ FileUtils.writeTextFile(mainDexRules, "# empty file");
+
// It is important to place the api usage sample jar after the current classpath because we want
// to find D8/R8 classes before the ones in the jar, otherwise renamed classes and fields cannot
// be found.
@@ -67,7 +70,8 @@
.addAll(
ImmutableList.of(
ToolHelper.getJavaExecutable(),
- "-cp", classPath,
+ "-cp",
+ classPath,
main,
// Compiler arguments.
"--output",
@@ -76,9 +80,11 @@
Integer.toString(minApiLevel),
"--main-dex-list",
mainDexList.toString(),
+ "--main-dex-rules",
+ mainDexRules.toString(),
"--lib",
- ToolHelper.getAndroidJar(
- AndroidApiLevel.getAndroidApiLevel(minApiLevel)).toString(),
+ ToolHelper.getAndroidJar(AndroidApiLevel.getAndroidApiLevel(minApiLevel))
+ .toString(),
"--classpath",
lib1.toString(),
"--classpath",
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index 9b80b80..3bcb3a7 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -255,6 +255,21 @@
}
@Test
+ public void mainDexRules() throws Throwable {
+ 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());
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void nonExistingMainDexRules() throws Throwable {
+ Path mainDexRules = temp.getRoot().toPath().resolve("main-dex.rules");
+ parse("--main-dex-rules", mainDexRules.toString());
+ }
+
+ @Test
public void mainDexList() throws Throwable {
Path mainDexList1 = temp.newFile("main-dex-list-1.txt").toPath();
Path mainDexList2 = temp.newFile("main-dex-list-2.txt").toPath();
diff --git a/src/test/java/com/android/tools/r8/D8TestBuilder.java b/src/test/java/com/android/tools/r8/D8TestBuilder.java
index 44fe743..4c88d91 100644
--- a/src/test/java/com/android/tools/r8/D8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/D8TestBuilder.java
@@ -81,4 +81,9 @@
}
return self();
}
+
+ public D8TestBuilder addMainDexRulesFiles(Path... mainDexRuleFiles) {
+ builder.addMainDexRulesFiles(mainDexRuleFiles);
+ return self();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
index 622d595..0e65de1 100644
--- a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
@@ -167,8 +167,14 @@
@Override
public ProguardTestBuilder addProgramClassFileData(Collection<byte[]> classes) {
- throw new Unimplemented(
- "No support for adding class files directly (we need to compute the descriptor)");
+ try {
+ Path out = getState().getNewTempFolder().resolve("out.jar");
+ TestBase.writeClassFileDataToJar(out, classes);
+ injars.add(out);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return self();
}
@Override
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index 0b9c896..7157943 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -107,15 +107,14 @@
.withOptionConsumer(opts -> opts.enableClassInlining = false)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 102, "lambdadesugaring"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 101, "lambdadesugaring"))
.run();
test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
.withMinApiLevel(ToolHelper.getMinApiLevelForDexVmNoHigherThan(AndroidApiLevel.K))
- .withOptionConsumer(opts -> opts.enableClassInlining = true)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 7, "lambdadesugaring"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 6, "lambdadesugaring"))
.run();
}
@@ -147,15 +146,14 @@
.withOptionConsumer(opts -> opts.enableClassInlining = false)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 102, "lambdadesugaring"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 101, "lambdadesugaring"))
.run();
test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
.withMinApiLevel(AndroidApiLevel.N)
- .withOptionConsumer(opts -> opts.enableClassInlining = true)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 7, "lambdadesugaring"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 6, "lambdadesugaring"))
.run();
}
@@ -175,7 +173,6 @@
test("lambdadesugaringnplus", "lambdadesugaringnplus", "LambdasWithStaticAndDefaultMethods")
.withMinApiLevel(ToolHelper.getMinApiLevelForDexVmNoHigherThan(AndroidApiLevel.K))
.withInterfaceMethodDesugaring(OffOrAuto.Auto)
- .withOptionConsumer(opts -> opts.enableClassInlining = true)
.withBuilderTransformation(ToolHelper::allowTestProguardOptions)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS_N_PLUS, Origin.unknown()))
@@ -199,7 +196,6 @@
test("lambdadesugaringnplus", "lambdadesugaringnplus", "LambdasWithStaticAndDefaultMethods")
.withMinApiLevel(AndroidApiLevel.N)
.withInterfaceMethodDesugaring(OffOrAuto.Auto)
- .withOptionConsumer(opts -> opts.enableClassInlining = true)
.withBuilderTransformation(ToolHelper::allowTestProguardOptions)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS_N_PLUS, Origin.unknown()))
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index ad512cd..a85ae6a 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -176,6 +176,7 @@
public static final Path R8LIB_EXCLUDE_DEPS_MAP =
Paths.get(LIBS_DIR, "r8lib-exclude-deps.jar.map");
public static final Path DEPS = Paths.get(LIBS_DIR, "deps_all.jar");
+ public static final Path R8_RETRACE_JAR = Paths.get(LIBS_DIR, "r8retrace.jar");
public static final Path DESUGAR_LIB_CONVERSIONS =
Paths.get(LIBS_DIR, "library_desugar_conversions.zip");
diff --git a/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java b/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
index 689e080..4161d87 100644
--- a/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
+++ b/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.ClassKind;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.JarApplicationReader;
import com.android.tools.r8.graph.JarClassFileReader;
@@ -95,8 +96,8 @@
private void readMethodTemplatesInto(CfCodePrinter codePrinter) throws IOException {
InternalOptions options = new InternalOptions();
options.testing.readInputStackMaps = true;
- JarClassFileReader reader =
- new JarClassFileReader(
+ JarClassFileReader<DexProgramClass> reader =
+ new JarClassFileReader<>(
new JarApplicationReader(options),
clazz -> {
for (DexEncodedMethod method : clazz.allMethodsSorted()) {
@@ -107,9 +108,10 @@
method.getHolderType().getName() + "_" + method.method.name.toString();
codePrinter.visitMethod(methodName, method.getCode().asCfCode());
}
- });
+ },
+ ClassKind.PROGRAM);
for (Class<?> clazz : getMethodTemplateClasses()) {
- reader.read(Origin.unknown(), ClassKind.PROGRAM, ToolHelper.getClassAsBytes(clazz));
+ reader.read(Origin.unknown(), ToolHelper.getClassAsBytes(clazz));
}
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/IllegalInliningOfMergedConstructorTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/IllegalInliningOfMergedConstructorTest.java
new file mode 100644
index 0000000..bbc415c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/IllegalInliningOfMergedConstructorTest.java
@@ -0,0 +1,90 @@
+// Copyright (c) 2021, 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.classmerging.horizontal;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+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 IllegalInliningOfMergedConstructorTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection params() {
+ return getTestParameters().withAllRuntimesAndApiLevels().withAllApiLevels().build();
+ }
+
+ public IllegalInliningOfMergedConstructorTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addEnumUnboxingInspector(
+ inspector -> inspector.assertUnboxedIf(parameters.isDexRuntime(), Reprocess.class))
+ .addHorizontallyMergedClassesInspector(
+ inspector -> inspector.assertMergedInto(B.class, A.class))
+ .addOptionsModification(options -> options.inliningInstructionLimit = 4)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello w0rld!");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new A((System.currentTimeMillis() > 0 ? Reprocess.A : Reprocess.B)).foo();
+ }
+ }
+
+ @NeverClassInline
+ static class A {
+
+ A(Reprocess r) {
+ new B().foo();
+
+ // This will force this constructor to be reprocessed, which will cause the inliner to attempt
+ // to inline B.<init>() into A.<init>(). Since B.<init>() has been moved to A as a result of
+ // horizontal class merging, the inliner will check if B.<init>() is eligible for inlining,
+ // which it is not in this case, due the assignment of $r8$classId.
+ System.out.print(r.ordinal());
+ }
+
+ @NeverInline
+ void foo() {
+ System.out.println("rld!");
+ }
+ }
+
+ @NeverClassInline
+ static class B {
+
+ public B() {}
+
+ @NeverInline
+ void foo() {
+ System.out.print("Hello w");
+ }
+ }
+
+ enum Reprocess {
+ A,
+ B
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonFinalOverrideOfFinalMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonFinalOverrideOfFinalMethodTest.java
new file mode 100644
index 0000000..d73ffcf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonFinalOverrideOfFinalMethodTest.java
@@ -0,0 +1,98 @@
+// 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runners.Parameterized;
+
+public class NonFinalOverrideOfFinalMethodTest extends HorizontalClassMergingTestBase {
+
+ @Parameterized.Parameters(name = "{0}, horizontalClassMerging:{1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.trueValues());
+ }
+
+ public NonFinalOverrideOfFinalMethodTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options ->
+ options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .addHorizontallyMergedClassesInspectorIf(
+ enableHorizontalClassMerging, inspector -> inspector.assertMergedInto(B.class, A.class))
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+
+ MethodSubject synchronizedMethodSubject = aClassSubject.uniqueMethodWithName("foo");
+ assertThat(synchronizedMethodSubject, isPresent());
+ assertFalse(synchronizedMethodSubject.isFinal());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A", "B", "BSub");
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ new A().foo();
+ new B().bar();
+ new BSub().foo();
+ }
+ }
+
+ @NeverClassInline
+ public static class A {
+
+ @NeverInline
+ public final void foo() {
+ System.out.println("A");
+ }
+ }
+
+ @NeverClassInline
+ @NoVerticalClassMerging
+ public static class B {
+
+ @NeverInline
+ public void bar() {
+ System.out.println("B");
+ }
+ }
+
+ @NeverClassInline
+ public static class BSub extends B {
+
+ @NeverInline
+ public void foo() {
+ System.out.println("BSub");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/StrictMethodMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/StrictMethodMergingTest.java
new file mode 100644
index 0000000..762824f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/StrictMethodMergingTest.java
@@ -0,0 +1,116 @@
+// 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runners.Parameterized;
+
+public class StrictMethodMergingTest extends HorizontalClassMergingTestBase {
+
+ @Parameterized.Parameters(name = "{0}, horizontalClassMerging:{1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.trueValues());
+ }
+
+ public StrictMethodMergingTest(TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options ->
+ options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .addHorizontallyMergedClassesInspectorIf(
+ enableHorizontalClassMerging,
+ inspector ->
+ inspector.assertMergedInto(B.class, A.class).assertMergedInto(D.class, C.class))
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+
+ MethodSubject synchronizedMethodSubject =
+ aClassSubject.uniqueMethodWithName("m$bridge");
+ assertThat(synchronizedMethodSubject, isPresent());
+ assertTrue(synchronizedMethodSubject.getAccessFlags().isStrict());
+
+ ClassSubject cClassSubject = inspector.clazz(C.class);
+ assertThat(cClassSubject, isPresent());
+
+ MethodSubject unsynchronizedMethodSubject =
+ cClassSubject.uniqueMethodWithName("m$bridge");
+ assertThat(unsynchronizedMethodSubject, isPresent());
+ assertFalse(unsynchronizedMethodSubject.getAccessFlags().isStrict());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A", "B", "C", "D");
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ new A().m();
+ new B().m();
+ new C().m();
+ new D().m();
+ }
+ }
+
+ @NeverClassInline
+ public static class A {
+
+ @NeverInline
+ public strictfp void m() {
+ System.out.println("A");
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+
+ @NeverInline
+ public strictfp void m() {
+ System.out.println("B");
+ }
+ }
+
+ @NeverClassInline
+ public static class C {
+
+ @NeverInline
+ public void m() {
+ System.out.println("C");
+ }
+ }
+
+ @NeverClassInline
+ public static class D {
+
+ @NeverInline
+ public void m() {
+ System.out.println("D");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/SynchronizedMethodMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/SynchronizedMethodMergingTest.java
new file mode 100644
index 0000000..8d802c4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/SynchronizedMethodMergingTest.java
@@ -0,0 +1,117 @@
+// 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runners.Parameterized;
+
+public class SynchronizedMethodMergingTest extends HorizontalClassMergingTestBase {
+
+ @Parameterized.Parameters(name = "{0}, horizontalClassMerging:{1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.trueValues());
+ }
+
+ public SynchronizedMethodMergingTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options ->
+ options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .addHorizontallyMergedClassesInspectorIf(
+ enableHorizontalClassMerging,
+ inspector ->
+ inspector.assertMergedInto(B.class, A.class).assertMergedInto(D.class, C.class))
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+
+ MethodSubject synchronizedMethodSubject =
+ aClassSubject.uniqueMethodWithName("m$bridge");
+ assertThat(synchronizedMethodSubject, isPresent());
+ assertTrue(synchronizedMethodSubject.isSynchronized());
+
+ ClassSubject cClassSubject = inspector.clazz(C.class);
+ assertThat(cClassSubject, isPresent());
+
+ MethodSubject unsynchronizedMethodSubject =
+ cClassSubject.uniqueMethodWithName("m$bridge");
+ assertThat(unsynchronizedMethodSubject, isPresent());
+ assertFalse(unsynchronizedMethodSubject.isSynchronized());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A", "B", "C", "D");
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ new A().m();
+ new B().m();
+ new C().m();
+ new D().m();
+ }
+ }
+
+ @NeverClassInline
+ public static class A {
+
+ @NeverInline
+ public synchronized void m() {
+ System.out.println("A");
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+
+ @NeverInline
+ public synchronized void m() {
+ System.out.println("B");
+ }
+ }
+
+ @NeverClassInline
+ public static class C {
+
+ @NeverInline
+ public void m() {
+ System.out.println("C");
+ }
+ }
+
+ @NeverClassInline
+ public static class D {
+
+ @NeverInline
+ public void m() {
+ System.out.println("D");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
index ccfaeaf..5b87397 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
@@ -24,11 +24,13 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
+import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.tracereferences.TraceReferences;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
@@ -126,9 +128,13 @@
StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
.setMinApiLevel(apiLevel.getLevel())
.setOutput(desugaredLib, OutputMode.DexIndexed);
+ Path mapping = null;
if (shrink) {
- l8Builder.addProguardConfiguration(
- Arrays.asList(keepRules.split(System.lineSeparator())), Origin.unknown());
+ mapping = temp.newFolder().toPath().resolve("mapping.txt");
+ List<String> lines =
+ new ArrayList<>(Arrays.asList(keepRules.split(System.lineSeparator())));
+ lines.add("-printmapping " + mapping);
+ l8Builder.addProguardConfiguration(lines, Origin.unknown());
}
ToolHelper.runL8(
l8Builder.build(),
@@ -148,6 +154,15 @@
.startsWith(
"Invalid parameter counts in MethodParameter attributes.")));
}
+ // TODO(b/176900254): The nest check should not be necessary.
+ new CodeInspector(desugaredLib, mapping)
+ .forAllClasses(
+ clazz ->
+ assertTrue(
+ clazz.getFinalName().startsWith("j$.")
+ || clazz
+ .getOriginalName()
+ .startsWith(NestBasedAccessDesugaring.NEST_CONSTRUCTOR_NAME)));
return desugaredLib;
} catch (Exception e) {
// Don't wrap assumption violation so junit can catch it.
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MonthTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MonthTest.java
new file mode 100644
index 0000000..6b2692b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MonthTest.java
@@ -0,0 +1,88 @@
+// Copyright (c) 2021, 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.desugaredlibrary;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.utils.StringUtils;
+import java.time.Month;
+import java.time.format.TextStyle;
+import java.util.Locale;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MonthTest extends DesugaredLibraryTestBase {
+ private final TestParameters parameters;
+
+ private static final String EXPECTED_JAVA_8_OUTPUT = StringUtils.lines("4");
+ private static final String EXPECTED_JAVA_9_OUTPUT = StringUtils.lines("April");
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+ }
+
+ public MonthTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private String getExpectedResult(TestParameters parameters) {
+ if (parameters.isCfRuntime()) {
+ if (parameters.getRuntime().asCf().isNewerThan(TestRuntime.CfVm.JDK8)) {
+ return EXPECTED_JAVA_9_OUTPUT;
+ }
+ return EXPECTED_JAVA_8_OUTPUT;
+ }
+ assert parameters.isDexRuntime();
+ // Assumes java.time is desugared only if any library desugaring is required, i.e., on 26.
+ if (requiresAnyCoreLibDesugaring(parameters)) {
+ return EXPECTED_JAVA_8_OUTPUT;
+ }
+ return EXPECTED_JAVA_9_OUTPUT;
+ }
+
+ @Test
+ public void testMonthD8() throws Exception {
+ if (parameters.isCfRuntime()) {
+ testForJvm()
+ .addInnerClasses(MonthTest.class)
+ .run(parameters.getRuntime(), MonthTest.Main.class)
+ .assertSuccessWithOutput(getExpectedResult(parameters));
+ return;
+ }
+ testForD8(parameters.getBackend())
+ .addInnerClasses(MonthTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel())
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(getExpectedResult(parameters));
+ }
+
+ @Test
+ public void testMonthR8() throws Exception {
+ Assume.assumeTrue(parameters.isDexRuntime());
+ testForR8(parameters.getBackend())
+ .addInnerClasses(MonthTest.class)
+ .addKeepMainRule(MonthTest.Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel())
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(getExpectedResult(parameters));
+ }
+
+ static class Main {
+ public static void main(String[] args) {
+ System.out.println(Month.APRIL.getDisplayName(TextStyle.FULL_STANDALONE, Locale.UK));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
index a9590a3..8031f43 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
@@ -115,6 +115,10 @@
return invokesMethod("int", holder, "hashCode", ImmutableList.of("java.lang.Object"));
}
+ private Matcher<MethodSubject> invokesClassGetClass() {
+ return invokesMethod("java.lang.Class", "java.lang.Object", "getClass", ImmutableList.of());
+ }
+
private Matcher<MethodSubject> invokesObjectsRequireNonNull(String holder) {
return invokesMethod(
"java.lang.Object", holder, "requireNonNull", ImmutableList.of("java.lang.Object"));
@@ -218,7 +222,7 @@
onlyIf(invokeJavaUtilObjects, invokesObjectsRequireNonNull("java.util.Objects")));
assertThat(
testClass.uniqueMethodWithName("objectsRequireNonNull"),
- onlyIf(invokeJDollarUtilObjects, invokesObjectsRequireNonNull("j$.util.Objects")));
+ onlyIf(parameters.getApiLevel().isLessThan(AndroidApiLevel.K), invokesClassGetClass()));
assertThat(
testClass.uniqueMethodWithName("objectsRequireNonNullWithMessage"),
@@ -525,7 +529,7 @@
objectsEquals(args, args);
objectsHash(1, 2);
objectsHashCode(4);
- objectsRequireNonNull(null);
+ objectsRequireNonNull(System.currentTimeMillis() >= 0 ? null : new Object());
objectsRequireNonNullWithMessage(null, "Was null");
if (objectsRequireNonNullWithSupplierSupported) {
objectsRequireNonNullWithSupplier(null, () -> "Supplier said was null");
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTest.java
index fcc2124..dc13cb2 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTest.java
@@ -30,7 +30,7 @@
private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.N;
private static final String EXPECTED_RESULT =
- StringUtils.lines("[5, 6, 7]", "j$.$r8$wrapper$java$util$stream$IntStream$-V-WRP");
+ StringUtils.lines("[5, 6, 7]", "j$.wrappers.$r8$wrapper$java$util$stream$IntStream$-V-WRP");
@Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
public static List<Object[]> data() {
diff --git a/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java b/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
index d2a5729..330535e 100644
--- a/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
+++ b/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
@@ -41,15 +42,18 @@
}
private ObjectToOffsetMapping emptyObjectTObjectMapping() {
- return new ObjectToOffsetMapping(
+ AppView<AppInfo> appView =
AppView.createForD8(
AppInfo.createInitialAppInfo(
DexApplication.builder(
new InternalOptions(new DexItemFactory(), new Reporter()), null)
- .build())),
+ .build()));
+ return new ObjectToOffsetMapping(
+ appView,
GraphLens.getIdentityLens(),
NamingLens.getIdentityLens(),
InitClassLens.getDefault(),
+ new LensCodeRewriterUtils(appView),
Collections.emptyList(),
Collections.emptyList(),
Collections.emptyList(),
diff --git a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
index 7d22782..603a0b1 100644
--- a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
@@ -272,13 +272,13 @@
DexMethod mOnI3 = factory.createMethod(i3, mProto, m);
DexMethod mOnI4 = factory.createMethod(i4, mProto, m);
- assertEquals(mOnI0, appInfo.lookupSuperTarget(mOnC0, c1).method);
- assertEquals(mOnI1, appInfo.lookupSuperTarget(mOnI1, c1).method);
- assertEquals(mOnI2, appInfo.lookupSuperTarget(mOnI2, c1).method);
+ assertEquals(mOnI0, appInfo.lookupSuperTarget(mOnC0, c1).getReference());
+ assertEquals(mOnI1, appInfo.lookupSuperTarget(mOnI1, c1).getReference());
+ assertEquals(mOnI2, appInfo.lookupSuperTarget(mOnI2, c1).getReference());
assertNull(appInfo.lookupSuperTarget(mOnC1, c2)); // C2 is not a subclass of C1.
- assertEquals(mOnI1, appInfo.lookupSuperTarget(mOnI3, c2).method);
- assertEquals(mOnI2, appInfo.lookupSuperTarget(mOnI4, c2).method);
+ assertEquals(mOnI1, appInfo.lookupSuperTarget(mOnI3, c2).getReference());
+ assertEquals(mOnI2, appInfo.lookupSuperTarget(mOnI4, c2).getReference());
// Copy classes to run on the Java VM.
Path out = temp.newFolder().toPath();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/RedundantConstNumberRemovalTest.java b/src/test/java/com/android/tools/r8/ir/optimize/RedundantConstNumberRemovalTest.java
index 4813749..27f097b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/RedundantConstNumberRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/RedundantConstNumberRemovalTest.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -39,6 +40,7 @@
return getTestParameters()
.withCfRuntimes()
.withDexRuntimesStartingFromExcluding(Version.V4_4_4)
+ .withApiLevelsStartingAtIncluding(AndroidApiLevel.L)
.build();
}
@@ -64,7 +66,7 @@
.enableInliningAnnotations()
.addOptionsModification(
internalOptions -> internalOptions.enableRedundantConstNumberOptimization = true)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(TestClass.class)
.assertSuccessWithOutput(expectedOutput);
@@ -112,15 +114,10 @@
assertEquals(1, code.blocks.size());
// The block only has three instructions.
BasicBlock entryBlock = code.entryBlock();
- assertEquals(3, entryBlock.getInstructions().size());
+ assertEquals(2, entryBlock.getInstructions().size());
// The first one is the `argument` instruction.
Instruction argument = entryBlock.getInstructions().getFirst();
assertTrue(argument.isArgument());
- // The next one is a `const-number` instruction is not used for anything.
- // TODO(christofferqa): D8 should be able to get rid of the unused const-number instruction.
- Instruction unused = entryBlock.getInstructions().get(1);
- assertTrue(unused.isConstNumber());
- assertEquals(0, unused.outValue().numberOfAllUsers());
// The `return` instruction returns the argument.
Instruction ret = entryBlock.getInstructions().getLast();
assertTrue(ret.isReturn());
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/ifs/TrivialObjectEqualsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/ifs/TrivialObjectEqualsTest.java
new file mode 100644
index 0000000..5e5f50c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/ifs/TrivialObjectEqualsTest.java
@@ -0,0 +1,88 @@
+// Copyright (c) 2021, 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.ifs;
+
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethodWithName;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class TrivialObjectEqualsTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public TrivialObjectEqualsTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector ->
+ assertThat(
+ inspector.clazz(Main.class).uniqueMethodWithName("dead"),
+ not(invokesMethodWithName("dead"))))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector ->
+ assertThat(inspector.clazz(Main.class).uniqueMethodWithName("dead"), isAbsent()))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ Object o1 = new Object();
+ Object o2 = new Object();
+ if (o1 == o1) {
+ System.out.print("Hello");
+ } else {
+ dead();
+ }
+ if (o1 == o2) {
+ dead();
+ } else {
+ System.out.println(" world!");
+ }
+ }
+
+ @NeverInline
+ static void dead() {
+ System.out.println("Unexpected!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByConstantInOneConstructorTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByConstantInOneConstructorTest.java
index 8dcd7bc..0902756 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByConstantInOneConstructorTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByConstantInOneConstructorTest.java
@@ -51,11 +51,7 @@
ClassSubject testClassSubject = inspector.clazz(TestClass.class);
assertThat(testClassSubject, isPresent());
assertThat(testClassSubject.uniqueMethodWithName("live"), isPresent());
- if (parameters.isCfRuntime()) {
- assertThat(testClassSubject.uniqueMethodWithName("dead"), isPresent());
- } else {
- assertThat(testClassSubject.uniqueMethodWithName("dead"), not(isPresent()));
- }
+ assertThat(testClassSubject.uniqueMethodWithName("dead"), not(isPresent()));
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByDifferentConstantsInMultipleConstructorsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByDifferentConstantsInMultipleConstructorsTest.java
index 9bedfb1..c3a453d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByDifferentConstantsInMultipleConstructorsTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByDifferentConstantsInMultipleConstructorsTest.java
@@ -51,11 +51,7 @@
ClassSubject testClassSubject = inspector.clazz(TestClass.class);
assertThat(testClassSubject, isPresent());
assertThat(testClassSubject.uniqueMethodWithName("live"), isPresent());
- if (parameters.isCfRuntime()) {
- assertThat(testClassSubject.uniqueMethodWithName("dead"), isPresent());
- } else {
- assertThat(testClassSubject.uniqueMethodWithName("dead"), not(isPresent()));
- }
+ assertThat(testClassSubject.uniqueMethodWithName("dead"), not(isPresent()));
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/ObjectsToStringOnLibraryClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/ObjectsToStringOnLibraryClassTest.java
new file mode 100644
index 0000000..3e852df
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/ObjectsToStringOnLibraryClassTest.java
@@ -0,0 +1,100 @@
+// Copyright (c) 2021, 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.string;
+
+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.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.Objects;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ObjectsToStringOnLibraryClassTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public ObjectsToStringOnLibraryClassTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ MethodSubject mainMethodSubject = inspector.clazz(Main.class).mainMethod();
+ assertThat(mainMethodSubject, isPresent());
+ assertTrue(
+ mainMethodSubject
+ .streamInstructions()
+ .filter(InstructionSubject::isInvoke)
+ .allMatch(
+ x ->
+ x.getMethod()
+ .getName()
+ .toSourceString()
+ .equals("currentTimeMillis")));
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithEmptyOutput();
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ boolean unknown = System.currentTimeMillis() > 0;
+
+ // Boolean.
+ Objects.toString(unknown ? Boolean.FALSE : null);
+ Objects.toString(unknown ? Boolean.TRUE : null);
+
+ // Byte.
+ Objects.toString(unknown ? Byte.valueOf((byte) 0) : null);
+
+ // Char.
+ Objects.toString(unknown ? Character.valueOf((char) 0) : null);
+
+ // Double.
+ Objects.toString(unknown ? Double.valueOf(0) : null);
+
+ // Float.
+ Objects.toString(unknown ? Float.valueOf(0) : null);
+
+ // Integer.
+ Objects.toString(unknown ? Integer.valueOf(0) : null);
+
+ // Long.
+ Objects.toString(unknown ? Long.valueOf(0) : null);
+
+ // Short.
+ Objects.toString(unknown ? Short.valueOf((short) 0) : null);
+
+ // String.
+ Objects.toString(unknown ? "null" : null);
+ Objects.toString(unknown ? new String("null") : null);
+
+ // StringBuffer, StringBuilder.
+ Objects.toString(unknown ? new StringBuffer() : null);
+ Objects.toString(unknown ? new StringBuilder() : null);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithIfNullUserTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithIfNullUserTest.java
new file mode 100644
index 0000000..2d4b414
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithIfNullUserTest.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2021, 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.string;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class StringBuilderWithIfNullUserTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public StringBuilderWithIfNullUserTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(StringBuilderWithIfNullUserTest.class)
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Hello world!");
+ if (builder != null) {
+ System.out.println(builder.toString());
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithIfUserTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithIfUserTest.java
new file mode 100644
index 0000000..14ba197
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithIfUserTest.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2021, 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.string;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class StringBuilderWithIfUserTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public StringBuilderWithIfUserTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(StringBuilderWithIfUserTest.class)
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ StringBuilder builder = new StringBuilder();
+ StringBuilder other = System.currentTimeMillis() > 0 ? new StringBuilder() : builder;
+ builder.append("Hello world!");
+ if (builder != other) {
+ System.out.println(builder.toString());
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/UnusedStringBuilderWithAppendObjectTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/UnusedStringBuilderWithAppendObjectTest.java
index d8ed11c..b8c0799 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/UnusedStringBuilderWithAppendObjectTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/UnusedStringBuilderWithAppendObjectTest.java
@@ -5,13 +5,14 @@
package com.android.tools.r8.ir.optimize.string;
import static com.android.tools.r8.utils.codeinspector.CodeMatchers.instantiatesClass;
-import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.Objects;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -40,11 +41,7 @@
.inspect(
inspector -> {
MethodSubject mainMethod = inspector.clazz(Main.class).mainMethod();
- assertThat(
- mainMethod,
- notIf(
- instantiatesClass(StringBuilder.class),
- canUseJavaUtilObjects(parameters) || parameters.isDexRuntime()));
+ assertThat(mainMethod, not(instantiatesClass(StringBuilder.class)));
})
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithEmptyOutput();
@@ -55,6 +52,8 @@
public static void main(String[] args) {
A a = System.currentTimeMillis() > 0 ? new A() : null;
new StringBuilder().append(a).toString();
+ Objects.toString(new StringBuilder().append(a));
+ String.valueOf(new StringBuilder().append(a));
}
}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
index 6ef399e..52b60fe 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
@@ -358,6 +358,26 @@
.sorted()
.collect(Collectors.toList());
+ // Build main-dex list using D8 & rules.
+ List<String> mainDexListFromD8;
+ {
+ final Box<String> mainDexListOutputFromD8 = new Box<>();
+ testForD8(Backend.DEX)
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.K))
+ .addProgramFiles(inputJar)
+ .addProgramFiles(Paths.get(EXAMPLE_BUILD_DIR, "multidexfakeframeworks" + JAR_EXTENSION))
+ .addMainDexRulesFiles(mainDexRules)
+ .setMainDexListConsumer(ToolHelper.consumeString(mainDexListOutputFromD8::set))
+ .setMinApi(minSdk)
+ .allowStdoutMessages()
+ .compile();
+ mainDexListFromD8 =
+ StringUtils.splitLines(mainDexListOutputFromD8.get()).stream()
+ .map(this::mainDexStringToDescriptor)
+ .sorted()
+ .collect(Collectors.toList());
+ }
+
// Build main-dex list using R8.
final Box<String> r8MainDexListOutput = new Box<>();
testForR8(Backend.DEX)
@@ -402,6 +422,13 @@
}
String[] refList = new String(Files.readAllBytes(
expectedMainDexList), StandardCharsets.UTF_8).split("\n");
+ for (int i = 0; i < refList.length; i++) {
+ String reference = refList[i].trim();
+ if (mainDexListFromD8.size() <= i) {
+ fail("D8 main-dex list is missing '" + reference + "'");
+ }
+ checkSameMainDexEntry(reference, mainDexListFromD8.get(i));
+ }
int nonLambdaOffset = 0;
for (int i = 0; i < refList.length; i++) {
String reference = refList[i].trim();
diff --git a/src/test/java/com/android/tools/r8/movestringconstants/MoveStringConstantsTest.java b/src/test/java/com/android/tools/r8/movestringconstants/MoveStringConstantsTest.java
index d9b3cd4..61387ea 100644
--- a/src/test/java/com/android/tools/r8/movestringconstants/MoveStringConstantsTest.java
+++ b/src/test/java/com/android/tools/r8/movestringconstants/MoveStringConstantsTest.java
@@ -91,31 +91,37 @@
assumeTrue(backend == Backend.DEX);
validateSequence(
methodThrowToBeInlined.iterateInstructions(),
+
// 'if' with "foo#1" is flipped.
InstructionSubject::isIfEqz,
// 'if' with "foo#2" is removed along with the constant.
- // 'if' with "foo#3" is removed so now we have unconditional call.
- insn -> insn.isConstString("StringConstants::foo#3", JumboStringMode.DISALLOW),
- InstructionSubject::isInvokeStatic,
- InstructionSubject::isThrow,
+ // 'if' with "foo#3" is removed so now we have an unconditional call inside the branch.
+ InstructionSubject::isIfEq,
- // 'if's with "foo#4" and "foo#5" are flipped, but their throwing branches
- // are not moved to the end of the code (area for improvement?).
+ // 'if' with "foo#4" is flipped, but the throwing branch is not moved to the end of the code
+ // (area for improvement?).
insn -> insn.isConstString("StringConstants::foo#4", JumboStringMode.DISALLOW),
InstructionSubject::isIfEqz, // Flipped if
InstructionSubject::isGoto, // Jump around throwing branch.
InstructionSubject::isInvokeStatic, // Throwing branch.
InstructionSubject::isThrow,
+
+ // 'if's with "foo#5" are flipped.
insn -> insn.isConstString("StringConstants::foo#5", JumboStringMode.DISALLOW),
InstructionSubject::isIfEqz, // Flipped if
InstructionSubject::isReturnVoid, // Final return statement.
InstructionSubject::isInvokeStatic, // Throwing branch.
InstructionSubject::isThrow,
- // After 'if' with "foo#1" flipped, always throwing branch
- // moved here along with the constant.
+ // 'if' with "foo#3" is removed so now we have an unconditional call.
+ insn -> insn.isConstString("StringConstants::foo#3", JumboStringMode.DISALLOW),
+ InstructionSubject::isInvokeStatic,
+ InstructionSubject::isThrow,
+
+ // After 'if' with "foo#1" flipped, the always throwing branch is moved here along with the
+ // constant.
insn -> insn.isConstString("StringConstants::foo#1", JumboStringMode.DISALLOW),
InstructionSubject::isInvokeStatic,
InstructionSubject::isThrow);
diff --git a/src/test/java/com/android/tools/r8/naming/keeppackagenames/KeepPackageNameRootTest.java b/src/test/java/com/android/tools/r8/naming/keeppackagenames/KeepPackageNameRootTest.java
new file mode 100644
index 0000000..309ab55
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/keeppackagenames/KeepPackageNameRootTest.java
@@ -0,0 +1,74 @@
+// 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.naming.keeppackagenames;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.tools.r8.ProguardVersion;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompileResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestShrinkerBuilder;
+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 KeepPackageNameRootTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public KeepPackageNameRootTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8Compat() throws Exception {
+ run(testForR8Compat(Backend.CF));
+ }
+
+ @Test
+ public void testR8Full() throws Exception {
+ run(testForR8(Backend.CF));
+ }
+
+ @Test
+ public void testR8PG() throws Exception {
+ run(testForProguard(ProguardVersion.V7_0_0).addKeepRules("-dontwarn"));
+ }
+
+ private TestCompileResult<?, ?> run(TestShrinkerBuilder<?, ?, ?, ?, ?> testBuilder)
+ throws Exception {
+ return testBuilder
+ .addProgramClassFileData(
+ transformer(Main.class)
+ .setClassDescriptor("Lfoo/Main;")
+ .removeInnerClasses()
+ .transform())
+ .addKeepRules("-keeppackagenames foo.**")
+ .addKeepClassRulesWithAllowObfuscation("foo.Main")
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ assertEquals(1, inspector.allClasses().size());
+ inspector.forAllClasses(
+ clazz -> {
+ assertNotEquals("foo", clazz.getDexProgramClass().getType().getPackageName());
+ });
+ });
+ }
+
+ /* Will be in package foo */
+ public static class Main {}
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageCustomMethodHandleTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageCustomMethodHandleTest.java
new file mode 100644
index 0000000..f20603d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageCustomMethodHandleTest.java
@@ -0,0 +1,162 @@
+// Copyright (c) 2021, 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.repackage;
+
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.FLATTEN_PACKAGE_HIERARCHY;
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.transformers.ClassTransformer;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableList;
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+@RunWith(Parameterized.class)
+public class RepackageCustomMethodHandleTest extends RepackageTestBase {
+
+ private static final String EXPECTED = "InvokeCustom::foo";
+
+ @Parameters(name = "{1}, kind: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ ImmutableList.of(FLATTEN_PACKAGE_HIERARCHY, REPACKAGE_CLASSES),
+ getTestParameters()
+ .withCfRuntimes()
+ .withDexRuntimesStartingFromIncluding(Version.V8_1_0)
+ .withApiLevelsStartingAtIncluding(AndroidApiLevel.O_MR1)
+ .build());
+ }
+
+ public RepackageCustomMethodHandleTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(InvokeCustom.class)
+ .addProgramClassFileData(
+ transformer(Main.class).addClassTransformer(generateCallSiteInvoke()).transform())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test()
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(InvokeCustom.class)
+ .addProgramClassFileData(
+ transformer(Main.class).addClassTransformer(generateCallSiteInvoke()).transform())
+ .addKeepMainRule(Main.class)
+ .addKeepClassAndMembersRules(Main.class)
+ .apply(this::configureRepackaging)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(inspector -> assertThat(InvokeCustom.class, isRepackaged(inspector)))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ private ClassTransformer generateCallSiteInvoke() {
+ return new ClassTransformer() {
+ @Override
+ public void visit(
+ int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] interfaces) {
+ super.visit(version, access, name, signature, superName, interfaces);
+ MethodVisitor mv =
+ cv.visitMethod(
+ Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC,
+ "main",
+ "([Ljava/lang/String;)V",
+ null,
+ null);
+ MethodType mt =
+ MethodType.methodType(
+ CallSite.class,
+ MethodHandles.Lookup.class,
+ String.class,
+ MethodType.class,
+ MethodHandle.class);
+ Handle bootstrap =
+ new Handle(
+ Opcodes.H_INVOKESTATIC,
+ Type.getInternalName(InvokeCustom.class),
+ "createCallSite",
+ mt.toMethodDescriptorString(),
+ false);
+ mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class));
+ mv.visitInsn(Opcodes.DUP);
+ mv.visitMethodInsn(
+ Opcodes.INVOKESPECIAL,
+ Type.getInternalName(InvokeCustom.class),
+ "<init>",
+ "()V",
+ false);
+ mv.visitInvokeDynamicInsn(
+ "foo",
+ "(" + DescriptorUtils.javaTypeToDescriptor(InvokeCustom.class.getTypeName()) + ")V",
+ bootstrap,
+ new Handle(
+ Opcodes.H_INVOKEVIRTUAL,
+ Type.getInternalName(InvokeCustom.class),
+ "foo",
+ "()V",
+ false));
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(10, 10);
+ }
+ };
+ }
+
+ @NeverClassInline
+ public static class InvokeCustom {
+
+ @NeverInline
+ public void foo() {
+ System.out.println("InvokeCustom::foo");
+ }
+
+ public static CallSite createCallSite(
+ MethodHandles.Lookup caller, String name, MethodType type, MethodHandle mh) {
+ return new ConstantCallSite(mh);
+ }
+ }
+
+ public static class Main {
+
+ /*
+ public static void main(String[] args) {
+ CallSite cs = { InvokeCustom::foo, args: InvokeCustom }
+ cs.invoke(new InvokeCustom());
+ }
+ */
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/InvokeSuperCallInStaticTest.java b/src/test/java/com/android/tools/r8/resolution/InvokeSuperCallInStaticTest.java
index 8f4ec7b..74f834d 100644
--- a/src/test/java/com/android/tools/r8/resolution/InvokeSuperCallInStaticTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/InvokeSuperCallInStaticTest.java
@@ -18,7 +18,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ResolutionResult;
@@ -62,10 +62,10 @@
assertTrue(resolutionResult.isSingleResolution());
DexProgramClass context =
appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory()));
- DexEncodedMethod lookedUpMethod =
+ DexClassAndMethod lookedUpMethod =
resolutionResult.lookupInvokeSuperTarget(context, appView.appInfo());
assertNotNull(lookedUpMethod);
- assertEquals(lookedUpMethod.method, method);
+ assertEquals(lookedUpMethod.getReference(), method);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
index 6f14877..3f7568a 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
@@ -12,7 +12,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRunResult;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ResolutionResult;
@@ -131,13 +131,13 @@
assertEquals(
OptionalBool.of(inSameNest),
resolutionResult.isAccessibleFrom(callerClassDefinition, appInfo));
- DexEncodedMethod targetSpecial =
+ DexClassAndMethod targetSpecial =
resolutionResult.lookupInvokeSpecialTarget(callerClassDefinition, appInfo);
- DexEncodedMethod targetSuper =
+ DexClassAndMethod targetSuper =
resolutionResult.lookupInvokeSuperTarget(callerClassDefinition, appInfo);
if (inSameNest) {
assertEquals(definingClassDefinition.type, targetSpecial.getHolderType());
- assertEquals(targetSpecial, targetSuper);
+ assertEquals(targetSpecial.getReference(), targetSuper.getReference());
} else {
assertNull(targetSpecial);
assertNull(targetSuper);
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
index 6c7e5f2..5a3d01e 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
@@ -13,7 +13,7 @@
import com.android.tools.r8.TestRunResult;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ResolutionResult;
@@ -107,13 +107,13 @@
resolutionResult.isAccessibleFrom(callerClassDefinition, appInfo));
// Verify that looking up the dispatch target returns the defining method.
- DexEncodedMethod targetSpecial =
+ DexClassAndMethod targetSpecial =
resolutionResult.lookupInvokeSpecialTarget(callerClassDefinition, appInfo);
- DexEncodedMethod targetSuper =
+ DexClassAndMethod targetSuper =
resolutionResult.lookupInvokeSuperTarget(callerClassDefinition, appInfo);
if (inSameNest) {
assertEquals(definingClassDefinition.type, targetSpecial.getHolderType());
- assertEquals(targetSpecial, targetSuper);
+ assertEquals(targetSpecial.getReference(), targetSuper.getReference());
} else {
assertNull(targetSpecial);
assertNull(targetSuper);
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
index fe17f34..b291f91 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
@@ -5,7 +5,6 @@
import static com.android.tools.r8.TestRuntime.CfVm.JDK11;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -14,7 +13,7 @@
import com.android.tools.r8.TestRunResult;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ResolutionResult;
@@ -140,21 +139,16 @@
// Verify that looking up the dispatch target returns a valid target
// iff in the same nest and declaredHolder == definingHolder.
- DexEncodedMethod targetSpecial =
+ DexClassAndMethod targetSpecial =
resolutionResult.lookupInvokeSpecialTarget(callerClassDefinition, appInfo);
- DexEncodedMethod targetSuper =
+ DexClassAndMethod targetSuper =
resolutionResult.lookupInvokeSuperTarget(callerClassDefinition, appInfo);
- if (inSameNest && symbolicReferenceIsDefiningType) {
+ if (inSameNest) {
assertEquals(definingClassDefinition.type, targetSpecial.getHolderType());
- assertEquals(targetSpecial, targetSuper);
+ assertEquals(targetSpecial.getReference(), targetSuper.getReference());
} else {
assertNull(targetSpecial);
- if (!inSameNest) {
- assertNull(targetSuper);
- } else {
- // TODO(b/145775365): The current invoke-super will return the resolution target.
- assertNotNull(targetSuper);
- }
+ assertNull(targetSuper);
}
}
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java
index 9aa12b3..7d30885 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java
@@ -12,7 +12,7 @@
import com.android.tools.r8.TestRunResult;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ResolutionResult;
@@ -108,13 +108,13 @@
OptionalBool.TRUE, resolutionResult.isAccessibleFrom(callerClassDefinition, appInfo));
// Verify that looking up the dispatch target returns the defining method.
- DexEncodedMethod targetSpecial =
+ DexClassAndMethod targetSpecial =
resolutionResult.lookupInvokeSpecialTarget(callerClassDefinition, appInfo);
assertEquals(definingClassDefinition.type, targetSpecial.getHolderType());
- DexEncodedMethod targetSuper =
+ DexClassAndMethod targetSuper =
resolutionResult.lookupInvokeSuperTarget(callerClassDefinition, appInfo);
- assertEquals(targetSpecial, targetSuper);
+ assertEquals(targetSpecial.getReference(), targetSuper.getReference());
}
private void assertCallingClassCallsTarget(
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 2074afa..cd839f1 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
@@ -9,6 +9,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
@@ -17,6 +18,7 @@
import com.android.tools.r8.retrace.stacktraces.ActualRetraceBotStackTraceWithInfo;
import com.android.tools.r8.retrace.stacktraces.FoundMethodVerboseStackTrace;
import com.android.tools.r8.retrace.stacktraces.PGStackTrace;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.base.Charsets;
import java.io.ByteArrayOutputStream;
@@ -36,16 +38,30 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+@RunWith(Parameterized.class)
public class RetraceCommandLineTests {
- private static final boolean testExternal = false;
+ private static String SMILEY_EMOJI = "\uD83D\uDE00";
+
private static final String WAITING_MESSAGE =
"Waiting for stack-trace input..." + StringUtils.LINE_SEPARATOR;
@Rule public TemporaryFolder folder = new TemporaryFolder();
- private static String SMILEY_EMOJI = "\uD83D\uDE00";
+ private final boolean testExternal;
+
+ @Parameters(name = "{0}")
+ public static Boolean[] data() {
+ return BooleanUtils.values();
+ }
+
+ public RetraceCommandLineTests(boolean testExternal) {
+ this.testExternal = testExternal;
+ }
@Test
public void testPrintIdentityStackTraceFile() throws IOException {
@@ -233,11 +249,14 @@
private ProcessResult runRetraceCommandLine(File stdInput, Collection<String> args)
throws IOException {
if (testExternal) {
+ // The external dependency is built on top of R8Lib. If test.py is run with
+ // no r8lib, do not try and run the external R8 Retrace since it has not been built.
+ assumeTrue(Files.exists(ToolHelper.R8LIB_JAR));
List<String> command = new ArrayList<>();
command.add(ToolHelper.getSystemJavaExecutable());
command.add("-ea");
command.add("-cp");
- command.add(ToolHelper.R8_JAR.toString());
+ command.add(ToolHelper.R8_RETRACE_JAR.toString());
command.add("com.android.tools.r8.retrace.Retrace");
command.addAll(args);
ProcessBuilder builder = new ProcessBuilder(command);
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java
index 878a91d..4f79193 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java
@@ -297,7 +297,7 @@
@Override
public List<String> retracedStackTrace() {
- return Collections.singletonList("foo.Bar$Baz.baz(Bar.dummy)");
+ return Collections.singletonList("foo.Bar$Baz.baz(Bar.java)");
}
@Override
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 e8e09aa..94abc05 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -10,10 +10,13 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessagesImpl;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.retrace.internal.RetraceAbortException;
import com.android.tools.r8.retrace.stacktraces.ActualBotStackTraceBase;
import com.android.tools.r8.retrace.stacktraces.ActualIdentityStackTrace;
@@ -22,6 +25,7 @@
import com.android.tools.r8.retrace.stacktraces.AmbiguousStackTrace;
import com.android.tools.r8.retrace.stacktraces.AmbiguousWithMultipleLineMappingsStackTrace;
import com.android.tools.r8.retrace.stacktraces.AmbiguousWithSignatureNonVerboseStackTrace;
+import com.android.tools.r8.retrace.stacktraces.AutoStackTrace;
import com.android.tools.r8.retrace.stacktraces.CircularReferenceStackTrace;
import com.android.tools.r8.retrace.stacktraces.ColonInFileNameStackTrace;
import com.android.tools.r8.retrace.stacktraces.FileNameExtensionStackTrace;
@@ -46,7 +50,12 @@
import com.android.tools.r8.retrace.stacktraces.UnicodeInFileNameStackTrace;
import com.android.tools.r8.retrace.stacktraces.UnknownSourceStackTrace;
import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
@@ -59,44 +68,49 @@
@RunWith(Parameterized.class)
public class RetraceTests extends TestBase {
- @Parameters(name = "{0}, use regular expression: {1}")
+ @Parameters(name = "{0}, use regular expression: {1}, external: {2}")
public static Collection<Object[]> data() {
- return buildParameters(getTestParameters().withNoneRuntime().build(), BooleanUtils.values());
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(), BooleanUtils.values(), BooleanUtils.values());
}
+ private final TestParameters testParameters;
private final boolean useRegExpParsing;
+ private final boolean external;
- public RetraceTests(TestParameters parameters, boolean useRegExpParsing) {
+ public RetraceTests(TestParameters parameters, boolean useRegExpParsing, boolean external) {
+ this.testParameters = parameters;
this.useRegExpParsing = useRegExpParsing;
+ this.external = external;
}
@Test
- public void testCanMapExceptionClass() {
+ public void testCanMapExceptionClass() throws Exception {
runRetraceTest(new ObfucatedExceptionClassStackTrace());
}
@Test
- public void testSuppressedStackTrace() {
+ public void testSuppressedStackTrace() throws Exception {
runRetraceTest(new SuppressedStackTrace());
}
@Test
- public void testFileNameStackTrace() {
+ public void testFileNameStackTrace() throws Exception {
runRetraceTest(new FileNameExtensionStackTrace());
}
@Test
- public void testInlineFileNameStackTrace() {
+ public void testInlineFileNameStackTrace() throws Exception {
runRetraceTest(new InlineFileNameStackTrace());
}
@Test
- public void testInlineFileNameWithInnerClassesStackTrace() {
+ public void testInlineFileNameWithInnerClassesStackTrace() throws Exception {
runRetraceTest(new InlineFileNameWithInnerClassesStackTrace());
}
@Test
- public void testNoObfuscationRangeMappingWithStackTrace() {
+ public void testNoObfuscationRangeMappingWithStackTrace() throws Exception {
runRetraceTest(new NoObfuscationRangeMappingWithStackTrace());
}
@@ -123,18 +137,18 @@
}
@Test
- public void testInvalidStackTraceLineWarnings() {
+ public void testInvalidStackTraceLineWarnings() throws Exception {
InvalidStackTrace invalidStackTraceTest = new InvalidStackTrace();
runRetraceTest(invalidStackTraceTest).assertNoMessages();
}
@Test
- public void testAssertionErrorInRetrace() {
+ public void testAssertionErrorInRetrace() throws Exception {
runRetraceTest(new RetraceAssertionErrorStackTrace());
}
@Test
- public void testActualStackTraces() {
+ public void testActualStackTraces() throws Exception {
List<ActualBotStackTraceBase> stackTraces =
ImmutableList.of(new ActualIdentityStackTrace(), new ActualRetraceBotStackTrace());
for (ActualBotStackTraceBase stackTrace : stackTraces) {
@@ -144,37 +158,37 @@
}
@Test
- public void testAmbiguousStackTrace() {
+ public void testAmbiguousStackTrace() throws Exception {
runRetraceTest(new AmbiguousStackTrace());
}
@Test
- public void testAmbiguousMissingLineStackTrace() {
+ public void testAmbiguousMissingLineStackTrace() throws Exception {
runRetraceTest(new AmbiguousMissingLineStackTrace());
}
@Test
- public void testAmbiguousMissingLineNotVerbose() {
+ public void testAmbiguousMissingLineNotVerbose() throws Exception {
runRetraceTest(new AmbiguousWithSignatureNonVerboseStackTrace());
}
@Test
- public void testAmbiguousMultipleMappingsTest() {
+ public void testAmbiguousMultipleMappingsTest() throws Exception {
runRetraceTest(new AmbiguousWithMultipleLineMappingsStackTrace());
}
@Test
- public void testInliningWithLineNumbers() {
+ public void testInliningWithLineNumbers() throws Exception {
runRetraceTest(new InlineWithLineNumbersStackTrace());
}
@Test
- public void testInliningNoLineNumberInfoStackTraces() {
+ public void testInliningNoLineNumberInfoStackTraces() throws Exception {
runRetraceTest(new InlineNoLineNumberStackTrace());
}
@Test
- public void testCircularReferenceStackTrace() {
+ public void testCircularReferenceStackTrace() throws Exception {
// Proguard retrace (and therefore the default regular expression) will not retrace circular
// reference exceptions.
assumeFalse(useRegExpParsing);
@@ -182,76 +196,116 @@
}
@Test
- public void testObfuscatedRangeToSingleLine() {
+ public void testObfuscatedRangeToSingleLine() throws Exception {
runRetraceTest(new ObfuscatedRangeToSingleLineStackTrace());
}
@Test
@Ignore("b/170293908")
- public void testBootLoaderAndNamedModulesStackTrace() {
+ public void testBootLoaderAndNamedModulesStackTrace() throws Exception {
assumeFalse(useRegExpParsing);
runRetraceTest(new NamedModuleStackTrace());
}
@Test
- public void testUnknownSourceStackTrace() {
+ public void testUnknownSourceStackTrace() throws Exception {
runRetraceTest(new UnknownSourceStackTrace());
}
@Test
- public void testInlineSourceFileContext() {
+ public void testInlineSourceFileContext() throws Exception {
runRetraceTest(new InlineSourceFileContextStackTrace());
}
@Test
- public void testColonInSourceFileNameStackTrace() {
+ public void testColonInSourceFileNameStackTrace() throws Exception {
runRetraceTest(new ColonInFileNameStackTrace());
}
@Test
- public void testMultipleDotsInFileNameStackTrace() {
+ public void testMultipleDotsInFileNameStackTrace() throws Exception {
runRetraceTest(new MultipleDotsInFileNameStackTrace());
}
@Test
- public void testUnicodeInFileNameStackTrace() {
+ public void testUnicodeInFileNameStackTrace() throws Exception {
runRetraceTest(new UnicodeInFileNameStackTrace());
}
@Test
- public void testMemberFieldOverlapStackTrace() {
+ public void testMemberFieldOverlapStackTrace() throws Exception {
MemberFieldOverlapStackTrace stackTraceForTest = new MemberFieldOverlapStackTrace();
runRetraceTest(stackTraceForTest);
inspectRetraceTest(stackTraceForTest, stackTraceForTest::inspectField);
}
@Test
- public void testSourceFileWithNumberAndEmptyStackTrace() {
+ public void testSourceFileWithNumberAndEmptyStackTrace() throws Exception {
runRetraceTest(new SourceFileWithNumberAndEmptyStackTrace());
}
@Test
- public void testSourceFileNameSynthesizeStackTrace() {
+ public void testSourceFileNameSynthesizeStackTrace() throws Exception {
runRetraceTest(new SourceFileNameSynthesizeStackTrace());
}
+ @Test
+ public void testAutoStackTrace() throws Exception {
+ runRetraceTest(new AutoStackTrace());
+ }
+
private void inspectRetraceTest(
StackTraceForTest stackTraceForTest, Consumer<Retracer> inspection) {
inspection.accept(
Retracer.createDefault(stackTraceForTest::mapping, new TestDiagnosticMessagesImpl()));
}
- private TestDiagnosticMessagesImpl runRetraceTest(StackTraceForTest stackTraceForTest) {
- TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
- RetraceCommand retraceCommand =
- RetraceCommand.builder(diagnosticsHandler)
- .setProguardMapProducer(stackTraceForTest::mapping)
- .setStackTrace(stackTraceForTest.obfuscatedStackTrace())
- .setRegularExpression(useRegExpParsing ? DEFAULT_REGULAR_EXPRESSION : null)
- .setRetracedStackTraceConsumer(
- retraced -> assertEquals(stackTraceForTest.retracedStackTrace(), retraced))
- .build();
- Retrace.run(retraceCommand);
- return diagnosticsHandler;
+ private TestDiagnosticMessagesImpl runRetraceTest(StackTraceForTest stackTraceForTest)
+ throws Exception {
+ if (external) {
+ assumeTrue(useRegExpParsing);
+ assumeTrue(testParameters.isCfRuntime());
+ // The external dependency is built on top of R8Lib. If test.py is run with
+ // no r8lib, do not try and run the external R8 Retrace since it has not been built.
+ assumeTrue(Files.exists(ToolHelper.R8LIB_JAR));
+ Path path = temp.newFolder().toPath();
+ Path mappingFile = path.resolve("mapping");
+ Files.write(mappingFile, stackTraceForTest.mapping().getBytes());
+ Path stackTraceFile = path.resolve("stacktrace.txt");
+ Files.write(
+ stackTraceFile,
+ StringUtils.joinLines(stackTraceForTest.obfuscatedStackTrace())
+ .getBytes(StandardCharsets.UTF_8));
+
+ List<String> command = new ArrayList<>();
+ command.add(testParameters.getRuntime().asCf().getJavaExecutable().toString());
+ command.add("-ea");
+ command.add("-cp");
+ command.add(ToolHelper.R8_RETRACE_JAR.toString());
+ command.add("com.android.tools.r8.retrace.Retrace");
+ command.add(mappingFile.toString());
+ command.add(stackTraceFile.toString());
+ command.add("-quiet");
+ ProcessBuilder builder = new ProcessBuilder(command);
+ ProcessResult processResult = ToolHelper.runProcess(builder);
+ assertEquals(
+ StringUtils.joinLines(stackTraceForTest.retracedStackTrace())
+ + StringUtils.LINE_SEPARATOR,
+ processResult.stdout);
+ // TODO(b/177204438): Parse diagnostics from stdErr
+ return new TestDiagnosticMessagesImpl();
+ } else {
+ TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
+ RetraceCommand retraceCommand =
+ RetraceCommand.builder(diagnosticsHandler)
+ .setProguardMapProducer(stackTraceForTest::mapping)
+ .setStackTrace(stackTraceForTest.obfuscatedStackTrace())
+ .setRegularExpression(useRegExpParsing ? DEFAULT_REGULAR_EXPRESSION : null)
+ .setRetracedStackTraceConsumer(
+ retraced -> assertEquals(stackTraceForTest.retracedStackTrace(), retraced))
+ .build();
+ Retrace.run(retraceCommand);
+ return diagnosticsHandler;
+ }
}
}
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/AutoStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/AutoStackTrace.java
new file mode 100644
index 0000000..0504b58
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/AutoStackTrace.java
@@ -0,0 +1,41 @@
+// Copyright (c) 2021, 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 com.google.common.collect.ImmutableList;
+import java.util.List;
+
+public class AutoStackTrace implements StackTraceForTest {
+
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return ImmutableList.of(
+ "java.io.IOException: INVALID_SENDER",
+ "\tat qtr.a(:com.google.android.gms@203915081@20.39.15 (060808-335085812):46)",
+ "\tat qtr.a(:com.google.android.gms@203915081@20.39.15 (060808-335085812):18)");
+ }
+
+ @Override
+ public String mapping() {
+ return StringUtils.lines(
+ "com.android.tools.r8.AutoTest -> qtr:",
+ " 46:46:void foo(int):200:200 -> a",
+ " 17:19:void foo(int,int):23:25 -> a");
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return ImmutableList.of(
+ "java.io.IOException: INVALID_SENDER",
+ "\tat com.android.tools.r8.AutoTest.foo(AutoTest.java:200)",
+ "\tat com.android.tools.r8.AutoTest.foo(AutoTest.java:24)");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 0;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/FileNameExtensionStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/FileNameExtensionStackTrace.java
index ae9e0ff..dc77ca6 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/FileNameExtensionStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/FileNameExtensionStackTrace.java
@@ -41,7 +41,7 @@
" at R8.main(Native Method)",
" at R8.main(R8.java:)",
" at R8.main(R8.kt:1)",
- " at R8.main(R8.foo)",
+ " at R8.main(R8.java)",
" at R8.main(R8.java)",
" at R8.main(R8.java)",
" at R8.main(R8.java)",
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineFileNameStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineFileNameStackTrace.java
index 65f9824..ebc23a1 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineFileNameStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineFileNameStackTrace.java
@@ -21,10 +21,10 @@
public List<String> retracedStackTrace() {
return Arrays.asList(
"Exception in thread \"main\" java.lang.NullPointerException",
- "\tat foo.Bar$Baz.baz(Bar.dummy:0)",
- "\tat Foo$Bar.bar(Foo.dummy:2)",
- "\tat com.android.tools.r8.naming.retrace.Main$Foo.method1(Main.dummy:8)",
- "\tat com.android.tools.r8.naming.retrace.Main.main(Main.dummy:7)");
+ "\tat foo.Bar$Baz.baz(Bar.java:0)",
+ "\tat Foo$Bar.bar(Foo.java:2)",
+ "\tat com.android.tools.r8.naming.retrace.Main$Foo.method1(Main.java:8)",
+ "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java:7)");
}
@Override
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineFileNameWithInnerClassesStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineFileNameWithInnerClassesStackTrace.java
index 20ed6ec..8d7551a 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineFileNameWithInnerClassesStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineFileNameWithInnerClassesStackTrace.java
@@ -21,8 +21,8 @@
public List<String> retracedStackTrace() {
return Arrays.asList(
"Exception in thread \"main\" java.lang.NullPointerException",
- "\tat foo.Bar$Baz$Qux.baz(Bar.dummy:0)",
- "\tat com.android.tools.r8.naming.retrace.Main.main(Main.dummy:7)");
+ "\tat foo.Bar$Baz$Qux.baz(Bar.java:0)",
+ "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java:7)");
}
@Override
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/NoObfuscationRangeMappingWithStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/NoObfuscationRangeMappingWithStackTrace.java
index 4057a91..3f944e2 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/NoObfuscationRangeMappingWithStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/NoObfuscationRangeMappingWithStackTrace.java
@@ -24,10 +24,10 @@
public List<String> retracedStackTrace() {
return Arrays.asList(
"Exception in thread \"main\" java.lang.NullPointerException",
- "\tat com.android.tools.r8.naming.retrace.Main.foo(Main.dummy:1)",
- "\tat com.android.tools.r8.naming.retrace.Main.bar(Main.dummy:3)",
- "\tat com.android.tools.r8.naming.retrace.Main.baz(Main.dummy:8)",
- "\tat com.android.tools.r8.naming.retrace.Main.main(Main.dummy:7)");
+ "\tat com.android.tools.r8.naming.retrace.Main.foo(Main.java:1)",
+ "\tat com.android.tools.r8.naming.retrace.Main.bar(Main.java:3)",
+ "\tat com.android.tools.r8.naming.retrace.Main.baz(Main.java:8)",
+ "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java:7)");
}
@Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
index f81dffd..37f26db 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
@@ -94,6 +95,11 @@
}
@Override
+ public MethodAccessFlags getAccessFlags() {
+ throw new Unreachable("Cannot get the access flags for an absent method");
+ }
+
+ @Override
public DexEncodedMethod getMethod() {
return null;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
index 4e6a3f5..3a9976f 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
@@ -196,6 +196,11 @@
}
@Override
+ public boolean isIfEq() {
+ return instruction instanceof CfIf && ((CfIf) instruction).getOpcode() == Opcodes.IF_ICMPEQ;
+ }
+
+ @Override
public boolean isIfEqz() {
return instruction instanceof CfIf && ((CfIf) instruction).getOpcode() == Opcodes.IFEQ;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
index 26944f9..4b9f733 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
@@ -316,6 +316,11 @@
}
@Override
+ public boolean isIfEq() {
+ return instruction instanceof IfEq;
+ }
+
+ @Override
public boolean isIfEqz() {
return instruction instanceof IfEqz;
}
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 4d5e0d3..c2ae3ab 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
@@ -26,6 +26,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.naming.MemberNaming;
@@ -142,6 +143,11 @@
}
@Override
+ public MethodAccessFlags getAccessFlags() {
+ return dexMethod.getAccessFlags();
+ }
+
+ @Override
public DexEncodedMethod getMethod() {
return dexMethod;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
index e6e26fd..56436e9 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
@@ -75,6 +75,8 @@
boolean isIfNez();
+ boolean isIfEq();
+
boolean isIfEqz();
boolean isReturn();
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
index c6a63e2..4826876 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.utils.codeinspector;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
@@ -37,6 +38,8 @@
return null;
}
+ public abstract MethodAccessFlags getAccessFlags();
+
@Override
public abstract MethodSignature getOriginalSignature();
diff --git a/tests/r8_api_usage_sample.jar b/tests/r8_api_usage_sample.jar
index c50e110..65070f5 100644
--- a/tests/r8_api_usage_sample.jar
+++ b/tests/r8_api_usage_sample.jar
Binary files differ
diff --git a/tools/archive.py b/tools/archive.py
index 3bf5514..b7ca7c0 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -158,7 +158,7 @@
is_master = IsMaster(version)
if is_master:
# On master we use the git hash to archive with
- print 'On master, using git hash for archiving'
+ print('On master, using git hash for archiving')
version = GetGitHash()
destination = GetVersionDestination('gs://', version, is_master)
diff --git a/tools/compiledump.py b/tools/compiledump.py
index 29fed2c..b5f49d1 100755
--- a/tools/compiledump.py
+++ b/tools/compiledump.py
@@ -106,7 +106,7 @@
return parser
def error(msg):
- print msg
+ print(msg)
sys.exit(1)
class Dump(object):
@@ -145,11 +145,11 @@
def proguard_input_map(self):
if self.if_exists('proguard_input.config'):
- print "Unimplemented: proguard_input configuration."
+ print("Unimplemented: proguard_input configuration.")
def main_dex_resource(self):
if self.if_exists('main-dex-list.txt'):
- print "Unimplemented: main-dex-list."
+ print("Unimplemented: main-dex-list.")
def build_properties_file(self):
return self.if_exists('build.properties')
@@ -270,7 +270,7 @@
if not dump.program_jar():
error("Cannot compile dump with no program classes")
if not dump.library_jar():
- print "WARNING: Unexpected lack of library classes in dump"
+ print("WARNING: Unexpected lack of library classes in dump")
build_properties = determine_build_properties(args, dump)
version = determine_version(args, dump)
compiler = determine_compiler(args, dump)
@@ -284,7 +284,7 @@
cmd = [jdk.GetJavaExecutable()]
if args.debug_agent:
if not args.nolib:
- print "WARNING: Running debugging agent on r8lib is questionable..."
+ print("WARNING: Running debugging agent on r8lib is questionable...")
cmd.append(
'-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005')
if args.xmx:
@@ -329,18 +329,18 @@
cmd.extend(otherargs)
utils.PrintCmd(cmd)
try:
- print subprocess.check_output(cmd, stderr=subprocess.STDOUT)
+ print(subprocess.check_output(cmd, stderr=subprocess.STDOUT))
return 0
- except subprocess.CalledProcessError, e:
- print e.output
+ except subprocess.CalledProcessError as e:
+ print(e.output)
if not args.nolib and version != 'source':
stacktrace = os.path.join(temp, 'stacktrace')
open(stacktrace, 'w+').write(e.output)
local_map = utils.R8LIB_MAP if version == 'master' else None
hash_or_version = None if version == 'master' else version
- print "=" * 80
- print " RETRACED OUTPUT"
- print "=" * 80
+ print("=" * 80)
+ print(" RETRACED OUTPUT")
+ print("=" * 80)
retrace.run(
local_map, hash_or_version, stacktrace, is_hash(version), no_r8lib=False)
return 1
diff --git a/tools/gradle.py b/tools/gradle.py
index a52e3f3..b6f5187 100755
--- a/tools/gradle.py
+++ b/tools/gradle.py
@@ -47,7 +47,7 @@
def PrintCmd(s):
if type(s) is list:
s = ' '.join(s)
- print 'Running: %s' % s
+ print('Running: %s' % s)
# I know this will hit os on windows eventually if we don't do this.
sys.stdout.flush()
diff --git a/tools/jdk.py b/tools/jdk.py
index bc40873..7138fda 100755
--- a/tools/jdk.py
+++ b/tools/jdk.py
@@ -40,7 +40,7 @@
return os.path.join(jdkHome, 'bin', executable) if jdkHome else executable
def Main():
- print GetJdkHome()
+ print(GetJdkHome())
if __name__ == '__main__':
sys.exit(Main())
diff --git a/tools/bisect.py b/tools/r8bisect.py
similarity index 100%
rename from tools/bisect.py
rename to tools/r8bisect.py
diff --git a/tools/retrace.py b/tools/retrace.py
index 41b5a06..b0a1ec8 100755
--- a/tools/retrace.py
+++ b/tools/retrace.py
@@ -107,6 +107,9 @@
map_path
]
+ if quiet:
+ retrace_args.append('--quiet')
+
if stacktrace:
retrace_args.append(stacktrace)
diff --git a/tools/run_on_app_dump.py b/tools/run_on_app_dump.py
index 1461181..4509656 100755
--- a/tools/run_on_app_dump.py
+++ b/tools/run_on_app_dump.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# 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.
@@ -221,15 +221,6 @@
'revision': '94342117097770ea3ca2c6df6ab496a1a55c3ce7',
'folder': 'rover-android',
}),
- App({
- 'id': 'io.rover.app.debug',
- 'name': 'Rover',
- 'dump_app': 'dump_app.zip',
- 'apk_app': 'example-app-release-unsigned.apk',
- 'url': 'https://github.com/RoverPlatform/rover-android',
- 'revision': '94342117097770ea3ca2c6df6ab496a1a55c3ce7',
- 'folder': 'rover-android',
- }),
# TODO(b/172808159): Monkey runner does not work
App({
'id': 'com.google.android.apps.santatracker',
diff --git a/tools/test.py b/tools/test.py
index cf14685..dffc4565 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -253,6 +253,7 @@
# Force gradle to build a version of r8lib without dependencies for
# BootstrapCurrentEqualityTest.
gradle_args.append('R8LibNoDeps')
+ gradle_args.append('R8Retrace')
if options.r8lib_no_deps:
gradle_args.append('-Pr8lib_no_deps')
if options.worktree:
diff --git a/tools/update_prebuilds_in_android.py b/tools/update_prebuilds_in_android.py
index 22c8863..fe8a3c0 100755
--- a/tools/update_prebuilds_in_android.py
+++ b/tools/update_prebuilds_in_android.py
@@ -53,14 +53,14 @@
src = os.path.join(root, srcs[i])
dest = os.path.join(target_root, 'prebuilts', 'r8', dests[i])
if os.path.exists(dest):
- print 'Copying: ' + src + ' -> ' + dest
+ print('Copying: ' + src + ' -> ' + dest)
copyfile(src, dest)
if maps:
- print 'Copying: ' + src + '.map -> ' + dest + '.map'
+ print('Copying: ' + src + '.map -> ' + dest + '.map')
copyfile(src + '.map', dest + '.map')
else:
- print ('WARNING: Not copying ' + src + ' -> ' + dest +
- ', as' + dest + ' does not exist already')
+ print('WARNING: Not copying ' + src + ' -> ' + dest +
+ ', as' + dest + ' does not exist already')
def copy_jar_targets(root, target_root, jar_targets, maps):
srcs = map((lambda t: t[0] + '.jar'), jar_targets)
@@ -83,7 +83,7 @@
target,
is_hash)
if not quiet:
- print 'Downloading: ' + url + ' -> ' + download_path
+ print('Downloading: ' + url + ' -> ' + download_path)
utils.download_file_from_cloud_storage(url, download_path, quiet=quiet)
def main_download(hash, maps, targets, target_root, version):
diff --git a/tools/utils.py b/tools/utils.py
index bed28d8..351a038 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -222,7 +222,7 @@
# there is an update.
os.utime(tgz, None)
else:
- print 'Ensure cloud dependency:', msg, 'present'
+ print('Ensure cloud dependency:', msg, 'present')
def DownloadFromX20(sha1_file):
download_script = os.path.join(REPO_ROOT, 'tools', 'download_from_x20.py')
@@ -336,7 +336,7 @@
def unpack_archive(filename):
dest_dir = extract_dir(filename)
if os.path.exists(dest_dir):
- print 'Deleting existing dir %s' % dest_dir
+ print('Deleting existing dir %s' % dest_dir)
shutil.rmtree(dest_dir)
dirname = os.path.dirname(os.path.abspath(filename))
with tarfile.open(filename, 'r:gz') as tar:
@@ -376,12 +376,12 @@
def __enter__(self):
self._old_cwd = os.getcwd()
if not self._quiet:
- print 'Enter directory:', self._working_directory
+ print('Enter directory:', self._working_directory)
os.chdir(self._working_directory)
def __exit__(self, *_):
if not self._quiet:
- print 'Enter directory:', self._old_cwd
+ print('Enter directory:', self._old_cwd)
os.chdir(self._old_cwd)
# Reading Android CTS test_result.xml