Merge commit '8e2f68f2b3881569a1f8fce97661853e72712928' into dev-release
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs.json b/src/library_desugar/jdk11/desugar_jdk_libs.json
index 41ac1e6..c2cea80 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs.json
@@ -54,9 +54,11 @@
{
"api_level_below_or_equal": 23,
"rewrite_prefix": {
+ "java.io.DesugarBufferedReader": "j$.io.DesugarBufferedReader",
"java.util.DoubleSummaryStatistics": "j$.util.DoubleSummaryStatistics",
"java.util.IntSummaryStatistics": "j$.util.IntSummaryStatistics",
"java.util.LongSummaryStatistics": "j$.util.LongSummaryStatistics",
+ "java.util.Objects": "j$.util.Objects",
"java.util.Optional": "j$.util.Optional",
"java.util.PrimitiveIterator": "j$.util.PrimitiveIterator",
"java.util.Spliterator": "j$.util.Spliterator",
@@ -67,6 +69,9 @@
"java.util.function.": "j$.util.function.",
"java.util.stream.": "j$.util.stream."
},
+ "maintain_prefix": [
+ "java.io.UncheckedIOException"
+ ],
"emulate_interface": {
"java.lang.Iterable": "j$.lang.Iterable",
"java.util.Collection": "j$.util.Collection",
@@ -101,6 +106,7 @@
"java.util.stream.Stream java.util.Arrays#stream(java.lang.Object[], int, int)": "java.util.DesugarArrays"
},
"retarget_method_with_emulated_dispatch": {
+ "java.util.stream.Stream java.io.BufferedReader#lines()": "java.io.DesugarBufferedReader",
"java.util.Spliterator java.util.LinkedHashSet#spliterator()": "java.util.DesugarLinkedHashSet"
},
"wrapper_conversion": [
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_minimal.json b/src/library_desugar/jdk11/desugar_jdk_libs_minimal.json
new file mode 100644
index 0000000..73fa330
--- /dev/null
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_minimal.json
@@ -0,0 +1,24 @@
+{
+ "identifier": "com.tools.android:desugar_jdk_libs_minimal:2.0.0",
+ "configuration_format_version": 100,
+ "required_compilation_api_level": 24,
+ "synthesized_library_classes_package_prefix": "j$.",
+ "support_all_callbacks_from_library": false,
+ "common_flags": [
+ {
+ "api_level_below_or_equal": 23,
+ "maintain_prefix": [
+ "java.util.function.",
+ "java.util.Optional"
+ ]
+ }
+ ],
+ "program_flags": [],
+ "library_flags": [],
+ "shrinker_config": [
+ "-keeppackagenames java.**",
+ "-keepattributes Signature",
+ "-keepattributes EnclosingMethod",
+ "-keepattributes InnerClasses"
+ ]
+}
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index c953853..acbd677 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -579,9 +579,7 @@
HorizontalClassMergerOptions horizontalClassMergerOptions =
internal.horizontalClassMergerOptions();
if (internal.isGeneratingDex()) {
- // TODO(b/227791663): Disable until fixed.
- horizontalClassMergerOptions.disable();
- // horizontalClassMergerOptions.setRestrictToSynthetics();
+ horizontalClassMergerOptions.setRestrictToSynthetics();
} else {
assert internal.isGeneratingClassFiles();
horizontalClassMergerOptions.disable();
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 1c8ba99..4e89130 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.dex.Marker.Tool;
import com.android.tools.r8.errors.CheckDiscardDiagnostic;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
+import com.android.tools.r8.experimental.startup.StartupInstrumentation;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppServices;
import com.android.tools.r8.graph.AppView;
@@ -443,6 +444,8 @@
assert appView.appInfo().hasLiveness();
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+ new StartupInstrumentation(appView).instrumentClasses(executorService);
+
assert verifyNoJarApplicationReaders(appView.appInfo().classes());
assert appView.checkForTesting(() -> allReferencesAssignedApiLevel(appViewWithLiveness));
// Build conservative main dex content after first round of tree shaking. This is used
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index f259f26..888b78b 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -945,8 +945,10 @@
internal.featureSplitConfiguration = featureSplitConfiguration;
- internal.startupConfiguration =
- StartupConfiguration.createStartupConfiguration(getDexItemFactory(), getReporter());
+ internal
+ .getStartupOptions()
+ .setStartupConfiguration(
+ StartupConfiguration.createStartupConfiguration(getDexItemFactory(), getReporter()));
internal.syntheticProguardRulesConsumer = syntheticProguardRulesConsumer;
@@ -983,6 +985,8 @@
internal.setDesugaredLibrarySpecification(desugaredLibrarySpecification);
internal.synthesizedClassPrefix = synthesizedClassPrefix;
+ // TODO(b/214382176): Enable all the time.
+ internal.loadAllClassDefinitions = !synthesizedClassPrefix.isEmpty();
internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
// Set up the map and source file providers.
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java
index 0d466f0..c81abe4 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java
@@ -174,16 +174,6 @@
for (CfTryCatch tryCatchRange : tryCatchRanges) {
if (tryCatchRange.start == label) {
currentCatchRanges.add(tryCatchRange);
- // We can have fall-through into this range requiring the current frame being
- // assignable to the handler frame. This is handled for locals when we see a throwing
- // instruction, but we can validate here that the stack will be a single element stack
- // [throwable].
- CfFrame destinationFrame = stateMap.get(tryCatchRange.start);
- if (destinationFrame == null) {
- throw CfCodeStackMapValidatingException.error("No frame for target catch range target");
- }
- checkStackIsAssignable(
- destinationFrame.getStack(), throwStack, factory, isJavaAssignable);
}
}
currentCatchRanges.removeIf(currentRange -> currentRange.end == label);
@@ -191,6 +181,35 @@
return this;
}
+ public void checkTryCatchRange(CfTryCatch tryCatchRange) {
+ // According to the spec:
+ // https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.10.1
+ // saying ` and the handler's target (the initial instruction of the handler code) is type
+ // safe assuming an incoming type state T. The type state T is derived from ExcStackFrame
+ // by replacing the operand stack with a stack whose sole element is the handler's
+ // exception class.
+ tryCatchRange.targets.forEach(
+ target -> {
+ CfFrame destinationFrame = stateMap.get(target);
+ if (destinationFrame == null) {
+ throw CfCodeStackMapValidatingException.error("No frame for target catch range target");
+ }
+ // From the spec: the handler's exception class is assignable to the class Throwable.
+ tryCatchRange.guards.forEach(
+ guard -> {
+ if (!isJavaAssignable.test(guard, factory.throwableType)) {
+ throw CfCodeStackMapValidatingException.error(
+ "Could not assign '" + guard.toSourceString() + "' to throwable.");
+ }
+ checkStackIsAssignable(
+ ImmutableDeque.of(FrameType.initialized(guard)),
+ destinationFrame.getStack(),
+ factory,
+ isJavaAssignable);
+ });
+ });
+ }
+
private void checkFrameIsSet() {
if (currentFrame == NO_FRAME) {
throw CfCodeStackMapValidatingException.error("Unexpected state change");
@@ -218,9 +237,6 @@
if (destinationFrame == null) {
throw CfCodeStackMapValidatingException.error("No frame for target catch range target");
}
- // We have to check all current handler targets have assignable locals and a 1-element
- // stack assignable to throwable. It is not required that the the thrown error is
- // handled.
checkLocalsIsAssignable(
currentFrame.getLocals(), destinationFrame.getLocals(), factory, isJavaAssignable);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLabel.java b/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
index 9e3b1ee..3df1eba 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
@@ -100,6 +100,6 @@
DexMethod context,
AppView<?> appView,
DexItemFactory dexItemFactory) {
- // This is a no-op.
+ frameBuilder.seenLabel(this);
}
}
diff --git a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
index 7bfd947..6b77d5b 100644
--- a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
+++ b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.CollectionUtils;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -66,17 +67,26 @@
this.options = options;
}
- private boolean shouldKeep(DexType type) {
- return namingLens.prefixRewrittenType(type) != null
- || options.machineDesugaredLibrarySpecification.getMaintainType().contains(type)
- || options.machineDesugaredLibrarySpecification.isCustomConversionRewrittenType(type)
- || options.machineDesugaredLibrarySpecification.isEmulatedInterfaceRewrittenType(type)
+ private boolean shouldKeep(DexType givenType) {
+ if (namingLens.prefixRewrittenType(givenType) != null
+ || options.machineDesugaredLibrarySpecification.isCustomConversionRewrittenType(givenType)
+ || options.machineDesugaredLibrarySpecification.isEmulatedInterfaceRewrittenType(
+ givenType)
// TODO(b/158632510): This should prefix match on DexString.
- || type.toDescriptorString()
+ || givenType
+ .toDescriptorString()
.startsWith(
"L"
+ options.machineDesugaredLibrarySpecification
- .getSynthesizedLibraryClassesPackagePrefix());
+ .getSynthesizedLibraryClassesPackagePrefix())) {
+ return true;
+ }
+ DexType type =
+ InterfaceDesugaringSyntheticHelper.isCompanionClassType(givenType)
+ ? InterfaceDesugaringSyntheticHelper.getInterfaceClassType(
+ givenType, options.dexItemFactory())
+ : givenType;
+ return options.machineDesugaredLibrarySpecification.getMaintainType().contains(type);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java b/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
index 92671f2..9978311 100644
--- a/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
+++ b/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.IntBox;
import com.android.tools.r8.utils.ThreadUtils;
import com.google.common.collect.Maps;
import java.util.ArrayList;
@@ -279,26 +280,29 @@
}
private final VirtualFile mainDex;
- private final List<VirtualFile> dexes;
+ private final List<VirtualFile> files;
+ private final List<VirtualFile> filesForDistribution;
private final BitSet fullDex = new BitSet();
private final Set<DexProgramClass> classes;
private final AppView<?> appView;
- private final int dexIndexOffset;
+ private final IntBox nextFileId;
private final NamingLens namingLens;
private final DirectSubClassesInfo directSubClasses;
public InheritanceClassInDexDistributor(
VirtualFile mainDex,
- List<VirtualFile> dexes,
+ List<VirtualFile> files,
+ List<VirtualFile> filesForDistribution,
Set<DexProgramClass> classes,
- int dexIndexOffset,
+ IntBox nextFileId,
NamingLens namingLens,
AppView<?> appView,
ExecutorService executorService) {
this.mainDex = mainDex;
- this.dexes = dexes;
+ this.files = files;
+ this.filesForDistribution = filesForDistribution;
this.classes = classes;
- this.dexIndexOffset = dexIndexOffset;
+ this.nextFileId = nextFileId;
this.namingLens = namingLens;
this.appView = appView;
this.executorService = executorService;
@@ -315,6 +319,8 @@
// Allocate member of groups depending on
// the main dex members
+ VirtualFileCycler cycler =
+ new VirtualFileCycler(files, filesForDistribution, appView, namingLens, nextFileId);
for (Iterator<ClassGroup> iter = remainingInheritanceGroups.iterator(); iter.hasNext();) {
ClassGroup group = iter.next();
if (group.dependsOnMainDexClasses) {
@@ -344,19 +350,19 @@
new ClassGroup(groupSplit.mainDexIndependents);
Collection<VirtualFile> mainDexInpendentsDexes =
- assignGroup(mainDexIndependentGroup, Collections.singletonList(mainDex));
+ assignGroup(mainDexIndependentGroup, cycler, Collections.singletonList(mainDex));
Set<DexProgramClass> classesWithLinkingError =
new HashSet<>(groupSplit.dependentsOfMainDexIndependents);
classesWithLinkingError.addAll(classesMissingMainDex);
- assignClassesWithLinkingError(classesWithLinkingError, mainDexInpendentsDexes);
+ assignClassesWithLinkingError(classesWithLinkingError, cycler, mainDexInpendentsDexes);
}
}
// Allocate member of groups independents from the main dex members
for (ClassGroup group : remainingInheritanceGroups) {
if (!group.dependsOnMainDexClasses) {
- assignGroup(group, Collections.emptyList());
+ assignGroup(group, cycler, Collections.emptyList());
}
}
}
@@ -369,11 +375,13 @@
return groupClassNumber;
}
- private Collection<VirtualFile> assignGroup(ClassGroup group, List<VirtualFile> exclude) {
- VirtualFileCycler cycler = new VirtualFileCycler(dexes, appView, namingLens, dexIndexOffset);
+ private Collection<VirtualFile> assignGroup(
+ ClassGroup group, VirtualFileCycler cycler, List<VirtualFile> exclude) {
if (group.members.isEmpty()) {
return Collections.emptyList();
- } else if (group.canFitInOneDex()) {
+ }
+ cycler.reset();
+ if (group.canFitInOneDex()) {
VirtualFile currentDex;
while (true) {
currentDex = cycler.nextOrCreate(dex -> !exclude.contains(dex) && !isDexFull(dex));
@@ -399,7 +407,8 @@
Collection<VirtualFile> newExclude = new HashSet<>(exclude);
newExclude.add(dexForLinkingClasses);
- Collection<VirtualFile> usedDex = assignClassesWithLinkingError(remaining, newExclude);
+ Collection<VirtualFile> usedDex =
+ assignClassesWithLinkingError(remaining, cycler, newExclude);
usedDex.add(dexForLinkingClasses);
return usedDex;
}
@@ -412,14 +421,11 @@
* @param classes set of classes to assign, the set will be destroyed during assignment.
*/
private Collection<VirtualFile> assignClassesWithLinkingError(
- Set<DexProgramClass> classes, Collection<VirtualFile> exclude) {
-
+ Set<DexProgramClass> classes, VirtualFileCycler cycler, Collection<VirtualFile> exclude) {
List<ClassGroup> layers = collectNoDirectInheritanceGroups(classes);
-
Collections.sort(layers);
Collection<VirtualFile> usedDex = new ArrayList<>();
- VirtualFileCycler cycler = new VirtualFileCycler(dexes, appView, namingLens, dexIndexOffset);
// Don't modify exclude. Think about modifying the input collection considering this
// is private API.
Set<VirtualFile> currentExclude = new HashSet<>(exclude);
@@ -456,10 +462,8 @@
dexForLayer.commitTransaction();
break;
}
-
}
}
-
}
return usedDex;
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 7f39bfc..d5995f4 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexCallSite;
+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;
@@ -24,7 +25,9 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
+import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.graph.ThrowNullCode;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.naming.ClassNameMapper;
@@ -34,18 +37,22 @@
import com.android.tools.r8.synthesis.SyntheticNaming;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.IntBox;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterators;
import com.google.common.collect.Sets;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
@@ -320,11 +327,7 @@
if (!combineSyntheticClassesWithPrimaryClass
|| !appView.getSyntheticItems().isSyntheticClass(clazz)) {
VirtualFile file =
- new VirtualFile(
- virtualFiles.size(),
- writer.appView,
- writer.namingLens,
- clazz);
+ new VirtualFile(virtualFiles.size(), appView, writer.namingLens, clazz);
virtualFiles.add(file);
file.addClass(clazz);
files.put(clazz, file);
@@ -362,7 +365,7 @@
this.classes = SetUtils.newIdentityHashSet(classes);
// Create the primary dex file. The distribution will add more if needed.
- mainDexFile = new VirtualFile(0, writer.appView, writer.namingLens);
+ mainDexFile = new VirtualFile(0, appView, writer.namingLens);
assert virtualFiles.isEmpty();
virtualFiles.add(mainDexFile);
addMarkers(mainDexFile);
@@ -435,25 +438,27 @@
if (featureSplitClasses.isEmpty()) {
return;
}
- List<VirtualFile> filesForDistribution;
for (Map.Entry<FeatureSplit, Set<DexProgramClass>> featureSplitSetEntry :
featureSplitClasses.entrySet()) {
// Add a new virtual file, start from index 0 again
+ IntBox nextFileId = new IntBox();
VirtualFile featureFile =
new VirtualFile(
- 0,
- writer.appView,
+ nextFileId.getAndIncrement(),
+ appView,
writer.namingLens,
featureSplitSetEntry.getKey());
virtualFiles.add(featureFile);
addMarkers(featureFile);
- filesForDistribution = virtualFiles.subList(virtualFiles.size() - 1, virtualFiles.size());
+ List<VirtualFile> files = virtualFiles;
+ List<VirtualFile> filesForDistribution = ImmutableList.of(featureFile);
new PackageSplitPopulator(
+ files,
filesForDistribution,
appView,
featureSplitSetEntry.getValue(),
originalNames,
- 0,
+ nextFileId,
writer.namingLens)
.run();
}
@@ -474,6 +479,9 @@
@Override
public List<VirtualFile> run() throws IOException {
+ assert virtualFiles.size() == 1;
+ assert virtualFiles.get(0).isEmpty();
+
int totalClassNumber = classes.size();
// First fill required classes into the main dex file.
fillForMainDexList(classes);
@@ -483,37 +491,37 @@
}
List<VirtualFile> filesForDistribution = virtualFiles;
- int fileIndexOffset = 0;
boolean multidexLegacy = !mainDexFile.isEmpty();
if (options.minimalMainDex && multidexLegacy) {
- assert !virtualFiles.get(0).isEmpty();
assert virtualFiles.size() == 1;
- // The main dex file is filtered out, so ensure at least one file for the remaining classes.
- virtualFiles.add(new VirtualFile(1, writer.appView, writer.namingLens));
- filesForDistribution = virtualFiles.subList(1, virtualFiles.size());
- fileIndexOffset = 1;
+ assert !virtualFiles.get(0).isEmpty();
+ // Don't consider the main dex for distribution.
+ filesForDistribution = Collections.emptyList();
}
Map<FeatureSplit, Set<DexProgramClass>> featureSplitClasses =
removeFeatureSplitClassesGetMapping();
+ IntBox nextFileId = new IntBox(1);
if (multidexLegacy && options.enableInheritanceClassInDexDistributor) {
new InheritanceClassInDexDistributor(
mainDexFile,
+ virtualFiles,
filesForDistribution,
classes,
- fileIndexOffset,
+ nextFileId,
writer.namingLens,
- writer.appView,
+ appView,
executorService)
.distribute();
} else {
new PackageSplitPopulator(
+ virtualFiles,
filesForDistribution,
appView,
classes,
originalNames,
- fileIndexOffset,
+ nextFileId,
writer.namingLens)
.run();
}
@@ -820,33 +828,41 @@
static class VirtualFileCycler {
private final List<VirtualFile> files;
+ private final List<VirtualFile> filesForDistribution;
private final AppView<?> appView;
private final NamingLens namingLens;
- private int nextFileId;
+ private final IntBox nextFileId;
private Iterator<VirtualFile> allFilesCyclic;
private Iterator<VirtualFile> activeFiles;
- private FeatureSplit featuresplit;
+ private FeatureSplit featureSplit;
VirtualFileCycler(
List<VirtualFile> files,
+ List<VirtualFile> filesForDistribution,
AppView<?> appView,
NamingLens namingLens,
- int fileIndexOffset) {
+ IntBox nextFileId) {
this.files = files;
+ this.filesForDistribution = new ArrayList<>(filesForDistribution);
this.appView = appView;
this.namingLens = namingLens;
+ this.nextFileId = nextFileId;
- nextFileId = files.size() + fileIndexOffset;
- if (files.size() > 0) {
- featuresplit = files.get(0).getFeatureSplit();
+ if (filesForDistribution.size() > 0) {
+ featureSplit = filesForDistribution.get(0).getFeatureSplit();
}
reset();
}
+ void clearFilesForDistribution() {
+ filesForDistribution.clear();
+ reset();
+ }
+
void reset() {
- allFilesCyclic = Iterators.cycle(files);
+ allFilesCyclic = Iterators.cycle(filesForDistribution);
restart();
}
@@ -863,11 +879,10 @@
*/
VirtualFile nextOrCreate() {
if (hasNext()) {
- return activeFiles.next();
+ return next();
} else {
- VirtualFile newFile = new VirtualFile(nextFileId++, appView, namingLens, featuresplit);
- files.add(newFile);
- allFilesCyclic = Iterators.cycle(files);
+ VirtualFile newFile = internalAddFile();
+ allFilesCyclic = Iterators.cycle(filesForDistribution);
return newFile;
}
}
@@ -892,16 +907,29 @@
// Start a new iteration over all files, starting at the current one.
void restart() {
- activeFiles = Iterators.limit(allFilesCyclic, files.size());
+ activeFiles = Iterators.limit(allFilesCyclic, filesForDistribution.size());
}
VirtualFile addFile() {
- VirtualFile newFile = new VirtualFile(nextFileId++, appView, namingLens, featuresplit);
- files.add(newFile);
-
+ VirtualFile newFile = internalAddFile();
reset();
return newFile;
}
+
+ private VirtualFile internalAddFile() {
+ VirtualFile newFile =
+ new VirtualFile(nextFileId.getAndIncrement(), appView, namingLens, featureSplit);
+ files.add(newFile);
+ filesForDistribution.add(newFile);
+ return newFile;
+ }
+
+ VirtualFileCycler ensureFile() {
+ if (filesForDistribution.isEmpty()) {
+ addFile();
+ }
+ return this;
+ }
}
/**
@@ -1029,16 +1057,18 @@
PackageSplitPopulator(
List<VirtualFile> files,
+ List<VirtualFile> filesForDistribution,
AppView<?> appView,
Collection<DexProgramClass> classes,
Map<DexProgramClass, String> originalNames,
- int fileIndexOffset,
+ IntBox nextFileId,
NamingLens namingLens) {
this.classPartioning = PackageSplitClassPartioning.create(classes, appView, originalNames);
this.originalNames = originalNames;
this.dexItemFactory = appView.dexItemFactory();
this.options = appView.options();
- this.cycler = new VirtualFileCycler(files, appView, namingLens, fileIndexOffset);
+ this.cycler =
+ new VirtualFileCycler(files, filesForDistribution, appView, namingLens, nextFileId);
}
static boolean coveredByPrefix(String originalName, String currentPrefix) {
@@ -1059,11 +1089,19 @@
public void run() {
addStartupClasses();
+ enableStartupCompletenessCheckForTesting();
List<DexProgramClass> nonPackageClasses = addNonStartupClasses();
addNonPackageClasses(cycler, nonPackageClasses);
}
private void addStartupClasses() {
+ List<DexProgramClass> startupClasses = classPartioning.getStartupClasses();
+ if (startupClasses.isEmpty()) {
+ return;
+ }
+
+ assert options.getStartupOptions().hasStartupConfiguration();
+
// In practice, all startup classes should fit in a single dex file, so optimistically try to
// commit the startup classes using a single transaction.
VirtualFile virtualFile = cycler.next();
@@ -1091,7 +1129,36 @@
}
}
- cycler.restart();
+ if (options.getStartupOptions().isMinimalStartupDexEnabled()) {
+ cycler.clearFilesForDistribution();
+ } else {
+ cycler.restart();
+ }
+ }
+
+ /**
+ * Replaces the code of each method of a non-startup class by {@code throw null}. If the
+ * application fails on launch with this enabled this points to the startup configuration being
+ * incomplete.
+ */
+ private void enableStartupCompletenessCheckForTesting() {
+ if (!options.getStartupOptions().isStartupCompletenessCheckForTesting()) {
+ return;
+ }
+ for (DexProgramClass clazz : classPartioning.getNonStartupClasses()) {
+ clazz.forEachProgramMethodMatching(
+ DexEncodedMethod::hasCode,
+ method ->
+ method.getDefinition().setCode(ThrowNullCode.get(), Int2ReferenceMaps.emptyMap()));
+ if (!clazz.hasClassInitializer()) {
+ clazz.addDirectMethod(
+ DexEncodedMethod.syntheticBuilder()
+ .setAccessFlags(MethodAccessFlags.createForClassInitializer())
+ .setCode(ThrowNullCode.get())
+ .setMethod(dexItemFactory.createClassInitializer(clazz.getType()))
+ .build());
+ }
+ }
}
private List<DexProgramClass> addNonStartupClasses() {
@@ -1099,7 +1166,7 @@
int transactionStartIndex = 0;
String currentPrefix = null;
Object2IntMap<String> packageAssignments = new Object2IntOpenHashMap<>();
- VirtualFile current = cycler.next();
+ VirtualFile current = cycler.ensureFile().next();
List<DexProgramClass> classes = classPartioning.getNonStartupClasses();
List<DexProgramClass> nonPackageClasses = new ArrayList<>();
for (int classIndex = 0; classIndex < classes.size(); classIndex++) {
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupConfiguration.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupConfiguration.java
index 0f9ad9c..0c1425c 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupConfiguration.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupConfiguration.java
@@ -45,11 +45,13 @@
*/
public static StartupConfiguration createStartupConfiguration(
DexItemFactory dexItemFactory, Reporter reporter) {
- String propertyValue = System.getProperty("com.android.tools.r8.startupclassdescriptors");
+ String propertyValue = System.getProperty("com.android.tools.r8.startup.config");
if (propertyValue == null) {
return null;
}
+ reporter.warning("Use of startupconfig is experimental");
+
List<String> startupDescriptors;
try {
startupDescriptors = FileUtils.readAllLines(Paths.get(propertyValue));
@@ -115,10 +117,10 @@
String protoWithNameDescriptor = startupMethodDescriptor.substring(methodNameStartIndex);
int methodNameEndIndex = protoWithNameDescriptor.indexOf('(');
- if (methodNameEndIndex <= 1) {
+ if (methodNameEndIndex <= 0) {
return null;
}
- String methodName = protoWithNameDescriptor.substring(methodNameEndIndex);
+ String methodName = protoWithNameDescriptor.substring(0, methodNameEndIndex);
String protoDescriptor = protoWithNameDescriptor.substring(methodNameEndIndex);
DexProto proto = parseStartupMethodProto(protoDescriptor, dexItemFactory);
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupInstrumentation.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupInstrumentation.java
new file mode 100644
index 0000000..8995dbc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupInstrumentation.java
@@ -0,0 +1,126 @@
+// Copyright (c) 2022, 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.experimental.startup;
+
+import com.android.tools.r8.androidapi.ComputedApiLevel;
+import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.cf.code.CfConstString;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfReturnVoid;
+import com.android.tools.r8.cf.code.CfStackInstruction;
+import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
+import com.android.tools.r8.cf.code.CfStaticFieldRead;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.Code;
+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.MethodAccessFlags;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import org.objectweb.asm.Opcodes;
+
+public class StartupInstrumentation {
+
+ private final AppView<?> appView;
+ private final DexItemFactory dexItemFactory;
+ private final StartupOptions options;
+
+ public StartupInstrumentation(AppView<?> appView) {
+ this.appView = appView;
+ this.dexItemFactory = appView.dexItemFactory();
+ this.options = appView.options().getStartupOptions();
+ }
+
+ public void instrumentClasses(ExecutorService executorService) throws ExecutionException {
+ if (!appView.options().getStartupOptions().isStartupInstrumentationEnabled()) {
+ return;
+ }
+ ThreadUtils.processItems(
+ appView.appInfo().classes(), this::internalInstrumentClass, executorService);
+ }
+
+ public void instrumentClass(DexProgramClass clazz) {
+ if (!appView.options().getStartupOptions().isStartupInstrumentationEnabled()) {
+ return;
+ }
+ internalInstrumentClass(clazz);
+ }
+
+ private void internalInstrumentClass(DexProgramClass clazz) {
+ ProgramMethod classInitializer = ensureClassInitializer(clazz);
+ instrumentClassInitializer(classInitializer);
+ }
+
+ private ProgramMethod ensureClassInitializer(DexProgramClass clazz) {
+ if (!clazz.hasClassInitializer()) {
+ int maxLocals = 0;
+ int maxStack = 0;
+ ComputedApiLevel computedApiLevel =
+ appView.apiLevelCompute().computeInitialMinApiLevel(appView.options());
+ clazz.addDirectMethod(
+ DexEncodedMethod.syntheticBuilder()
+ .setAccessFlags(MethodAccessFlags.createForClassInitializer())
+ .setApiLevelForCode(computedApiLevel)
+ .setApiLevelForDefinition(computedApiLevel)
+ .setClassFileVersion(CfVersion.V1_6)
+ .setCode(
+ new CfCode(
+ clazz.getType(), maxStack, maxLocals, ImmutableList.of(new CfReturnVoid())))
+ .setMethod(dexItemFactory.createClassInitializer(clazz.getType()))
+ .build());
+ }
+ return clazz.getProgramClassInitializer();
+ }
+
+ private void instrumentClassInitializer(ProgramMethod classInitializer) {
+ Code code = classInitializer.getDefinition().getCode();
+ if (!code.isCfCode()) {
+ // Should generally not happen.
+ assert false;
+ return;
+ }
+
+ CfCode cfCode = code.asCfCode();
+ List<CfInstruction> instructions;
+ if (options.hasStartupInstrumentationTag()) {
+ instructions = new ArrayList<>(4 + cfCode.getInstructions().size());
+ instructions.add(
+ new CfConstString(dexItemFactory.createString(options.getStartupInstrumentationTag())));
+ instructions.add(new CfConstString(classInitializer.getHolderType().getDescriptor()));
+ instructions.add(
+ new CfInvoke(Opcodes.INVOKESTATIC, dexItemFactory.androidUtilLogMembers.i, false));
+ instructions.add(new CfStackInstruction(Opcode.Pop));
+ } else {
+ instructions = new ArrayList<>(3 + cfCode.getInstructions().size());
+ instructions.add(new CfStaticFieldRead(dexItemFactory.javaLangSystemMembers.out));
+ instructions.add(new CfConstString(classInitializer.getHolderType().getDescriptor()));
+ instructions.add(
+ new CfInvoke(
+ Opcodes.INVOKEVIRTUAL,
+ dexItemFactory.javaIoPrintStreamMembers.printlnWithString,
+ false));
+ }
+ instructions.addAll(cfCode.getInstructions());
+ classInitializer.setCode(
+ new CfCode(
+ cfCode.getOriginalHolder(),
+ Math.max(cfCode.getMaxStack(), 2),
+ cfCode.getMaxLocals(),
+ instructions,
+ cfCode.getTryCatchRanges(),
+ cfCode.getLocalVariables(),
+ cfCode.getDiagnosticPosition(),
+ cfCode.getMetadata()),
+ appView);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java
new file mode 100644
index 0000000..7fad730
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java
@@ -0,0 +1,70 @@
+// Copyright (c) 2022, 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.experimental.startup;
+
+import static com.android.tools.r8.utils.InternalOptions.getSystemPropertyForDevelopmentOrDefault;
+import static com.android.tools.r8.utils.InternalOptions.isSystemPropertyForDevelopmentSet;
+
+public class StartupOptions {
+
+ private boolean enableMinimalStartupDex =
+ isSystemPropertyForDevelopmentSet("com.android.tools.r8.startup.minimalstartupdex");
+ private boolean enableStartupCompletenessCheckForTesting =
+ isSystemPropertyForDevelopmentSet("com.android.tools.r8.startup.completenesscheck");
+ private boolean enableStartupInstrumentation =
+ isSystemPropertyForDevelopmentSet("com.android.tools.r8.startup.instrument");
+ private String startupInstrumentationTag =
+ getSystemPropertyForDevelopmentOrDefault(
+ "com.android.tools.r8.startup.instrumentationtag", null);
+
+ private StartupConfiguration startupConfiguration;
+
+ public boolean hasStartupInstrumentationTag() {
+ return startupInstrumentationTag != null;
+ }
+
+ public String getStartupInstrumentationTag() {
+ return startupInstrumentationTag;
+ }
+
+ public boolean isMinimalStartupDexEnabled() {
+ return enableMinimalStartupDex;
+ }
+
+ public StartupOptions setEnableMinimalStartupDex() {
+ enableMinimalStartupDex = true;
+ return this;
+ }
+
+ public boolean isStartupInstrumentationEnabled() {
+ return enableStartupInstrumentation;
+ }
+
+ public StartupOptions setEnableStartupInstrumentation() {
+ enableStartupInstrumentation = true;
+ return this;
+ }
+
+ public boolean isStartupCompletenessCheckForTesting() {
+ return enableStartupCompletenessCheckForTesting;
+ }
+
+ public StartupOptions setEnableStartupCompletenessCheckForTesting() {
+ enableStartupCompletenessCheckForTesting = true;
+ return this;
+ }
+
+ public boolean hasStartupConfiguration() {
+ return startupConfiguration != null;
+ }
+
+ public StartupConfiguration getStartupConfiguration() {
+ return startupConfiguration;
+ }
+
+ public void setStartupConfiguration(StartupConfiguration startupConfiguration) {
+ this.startupConfiguration = startupConfiguration;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
index 215b25a..82b0243 100644
--- a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
+++ b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
@@ -50,7 +50,7 @@
return createInitialClassToFeatureSplitMap(
options.dexItemFactory(),
options.featureSplitConfiguration,
- options.startupConfiguration,
+ options.getStartupOptions().getStartupConfiguration(),
options.reporter);
}
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 4c28ff0..81979c8 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -532,17 +532,17 @@
* may be abstract.
*/
public DexClassAndMethod lookupMaximallySpecificMethod(DexClass clazz, DexMethod method) {
- return new MethodResolution(this::definitionFor, dexItemFactory())
+ return MethodResolution.createLegacy(this::definitionFor, dexItemFactory())
.lookupMaximallySpecificTarget(clazz, method);
}
MethodResolutionResult resolveMaximallySpecificTarget(DexClass clazz, DexMethod method) {
- return new MethodResolution(this::definitionFor, dexItemFactory())
+ return MethodResolution.createLegacy(this::definitionFor, dexItemFactory())
.resolveMaximallySpecificTarget(clazz, method);
}
MethodResolutionResult resolveMaximallySpecificTarget(LambdaDescriptor lambda, DexMethod method) {
- return new MethodResolution(this::definitionFor, dexItemFactory())
+ return MethodResolution.createLegacy(this::definitionFor, dexItemFactory())
.resolveMaximallySpecificTarget(lambda, method);
}
@@ -648,7 +648,7 @@
*/
public MethodResolutionResult unsafeResolveMethodDueToDexFormat(DexMethod method) {
assert checkIfObsolete();
- return new MethodResolution(this::definitionFor, dexItemFactory())
+ return MethodResolution.createLegacy(this::definitionFor, dexItemFactory())
.unsafeResolveMethodDueToDexFormat(method);
}
@@ -698,7 +698,7 @@
public MethodResolutionResult resolveMethodOnClass(
DexType holder, DexProto proto, DexString name) {
assert checkIfObsolete();
- return new MethodResolution(this::definitionFor, dexItemFactory())
+ return MethodResolution.createLegacy(this::definitionFor, dexItemFactory())
.resolveMethodOnClass(holder, proto, name);
}
@@ -715,7 +715,7 @@
public MethodResolutionResult resolveMethodOnClass(
DexClass clazz, DexProto proto, DexString name) {
assert checkIfObsolete();
- return new MethodResolution(this::definitionFor, dexItemFactory())
+ return MethodResolution.createLegacy(this::definitionFor, dexItemFactory())
.resolveMethodOnClass(clazz, proto, name);
}
@@ -726,7 +726,7 @@
public MethodResolutionResult resolveMethodOnInterface(DexType holder, DexMethod method) {
assert checkIfObsolete();
- return new MethodResolution(this::definitionFor, dexItemFactory())
+ return MethodResolution.createLegacy(this::definitionFor, dexItemFactory())
.resolveMethodOnInterface(holder, method.getProto(), method.getName());
}
@@ -744,7 +744,7 @@
public MethodResolutionResult resolveMethodOnInterface(
DexClass clazz, DexProto proto, DexString name) {
assert checkIfObsolete();
- return new MethodResolution(this::definitionFor, dexItemFactory())
+ return MethodResolution.createLegacy(this::definitionFor, dexItemFactory())
.resolveMethodOnInterface(clazz, proto, name);
}
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 910d5f5..4974a5d 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -963,6 +963,16 @@
isAssignablePredicate(appView),
appView.dexItemFactory(),
maxStack);
+ for (CfTryCatch tryCatchRange : tryCatchRanges) {
+ try {
+ builder.checkTryCatchRange(tryCatchRange);
+ } catch (CfCodeStackMapValidatingException ex) {
+ return reportStackMapError(
+ CfCodeStackMapValidatingException.invalidTryCatchRange(
+ method, tryCatchRange, ex.getMessage(), appView),
+ appView);
+ }
+ }
if (stateMap.containsKey(null)) {
assert !shouldComputeInitialFrame();
builder.checkFrameAndSet(stateMap.get(null));
@@ -989,7 +999,7 @@
instruction.evaluate(builder, previousMethodSignature, appView, appView.dexItemFactory());
} catch (CfCodeStackMapValidatingException ex) {
return reportStackMapError(
- CfCodeStackMapValidatingException.toDiagnostics(
+ CfCodeStackMapValidatingException.invalidStackMapForInstruction(
method, i, instruction, ex.getMessage(), appView),
appView);
}
diff --git a/src/main/java/com/android/tools/r8/graph/CfCodeStackMapValidatingException.java b/src/main/java/com/android/tools/r8/graph/CfCodeStackMapValidatingException.java
index 1a25ce6..4b417bf 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCodeStackMapValidatingException.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCodeStackMapValidatingException.java
@@ -5,6 +5,8 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfTryCatch;
+import com.android.tools.r8.utils.StringUtils;
public class CfCodeStackMapValidatingException extends RuntimeException {
@@ -52,7 +54,24 @@
sb.toString());
}
- public static CfCodeDiagnostics toDiagnostics(
+ public static CfCodeDiagnostics invalidTryCatchRange(
+ ProgramMethod method, CfTryCatch tryCatch, String detailMessage, AppView<?> appView) {
+ StringBuilder sb =
+ new StringBuilder("Invalid try catch range for ")
+ .append(StringUtils.join(", ", tryCatch.guards, DexType::getTypeName))
+ .append(": ")
+ .append(detailMessage)
+ .append(".");
+ if (appView.enableWholeProgramOptimizations()) {
+ sb.append(" In later version of R8, the method may be assumed not reachable.");
+ }
+ return new CfCodeDiagnostics(
+ method.getOrigin(),
+ appView.graphLens().getOriginalMethodSignature(method.getReference()),
+ sb.toString());
+ }
+
+ public static CfCodeDiagnostics invalidStackMapForInstruction(
ProgramMethod method,
int instructionIndex,
CfInstruction instruction,
diff --git a/src/main/java/com/android/tools/r8/graph/ClassResolutionResult.java b/src/main/java/com/android/tools/r8/graph/ClassResolutionResult.java
index 4eca4e1..12baacf 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassResolutionResult.java
@@ -14,8 +14,17 @@
DexClass toSingleClassWithProgramOverLibrary();
+ // The alternative class is:
+ // - the other class than the single class if the resolution resolves into multiple classes,
+ // - null if the resolution resolves into a single class.
+ DexClass toAlternativeClassWithProgramOverLibrary();
+
void forEachClassResolutionResult(Consumer<DexClass> consumer);
+ default boolean isMultipleClassResolutionResult() {
+ return false;
+ }
+
static Builder builder() {
return new Builder();
}
@@ -86,6 +95,11 @@
}
@Override
+ public DexClass toAlternativeClassWithProgramOverLibrary() {
+ return null;
+ }
+
+ @Override
public void forEachClassResolutionResult(Consumer<DexClass> consumer) {
// Intentionally empty
}
@@ -112,6 +126,11 @@
consumer.accept(programOrClasspathClass);
consumer.accept(libraryClass);
}
+
+ @Override
+ public boolean isMultipleClassResolutionResult() {
+ return true;
+ }
}
class ProgramAndLibraryClassResolutionResult
@@ -126,6 +145,11 @@
public DexClass toSingleClassWithProgramOverLibrary() {
return programOrClasspathClass;
}
+
+ @Override
+ public DexClass toAlternativeClassWithProgramOverLibrary() {
+ return libraryClass;
+ }
}
class ClasspathAndLibraryClassResolutionResult
@@ -140,5 +164,10 @@
public DexClass toSingleClassWithProgramOverLibrary() {
return libraryClass;
}
+
+ @Override
+ public DexClass toAlternativeClassWithProgramOverLibrary() {
+ return programOrClasspathClass;
+ }
}
}
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 4fac278..a615b1f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -150,6 +150,11 @@
return this;
}
+ @Override
+ public DexClass toAlternativeClassWithProgramOverLibrary() {
+ return null;
+ }
+
public abstract void accept(
Consumer<DexProgramClass> programClassConsumer,
Consumer<DexClasspathClass> classpathClassConsumer,
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 98dac6d..7c0e5d2 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -547,11 +547,13 @@
public final ClassMethods classMethods = new ClassMethods();
public final ConstructorMethods constructorMethods = new ConstructorMethods();
public final EnumMembers enumMembers = new EnumMembers();
+ public final AndroidUtilLogMembers androidUtilLogMembers = new AndroidUtilLogMembers();
public final JavaLangReflectArrayMembers javaLangReflectArrayMembers =
new JavaLangReflectArrayMembers();
public final JavaLangAnnotationRetentionPolicyMembers javaLangAnnotationRetentionPolicyMembers =
new JavaLangAnnotationRetentionPolicyMembers();
- public final JavaLangSystemMethods javaLangSystemMethods = new JavaLangSystemMethods();
+ public final JavaLangSystemMembers javaLangSystemMembers = new JavaLangSystemMembers();
+ public final JavaIoPrintStreamMembers javaIoPrintStreamMembers = new JavaIoPrintStreamMembers();
public final NullPointerExceptionMethods npeMethods = new NullPointerExceptionMethods();
public final IllegalArgumentExceptionMethods illegalArgumentExceptionMethods =
new IllegalArgumentExceptionMethods();
@@ -1702,6 +1704,14 @@
}
}
+ public class AndroidUtilLogMembers {
+
+ public final DexMethod i =
+ createMethod(androidUtilLogType, createProto(intType, stringType, stringType), "i");
+
+ private AndroidUtilLogMembers() {}
+ }
+
public class JavaLangAnnotationRetentionPolicyMembers {
public final DexField CLASS =
@@ -1722,7 +1732,9 @@
private JavaLangReflectArrayMembers() {}
}
- public class JavaLangSystemMethods {
+ public class JavaLangSystemMembers {
+
+ public final DexField out = createField(javaLangSystemType, javaIoPrintStreamType, "out");
public final DexMethod arraycopy =
createMethod(
@@ -1732,7 +1744,15 @@
public final DexMethod identityHashCode =
createMethod(javaLangSystemType, createProto(intType, objectType), identityHashCodeName);
- private JavaLangSystemMethods() {}
+ private JavaLangSystemMembers() {}
+ }
+
+ public class JavaIoPrintStreamMembers {
+
+ public final DexMethod printlnWithString =
+ createMethod(javaIoPrintStreamType, createProto(voidType, stringType), "println");
+
+ private JavaIoPrintStreamMembers() {}
}
public class EnumMembers {
diff --git a/src/main/java/com/android/tools/r8/graph/MethodResolution.java b/src/main/java/com/android/tools/r8/graph/MethodResolution.java
index 2f6939e..0927de2 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodResolution.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodResolution.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.graph;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.MethodResolutionResult.ArrayCloneMethodResult;
import com.android.tools.r8.graph.MethodResolutionResult.ClassNotFoundResult;
import com.android.tools.r8.graph.MethodResolutionResult.IllegalAccessOrNoSuchMethodResult;
@@ -27,15 +28,31 @@
*/
public class MethodResolution {
- private final Function<DexType, DexClass> definitionFor;
+ private final Function<DexType, ClassResolutionResult> definitionFor;
private final DexItemFactory factory;
+ private final boolean escapeIfLibraryHasProgramSuperType;
- public MethodResolution(Function<DexType, DexClass> definitionFor, DexItemFactory factory) {
+ private MethodResolution(
+ Function<DexType, ClassResolutionResult> definitionFor,
+ DexItemFactory factory,
+ boolean escapeIfLibraryHasProgramSuperType) {
this.definitionFor = definitionFor;
this.factory = factory;
+ this.escapeIfLibraryHasProgramSuperType = escapeIfLibraryHasProgramSuperType;
}
- private DexClass definitionFor(DexType type) {
+ public static MethodResolution createLegacy(
+ Function<DexType, DexClass> definitionFor, DexItemFactory factory) {
+ return new MethodResolution(
+ type -> {
+ DexClass clazz = definitionFor.apply(type);
+ return clazz == null ? ClassResolutionResult.NoResolutionResult.noResult() : clazz;
+ },
+ factory,
+ false);
+ }
+
+ private ClassResolutionResult definitionFor(DexType type) {
return definitionFor.apply(type);
}
@@ -53,14 +70,19 @@
if (holder.isArrayType()) {
return resolveMethodOnArray(holder, method.getProto(), method.getName());
}
- DexClass definition = definitionFor(holder);
- if (definition == null) {
- return ClassNotFoundResult.INSTANCE;
- } else if (definition.isInterface()) {
- return resolveMethodOnInterface(definition, method.getProto(), method.getName());
- } else {
- return resolveMethodOnClass(definition, method.getProto(), method.getName());
- }
+ MethodResolutionResult.Builder builder = MethodResolutionResult.builder();
+ definitionFor(holder)
+ .forEachClassResolutionResult(
+ clazz -> {
+ if (clazz.isInterface()) {
+ builder.addResolutionResult(
+ resolveMethodOnInterface(clazz, method.getProto(), method.getName()));
+ } else {
+ builder.addResolutionResult(
+ resolveMethodOnClass(clazz, method.getProto(), method.getName()));
+ }
+ });
+ return builder.buildOrIfEmpty(ClassNotFoundResult.INSTANCE);
}
/**
@@ -96,15 +118,18 @@
if (holder.isArrayType()) {
return resolveMethodOnArray(holder, methodProto, methodName);
}
- DexClass clazz = definitionFor(holder);
- if (clazz == null) {
- return ClassNotFoundResult.INSTANCE;
- }
- // Step 1: If holder is an interface, resolution fails with an ICCE. We return null.
- if (clazz.isInterface()) {
- return IncompatibleClassResult.INSTANCE;
- }
- return resolveMethodOnClass(clazz, methodProto, methodName);
+ MethodResolutionResult.Builder builder = MethodResolutionResult.builder();
+ definitionFor(holder)
+ .forEachClassResolutionResult(
+ clazz -> {
+ // Step 1: If holder is an interface, resolution fails with an ICCE.
+ if (clazz.isInterface()) {
+ builder.addResolutionResult(IncompatibleClassResult.INSTANCE);
+ } else {
+ builder.addResolutionResult(resolveMethodOnClass(clazz, methodProto, methodName));
+ }
+ });
+ return builder.buildOrIfEmpty(ClassNotFoundResult.INSTANCE);
}
public MethodResolutionResult resolveMethodOnClass(
@@ -135,7 +160,8 @@
// Section 2.9 of the JVM Spec</a>.
DexEncodedMethod result = clazz.lookupSignaturePolymorphicMethod(methodName, factory);
if (result != null) {
- return new SingleResolutionResult(initialResolutionHolder, clazz, result);
+ return MethodResolutionResult.createSingleResolutionResult(
+ initialResolutionHolder, clazz, result);
}
// Pt 2: Find a method that matches the descriptor.
result = clazz.lookupMethod(methodProto, methodName);
@@ -148,17 +174,27 @@
if (result.isPrivateMethod() && clazz != initialResolutionHolder) {
return new IllegalAccessOrNoSuchMethodResult(initialResolutionHolder, result);
}
- return new SingleResolutionResult(initialResolutionHolder, clazz, result);
+ return MethodResolutionResult.createSingleResolutionResult(
+ initialResolutionHolder, clazz, result);
}
// Pt 3: Apply step two to direct superclass of holder.
+ MethodResolutionResult.Builder builder = MethodResolutionResult.builder();
if (clazz.superType != null) {
- DexClass superClass = definitionFor(clazz.superType);
- if (superClass != null) {
- return resolveMethodOnClassStep2(
- superClass, methodProto, methodName, initialResolutionHolder);
- }
+ definitionFor(clazz.superType)
+ .forEachClassResolutionResult(
+ superClass -> {
+ // Guard against going back into the program for resolution.
+ if (escapeIfLibraryHasProgramSuperType
+ && clazz.isLibraryClass()
+ && !superClass.isLibraryClass()) {
+ return;
+ }
+ builder.addResolutionResult(
+ resolveMethodOnClassStep2(
+ superClass, methodProto, methodName, initialResolutionHolder));
+ });
}
- return null;
+ return builder.buildOrIfEmpty(null);
}
/**
@@ -201,7 +237,7 @@
MaximallySpecificMethodsBuilder builder =
new MaximallySpecificMethodsBuilder(definitionFor, factory);
resolveMethodStep3Helper(
- method.getProto(), method.getName(), factory.objectType, lambda.interfaces, builder);
+ method.getProto(), method.getName(), null, builder, factory.objectType, lambda.interfaces);
return builder;
}
@@ -212,37 +248,57 @@
DexClass clazz,
MaximallySpecificMethodsBuilder builder) {
resolveMethodStep3Helper(
- methodProto, methodName, clazz.superType, Arrays.asList(clazz.interfaces.values), builder);
+ methodProto,
+ methodName,
+ clazz,
+ builder,
+ clazz.superType,
+ Arrays.asList(clazz.interfaces.values));
}
private void resolveMethodStep3Helper(
DexProto methodProto,
DexString methodName,
+ DexClass clazz,
+ MaximallySpecificMethodsBuilder builder,
DexType superType,
- List<DexType> interfaces,
- MaximallySpecificMethodsBuilder builder) {
+ List<DexType> interfaces) {
for (DexType iface : interfaces) {
- DexClass definition = definitionFor(iface);
- if (definition == null) {
- // Ignore missing interface definitions.
- continue;
+ ClassResolutionResult classResolutionResult = definitionFor(iface);
+ if (classResolutionResult.isMultipleClassResolutionResult()) {
+ // TODO(b/214382176, b/226170842): Compute maximal specific set in precense of
+ // multiple results.
+ throw new Unreachable(
+ "MethodResolution should not be passed definition with multiple results");
}
- assert definition.isInterface();
- DexEncodedMethod result = definition.lookupMethod(methodProto, methodName);
- if (isMaximallySpecificCandidate(result)) {
- // The candidate is added and doing so will prohibit shadowed methods from being in the set.
- builder.addCandidate(definition, result);
- } else {
- // Look at the super-interfaces of this class and keep searching.
- resolveMethodStep3Helper(methodProto, methodName, definition, builder);
- }
+ classResolutionResult.forEachClassResolutionResult(
+ definition -> {
+ assert definition.isInterface();
+ DexEncodedMethod result = definition.lookupMethod(methodProto, methodName);
+ if (isMaximallySpecificCandidate(result)) {
+ // The candidate is added and doing so will prohibit shadowed methods from being
+ // in the set.
+ builder.addCandidate(definition, result);
+ } else {
+ // Look at the super-interfaces of this class and keep searching.
+ resolveMethodStep3Helper(methodProto, methodName, definition, builder);
+ }
+ });
}
// Now look at indirect super interfaces.
if (superType != null) {
- DexClass superClass = definitionFor(superType);
- if (superClass != null) {
- resolveMethodStep3Helper(methodProto, methodName, superClass, builder);
- }
+ definitionFor(superType)
+ .forEachClassResolutionResult(
+ superClass -> {
+ // Guard against going back into the program for resolution.
+ if (escapeIfLibraryHasProgramSuperType
+ && clazz != null
+ && clazz.isLibraryClass()
+ && !superClass.isLibraryClass()) {
+ return;
+ }
+ resolveMethodStep3Helper(methodProto, methodName, superClass, builder);
+ });
}
}
@@ -271,17 +327,20 @@
if (holder.isArrayType()) {
return IncompatibleClassResult.INSTANCE;
}
+ MethodResolutionResult.Builder builder = MethodResolutionResult.builder();
// Step 1: Lookup interface.
- DexClass definition = definitionFor(holder);
- // If the definition is not an interface, resolution fails with an ICCE. We just return the
- // empty result here.
- if (definition == null) {
- return ClassNotFoundResult.INSTANCE;
- }
- if (!definition.isInterface()) {
- return IncompatibleClassResult.INSTANCE;
- }
- return resolveMethodOnInterface(definition, proto, methodName);
+ definitionFor(holder)
+ .forEachClassResolutionResult(
+ definition -> {
+ // If the definition is not an interface, resolution fails with an ICCE.
+ if (!definition.isInterface()) {
+ builder.addResolutionResult(IncompatibleClassResult.INSTANCE);
+ } else {
+ builder.addResolutionResult(
+ resolveMethodOnInterface(definition, proto, methodName));
+ }
+ });
+ return builder.buildOrIfEmpty(ClassNotFoundResult.INSTANCE);
}
public MethodResolutionResult resolveMethodOnInterface(
@@ -290,20 +349,28 @@
// Step 2: Look for exact method on interface.
DexEncodedMethod result = definition.lookupMethod(methodProto, methodName);
if (result != null) {
- return new SingleResolutionResult(definition, definition, result);
+ return MethodResolutionResult.createSingleResolutionResult(definition, definition, result);
}
// Step 3: Look for matching method on object class.
- DexClass objectClass = definitionFor(factory.objectType);
- if (objectClass == null) {
- return ClassNotFoundResult.INSTANCE;
- }
- result = objectClass.lookupMethod(methodProto, methodName);
- if (result != null && result.accessFlags.isPublic() && !result.accessFlags.isAbstract()) {
- return new SingleResolutionResult(definition, objectClass, result);
- }
- // Step 3: Look for maximally-specific superinterface methods or any interface definition.
- // This is the same for classes and interfaces.
- return resolveMethodStep3(definition, methodProto, methodName);
+ MethodResolutionResult.Builder builder = MethodResolutionResult.builder();
+ definitionFor(factory.objectType)
+ .forEachClassResolutionResult(
+ objectClass -> {
+ DexEncodedMethod objectResult = objectClass.lookupMethod(methodProto, methodName);
+ if (objectResult != null
+ && objectResult.accessFlags.isPublic()
+ && !objectResult.accessFlags.isAbstract()) {
+ builder.addResolutionResult(
+ MethodResolutionResult.createSingleResolutionResult(
+ definition, objectClass, objectResult));
+ } else {
+ // Step 3: Look for maximally-specific superinterface methods or any interface
+ // definition. This is the same for classes and interfaces.
+ builder.addResolutionResult(
+ resolveMethodStep3(definition, methodProto, methodName));
+ }
+ });
+ return builder.buildOrIfEmpty(ClassNotFoundResult.INSTANCE);
}
static class MaximallySpecificMethodsBuilder {
@@ -316,11 +383,11 @@
// prior to writing.
private final LinkedHashMap<DexClass, DexEncodedMethod> maximallySpecificMethods =
new LinkedHashMap<>();
- private final Function<DexType, DexClass> definitionFor;
+ private final Function<DexType, ClassResolutionResult> definitionFor;
private final DexItemFactory factory;
private MaximallySpecificMethodsBuilder(
- Function<DexType, DexClass> definitionFor, DexItemFactory factory) {
+ Function<DexType, ClassResolutionResult> definitionFor, DexItemFactory factory) {
this.definitionFor = definitionFor;
this.factory = factory;
}
@@ -343,22 +410,29 @@
if (type == null) {
return;
}
- DexClass clazz = definitionFor.apply(type);
- if (clazz == null) {
- return;
+ ClassResolutionResult classResolutionResult = definitionFor.apply(type);
+ if (classResolutionResult.isMultipleClassResolutionResult()) {
+ // TODO(b/214382176, b/226170842): Compute maximal specific set in precense of
+ // multiple results.
+ throw new Unreachable(
+ "MethodResolution should not be passed definition with multiple results");
}
- assert clazz.isInterface();
- assert clazz.superType == factory.objectType;
- // A null entry signifies that the candidate is shadowed blocking future candidates.
- // If the candidate is already shadowed at this type there is no need to shadow further up.
- if (maximallySpecificMethods.containsKey(clazz)
- && maximallySpecificMethods.get(clazz) == null) {
- return;
- }
- maximallySpecificMethods.put(clazz, null);
- for (DexType iface : clazz.interfaces.values) {
- markShadowed(iface);
- }
+ classResolutionResult.forEachClassResolutionResult(
+ clazz -> {
+ assert clazz.isInterface();
+ assert clazz.superType == factory.objectType;
+ // A null entry signifies that the candidate is shadowed blocking future candidates.
+ // If the candidate is already shadowed at this type there is no need to shadow
+ // further up.
+ if (maximallySpecificMethods.containsKey(clazz)
+ && maximallySpecificMethods.get(clazz) == null) {
+ return;
+ }
+ maximallySpecificMethods.put(clazz, null);
+ for (DexType iface : clazz.interfaces.values) {
+ markShadowed(iface);
+ }
+ });
}
DexClassAndMethod lookup() {
@@ -407,9 +481,9 @@
return IncompatibleClassResult.create(ListUtils.map(nonAbstractMethods, Entry::getValue));
}
- private static SingleResolutionResult singleResultHelper(
+ private static SingleResolutionResult<?> singleResultHelper(
DexClass initialResolutionResult, Entry<DexClass, DexEncodedMethod> entry) {
- return new SingleResolutionResult(
+ return MethodResolutionResult.createSingleResolutionResult(
initialResolutionResult != null ? initialResolutionResult : entry.getKey(),
entry.getKey(),
entry.getValue());
diff --git a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
index 49860a7..65ff279 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
@@ -5,6 +5,7 @@
import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.LookupResult.LookupResultSuccess;
import com.android.tools.r8.graph.LookupResult.LookupResultSuccess.LookupResultCollectionState;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
@@ -12,9 +13,13 @@
import com.android.tools.r8.shaking.InstantiatedObject;
import com.android.tools.r8.utils.BooleanBox;
import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.OptionalBool;
+import com.google.common.collect.Iterables;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.List;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
@@ -43,7 +48,7 @@
}
/** Returns non-null if isSingleResolution() is true, otherwise null. */
- public SingleResolutionResult asSingleResolution() {
+ public SingleResolutionResult<?> asSingleResolution() {
return null;
}
@@ -159,17 +164,49 @@
AppInfoWithClassHierarchy appInfo,
Consumer<? super DexEncodedMethod> methodCausingFailureConsumer);
+ public abstract void visitMethodResolutionResults(
+ Consumer<? super SingleResolutionResult<?>> programOrClasspathConsumer,
+ Consumer<? super SingleLibraryResolutionResult> libraryResultConsumer,
+ Consumer<? super ArrayCloneMethodResult> cloneResultConsumer,
+ Consumer<? super FailedResolutionResult> failedResolutionConsumer);
+
+ public boolean hasProgramResult() {
+ return false;
+ }
+
+ public SingleClasspathResolutionResult asSingleClasspathResolutionResult() {
+ return null;
+ }
+
+ protected SingleProgramResolutionResult asSingleProgramResolutionResult() {
+ return null;
+ }
+
+ public static SingleResolutionResult<?> createSingleResolutionResult(
+ DexClass initialResolutionHolder, DexClass holder, DexEncodedMethod definition) {
+ if (holder.isLibraryClass()) {
+ return new SingleLibraryResolutionResult(
+ initialResolutionHolder, holder.asLibraryClass(), definition);
+ } else if (holder.isClasspathClass()) {
+ return new SingleClasspathResolutionResult(
+ initialResolutionHolder, holder.asClasspathClass(), definition);
+ } else {
+ assert holder.isProgramClass();
+ return new SingleProgramResolutionResult(
+ initialResolutionHolder, holder.asProgramClass(), definition);
+ }
+ }
+
/** Result for a resolution that succeeds with a known declaration/definition. */
- public static class SingleResolutionResult extends MethodResolutionResult
+ public abstract static class SingleResolutionResult<T extends DexClass>
+ extends MethodResolutionResult
implements SuccessfulMemberResolutionResult<DexEncodedMethod, DexMethod> {
private final DexClass initialResolutionHolder;
- private final DexClass resolvedHolder;
+ private final T resolvedHolder;
private final DexEncodedMethod resolvedMethod;
public SingleResolutionResult(
- DexClass initialResolutionHolder,
- DexClass resolvedHolder,
- DexEncodedMethod resolvedMethod) {
+ DexClass initialResolutionHolder, T resolvedHolder, DexEncodedMethod resolvedMethod) {
assert initialResolutionHolder != null;
assert resolvedHolder != null;
assert resolvedMethod != null;
@@ -181,11 +218,8 @@
|| initialResolutionHolder.type == resolvedMethod.getHolderType();
}
- public SingleResolutionResult withInitialResolutionHolder(DexClass newInitialResolutionHolder) {
- return newInitialResolutionHolder != initialResolutionHolder
- ? new SingleResolutionResult(newInitialResolutionHolder, resolvedHolder, resolvedMethod)
- : this;
- }
+ public abstract SingleResolutionResult<T> withInitialResolutionHolder(
+ DexClass newInitialResolutionHolder);
@Override
public DexClass getInitialResolutionHolder() {
@@ -193,7 +227,7 @@
}
@Override
- public DexClass getResolvedHolder() {
+ public T getResolvedHolder() {
return resolvedHolder;
}
@@ -225,7 +259,7 @@
}
@Override
- public SingleResolutionResult asSingleResolution() {
+ public SingleResolutionResult<?> asSingleResolution() {
return this;
}
@@ -778,6 +812,108 @@
}
}
+ public static class SingleProgramResolutionResult
+ extends SingleResolutionResult<DexProgramClass> {
+
+ public SingleProgramResolutionResult(
+ DexClass initialResolutionHolder,
+ DexProgramClass resolvedHolder,
+ DexEncodedMethod resolvedMethod) {
+ super(initialResolutionHolder, resolvedHolder, resolvedMethod);
+ }
+
+ @Override
+ public SingleResolutionResult<DexProgramClass> withInitialResolutionHolder(
+ DexClass newInitialResolutionHolder) {
+ return newInitialResolutionHolder != getInitialResolutionHolder()
+ ? new SingleProgramResolutionResult(
+ newInitialResolutionHolder, getResolvedHolder(), getResolvedMethod())
+ : this;
+ }
+
+ @Override
+ public void visitMethodResolutionResults(
+ Consumer<? super SingleResolutionResult<?>> programOrClasspathConsumer,
+ Consumer<? super SingleLibraryResolutionResult> libraryResultConsumer,
+ Consumer<? super ArrayCloneMethodResult> cloneResultConsumer,
+ Consumer<? super FailedResolutionResult> failedResolutionConsumer) {
+ programOrClasspathConsumer.accept(this);
+ }
+
+ @Override
+ public boolean hasProgramResult() {
+ return true;
+ }
+
+ @Override
+ protected SingleProgramResolutionResult asSingleProgramResolutionResult() {
+ return this;
+ }
+ }
+
+ public static class SingleClasspathResolutionResult
+ extends SingleResolutionResult<DexClasspathClass> {
+
+ public SingleClasspathResolutionResult(
+ DexClass initialResolutionHolder,
+ DexClasspathClass resolvedHolder,
+ DexEncodedMethod resolvedMethod) {
+ super(initialResolutionHolder, resolvedHolder, resolvedMethod);
+ }
+
+ @Override
+ public SingleClasspathResolutionResult withInitialResolutionHolder(
+ DexClass newInitialResolutionHolder) {
+ return newInitialResolutionHolder != getInitialResolutionHolder()
+ ? new SingleClasspathResolutionResult(
+ newInitialResolutionHolder, getResolvedHolder(), getResolvedMethod())
+ : this;
+ }
+
+ @Override
+ public void visitMethodResolutionResults(
+ Consumer<? super SingleResolutionResult<?>> programOrClasspathConsumer,
+ Consumer<? super SingleLibraryResolutionResult> libraryResultConsumer,
+ Consumer<? super ArrayCloneMethodResult> cloneResultConsumer,
+ Consumer<? super FailedResolutionResult> failedResolutionConsumer) {
+ programOrClasspathConsumer.accept(this);
+ }
+
+ @Override
+ public SingleClasspathResolutionResult asSingleClasspathResolutionResult() {
+ return this;
+ }
+ }
+
+ public static class SingleLibraryResolutionResult
+ extends SingleResolutionResult<DexLibraryClass> {
+
+ public SingleLibraryResolutionResult(
+ DexClass initialResolutionHolder,
+ DexLibraryClass resolvedHolder,
+ DexEncodedMethod resolvedMethod) {
+ super(initialResolutionHolder, resolvedHolder, resolvedMethod);
+ }
+
+ @Override
+ public SingleLibraryResolutionResult withInitialResolutionHolder(
+ DexClass newInitialResolutionHolder) {
+ return newInitialResolutionHolder != getInitialResolutionHolder()
+ ? new SingleLibraryResolutionResult(
+ newInitialResolutionHolder, getResolvedHolder(), getResolvedMethod())
+ : this;
+ }
+
+ @Override
+ public void visitMethodResolutionResults(
+ Consumer<? super SingleResolutionResult<?>> programOrClasspathConsumer,
+ Consumer<? super SingleLibraryResolutionResult> libraryResultConsumer,
+ Consumer<? super ArrayCloneMethodResult> cloneResultConsumer,
+ Consumer<? super FailedResolutionResult> failedResolutionConsumer) {
+ libraryResultConsumer.accept(this);
+ }
+ }
+
abstract static class EmptyResult extends MethodResolutionResult {
@Override
@@ -870,6 +1006,15 @@
}
@Override
+ public void visitMethodResolutionResults(
+ Consumer<? super SingleResolutionResult<?>> programOrClasspathConsumer,
+ Consumer<? super SingleLibraryResolutionResult> libraryResultConsumer,
+ Consumer<? super ArrayCloneMethodResult> cloneResultConsumer,
+ Consumer<? super FailedResolutionResult> failedResolutionConsumer) {
+ cloneResultConsumer.accept(this);
+ }
+
+ @Override
public boolean isArrayCloneMethodResult() {
return true;
}
@@ -913,6 +1058,15 @@
public boolean hasMethodsCausingError() {
return false;
}
+
+ @Override
+ public void visitMethodResolutionResults(
+ Consumer<? super SingleResolutionResult<?>> programOrClasspathConsumer,
+ Consumer<? super SingleLibraryResolutionResult> libraryResultConsumer,
+ Consumer<? super ArrayCloneMethodResult> cloneResultConsumer,
+ Consumer<? super FailedResolutionResult> failedResolutionConsumer) {
+ failedResolutionConsumer.accept(this);
+ }
}
public static class ClassNotFoundResult extends FailedResolutionResult {
@@ -1038,4 +1192,230 @@
}
}
+ public abstract static class MultipleMethodResolutionResult<
+ C extends DexClass & ProgramOrClasspathClass, T extends SingleResolutionResult<C>>
+ extends MethodResolutionResult {
+
+ protected final T programOrClasspathResult;
+ protected final List<SingleLibraryResolutionResult> libraryResolutionResults;
+ protected final List<FailedResolutionResult> failedResolutionResults;
+
+ public MultipleMethodResolutionResult(
+ T programOrClasspathResult,
+ List<SingleLibraryResolutionResult> libraryResolutionResults,
+ List<FailedResolutionResult> failedResolutionResults) {
+ this.programOrClasspathResult = programOrClasspathResult;
+ this.libraryResolutionResults = libraryResolutionResults;
+ this.failedResolutionResults = failedResolutionResults;
+ }
+
+ @Override
+ public OptionalBool isAccessibleFrom(
+ ProgramDefinition context, AppInfoWithClassHierarchy appInfo) {
+ throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
+ }
+
+ @Override
+ public OptionalBool isAccessibleForVirtualDispatchFrom(
+ ProgramDefinition context, AppInfoWithClassHierarchy appInfo) {
+ throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
+ }
+
+ @Override
+ public boolean isVirtualTarget() {
+ throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
+ }
+
+ @Override
+ public DexClassAndMethod lookupInvokeSpecialTarget(
+ DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
+ throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
+ }
+
+ @Override
+ public DexClassAndMethod lookupInvokeSuperTarget(
+ DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
+ throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
+ }
+
+ @Override
+ public DexEncodedMethod lookupInvokeDirectTarget(
+ DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
+ throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
+ }
+
+ @Override
+ public DexEncodedMethod lookupInvokeStaticTarget(
+ DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
+ throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
+ }
+
+ @Override
+ public LookupResult lookupVirtualDispatchTargets(
+ DexProgramClass context,
+ AppInfoWithClassHierarchy appInfo,
+ InstantiatedSubTypeInfo instantiatedInfo,
+ PinnedPredicate pinnedPredicate) {
+ throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
+ }
+
+ @Override
+ public LookupResult lookupVirtualDispatchTargets(
+ DexProgramClass context,
+ AppInfoWithLiveness appInfo,
+ DexProgramClass refinedReceiverUpperBound,
+ DexProgramClass refinedReceiverLowerBound) {
+ throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
+ }
+
+ @Override
+ public LookupTarget lookupVirtualDispatchTarget(
+ InstantiatedObject instance, AppInfoWithClassHierarchy appInfo) {
+ throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
+ }
+
+ @Override
+ public DexClassAndMethod lookupVirtualDispatchTarget(
+ DexClass dynamicInstance, AppInfoWithClassHierarchy appInfo) {
+ throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
+ }
+
+ @Override
+ public LookupTarget lookupVirtualDispatchTarget(
+ LambdaDescriptor lambdaInstance,
+ AppInfoWithClassHierarchy appInfo,
+ Consumer<? super DexEncodedMethod> methodCausingFailureConsumer) {
+ throw new Unreachable("Should not be called on MultipleFieldResolutionResult");
+ }
+
+ @Override
+ public void visitMethodResolutionResults(
+ Consumer<? super SingleResolutionResult<?>> programOrClasspathConsumer,
+ Consumer<? super SingleLibraryResolutionResult> libraryResultConsumer,
+ Consumer<? super ArrayCloneMethodResult> cloneResultConsumer,
+ Consumer<? super FailedResolutionResult> failedResolutionConsumer) {
+ if (programOrClasspathResult != null) {
+ programOrClasspathConsumer.accept(programOrClasspathResult);
+ }
+ libraryResolutionResults.forEach(libraryResultConsumer);
+ failedResolutionResults.forEach(failedResolutionConsumer);
+ }
+ }
+
+ public static class MultipleProgramWithLibraryResolutionResult
+ extends MultipleMethodResolutionResult<DexProgramClass, SingleProgramResolutionResult> {
+
+ public MultipleProgramWithLibraryResolutionResult(
+ SingleProgramResolutionResult programOrClasspathResult,
+ List<SingleLibraryResolutionResult> libraryResolutionResults,
+ List<FailedResolutionResult> failedOrUnknownResolutionResults) {
+ super(programOrClasspathResult, libraryResolutionResults, failedOrUnknownResolutionResults);
+ }
+ }
+
+ public static class MultipleClasspathWithLibraryResolutionResult
+ extends MultipleMethodResolutionResult<DexClasspathClass, SingleClasspathResolutionResult> {
+
+ public MultipleClasspathWithLibraryResolutionResult(
+ SingleClasspathResolutionResult programOrClasspathResult,
+ List<SingleLibraryResolutionResult> libraryResolutionResults,
+ List<FailedResolutionResult> failedOrUnknownResolutionResults) {
+ super(programOrClasspathResult, libraryResolutionResults, failedOrUnknownResolutionResults);
+ }
+ }
+
+ public static class MultipleLibraryMethodResolutionResult
+ extends MultipleMethodResolutionResult<DexProgramClass, SingleProgramResolutionResult> {
+
+ public MultipleLibraryMethodResolutionResult(
+ List<SingleLibraryResolutionResult> libraryResolutionResults,
+ List<FailedResolutionResult> failedOrUnknownResolutionResults) {
+ super(null, libraryResolutionResults, failedOrUnknownResolutionResults);
+ }
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+
+ private MethodResolutionResult possiblySingleResult = null;
+ private List<MethodResolutionResult> allResults = null;
+
+ private Builder() {}
+
+ public void addResolutionResult(MethodResolutionResult result) {
+ if (possiblySingleResult == null) {
+ possiblySingleResult = result;
+ return;
+ }
+ if (allResults == null) {
+ allResults = new ArrayList<>();
+ allResults.add(possiblySingleResult);
+ }
+ allResults.add(result);
+ }
+
+ public MethodResolutionResult buildOrIfEmpty(MethodResolutionResult emptyResult) {
+ if (possiblySingleResult == null) {
+ return emptyResult;
+ } else if (allResults == null) {
+ return possiblySingleResult;
+ }
+ Box<SingleResolutionResult<?>> singleResult = new Box<>();
+ List<SingleLibraryResolutionResult> libraryResults = new ArrayList<>();
+ List<FailedResolutionResult> failedResults = new ArrayList<>();
+ allResults.forEach(
+ otherResult -> {
+ otherResult.visitMethodResolutionResults(
+ otherProgramOrClasspathResult -> {
+ if (singleResult.isSet()) {
+ assert false : "Unexpected multiple results between program and classpath";
+ if (singleResult.get().hasProgramResult()) {
+ return;
+ }
+ }
+ singleResult.set(otherProgramOrClasspathResult);
+ },
+ newLibraryResult -> {
+ if (!Iterables.any(
+ libraryResults,
+ existing ->
+ existing.getResolvedHolder() == newLibraryResult.getResolvedHolder())) {
+ libraryResults.add(newLibraryResult);
+ }
+ },
+ ConsumerUtils.emptyConsumer(),
+ newFailedResult -> {
+ if (!Iterables.any(
+ failedResults,
+ existing ->
+ existing.isFailedResolution() == newFailedResult.isFailedResolution())) {
+ failedResults.add(newFailedResult);
+ }
+ });
+ });
+ if (!singleResult.isSet()) {
+ if (libraryResults.size() == 1 && failedResults.isEmpty()) {
+ return libraryResults.get(0);
+ } else if (libraryResults.isEmpty() && failedResults.size() == 1) {
+ return failedResults.get(0);
+ } else {
+ return new MultipleLibraryMethodResolutionResult(libraryResults, failedResults);
+ }
+ } else if (libraryResults.isEmpty() && failedResults.isEmpty()) {
+ return singleResult.get();
+ } else if (singleResult.get().hasProgramResult()) {
+ return new MultipleProgramWithLibraryResolutionResult(
+ singleResult.get().asSingleProgramResolutionResult(), libraryResults, failedResults);
+ } else {
+ SingleClasspathResolutionResult classpathResult =
+ singleResult.get().asSingleClasspathResolutionResult();
+ assert classpathResult != null;
+ return new MultipleClasspathWithLibraryResolutionResult(
+ classpathResult, libraryResults, failedResults);
+ }
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java b/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
index b61f288..f303854 100644
--- a/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
@@ -234,6 +234,10 @@
return ImmutableList.of();
}
+ public Set<DexType> allImmediateSubtypes(DexType type) {
+ return getTypeInfo(type).directSubtypes;
+ }
+
public void forAllInterfaceRoots(Consumer<DexType> fn) {
Iterables.filter(
getTypeInfo(factory.objectType).directSubtypes,
@@ -245,12 +249,13 @@
private final DexType type;
- int hierarchyLevel = UNKNOWN_LEVEL;
+ private int hierarchyLevel = UNKNOWN_LEVEL;
+
/**
* Set of direct subtypes. This set has to remain sorted to ensure determinism. The actual
* sorting is not important but {@link DexType#compareTo(StructuralItem)} works well.
*/
- Set<DexType> directSubtypes = NO_DIRECT_SUBTYPE;
+ private Set<DexType> directSubtypes = NO_DIRECT_SUBTYPE;
TypeInfo(DexType type) {
this.type = type;
@@ -296,32 +301,28 @@
}
}
- synchronized void addDirectSubtype(TypeInfo subtypeInfo) {
+ private void addDirectSubtype(TypeInfo subtypeInfo) {
assert hierarchyLevel != UNKNOWN_LEVEL;
ensureDirectSubTypeSet();
directSubtypes.add(subtypeInfo.type);
subtypeInfo.setLevel(hierarchyLevel + 1);
}
- void tagAsSubtypeRoot() {
+ private void tagAsSubtypeRoot() {
setLevel(ROOT_LEVEL);
}
- void tagAsInterface() {
+ private void tagAsInterface() {
setLevel(INTERFACE_LEVEL);
}
- public boolean isInterface() {
+ private boolean isInterface() {
assert hierarchyLevel != UNKNOWN_LEVEL : "Program class missing: " + this;
assert type.isClassType();
return hierarchyLevel == INTERFACE_LEVEL;
}
- public boolean isUnknown() {
- return hierarchyLevel == UNKNOWN_LEVEL;
- }
-
- synchronized void addInterfaceSubtype(DexType type) {
+ private void addInterfaceSubtype(DexType type) {
// Interfaces all inherit from java.lang.Object. However, we assign a special level to
// identify them later on.
setLevel(INTERFACE_LEVEL);
@@ -329,21 +330,4 @@
directSubtypes.add(type);
}
}
-
- public Set<DexType> allImmediateSubtypes(DexType type) {
- return getTypeInfo(type).directSubtypes;
- }
-
- public boolean isUnknown(DexType type) {
- return getTypeInfo(type).isUnknown();
- }
-
- public boolean hasSubtypes(DexType type) {
- return !getTypeInfo(type).directSubtypes.isEmpty();
- }
-
- void registerNewType(DexType newType, DexType superType) {
- // Register the relationship between this type and its superType.
- getTypeInfo(superType).addDirectSubtype(getTypeInfo(newType));
- }
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
index 2ff1bfb..eb5b57e 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
@@ -44,7 +44,7 @@
import com.android.tools.r8.horizontalclassmerging.policies.NoVirtualMethodMerging;
import com.android.tools.r8.horizontalclassmerging.policies.NoWeakerAccessPrivileges;
import com.android.tools.r8.horizontalclassmerging.policies.NotMatchedByNoHorizontalClassMerging;
-import com.android.tools.r8.horizontalclassmerging.policies.OnlyClassesWithStaticDefinitions;
+import com.android.tools.r8.horizontalclassmerging.policies.OnlyClassesWithStaticDefinitionsAndNoClassInitializer;
import com.android.tools.r8.horizontalclassmerging.policies.OnlyDirectlyConnectedOrUnrelatedInterfaces;
import com.android.tools.r8.horizontalclassmerging.policies.PreserveMethodCharacteristics;
import com.android.tools.r8.horizontalclassmerging.policies.PreventClassMethodAndDefaultMethodCollisions;
@@ -137,7 +137,7 @@
ImmutableList.Builder<SingleClassPolicy> builder =
ImmutableList.<SingleClassPolicy>builder()
.add(new CheckSyntheticClasses(appView))
- .add(new OnlyClassesWithStaticDefinitions());
+ .add(new OnlyClassesWithStaticDefinitionsAndNoClassInitializer());
assert verifySingleClassPoliciesIrrelevantForMergingSyntheticsInD8(appView, mode, builder);
return builder.build();
}
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 1103c8f..6c8703b 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -57,7 +57,7 @@
private DexMethod superMethod(
AppView<? extends AppInfoWithClassHierarchy> appView, MergeGroup group) {
DexMethod template = methods.iterator().next().getReference();
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appView
.appInfo()
.resolveMethodOnClass(group.getSuperType(), template)
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVirtualMethodMerging.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVirtualMethodMerging.java
index a914cf9..fecb888 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVirtualMethodMerging.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVirtualMethodMerging.java
@@ -105,7 +105,7 @@
}
private boolean hasNonAbstractDefinitionInSuperClass(DexType superType, ProgramMethod method) {
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appView
.appInfo()
.resolveMethodOnClass(superType, method.getReference())
@@ -118,7 +118,7 @@
return Iterables.any(
interfaceTypes,
interfaceType -> {
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appView
.appInfo()
.resolveMethodOnInterface(interfaceType, method.getReference())
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyClassesWithStaticDefinitions.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyClassesWithStaticDefinitionsAndNoClassInitializer.java
similarity index 81%
rename from src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyClassesWithStaticDefinitions.java
rename to src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyClassesWithStaticDefinitionsAndNoClassInitializer.java
index 736ede0..8b6334a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyClassesWithStaticDefinitions.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyClassesWithStaticDefinitionsAndNoClassInitializer.java
@@ -9,10 +9,13 @@
import com.google.common.collect.Iterables;
/** Prevent merging of classes that has non-static methods or fields. */
-public class OnlyClassesWithStaticDefinitions extends SingleClassPolicy {
+public class OnlyClassesWithStaticDefinitionsAndNoClassInitializer extends SingleClassPolicy {
@Override
public boolean canMerge(DexProgramClass program) {
+ if (program.hasClassInitializer()) {
+ return false;
+ }
return !Iterables.any(program.members(), member -> !member.isStatic());
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventClassMethodAndDefaultMethodCollisions.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventClassMethodAndDefaultMethodCollisions.java
index 54bcf57..ca52f7c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventClassMethodAndDefaultMethodCollisions.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventClassMethodAndDefaultMethodCollisions.java
@@ -170,7 +170,7 @@
MethodCategory category = MethodCategory.CLASS_HIERARCHY_SAFE;
if (clazzReserved.contains(signature)) {
DexMethod template = signature.withHolder(clazz, appView.dexItemFactory());
- SingleResolutionResult result =
+ SingleResolutionResult<?> result =
appView.appInfo().resolveMethodOnClass(clazz, template).asSingleResolution();
if (result == null || result.getResolvedHolder().isInterface()) {
category = MethodCategory.KEEP_ABSENT;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
index d6ca793..d2b1d3b 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
@@ -331,8 +331,8 @@
if (definition.isStatic() != isStatic
|| appView.isCfByteCodePassThrough(getContext().getDefinition())
- || resolutionResult.isAccessibleFrom(getContext(), appView).isPossiblyFalse()
|| !resolutionResult.isSingleProgramFieldResolutionResult()
+ || resolutionResult.isAccessibleFrom(getContext(), appView).isPossiblyFalse()
|| appView.appInfo().isNeverReprocessMethod(getContext())) {
recordAccessThatCannotBeOptimized(field, definition);
return;
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
index f7a77af..b9f07e3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
@@ -799,11 +799,6 @@
BasicBlock invokePredecessor = invokeBlock.getPredecessors().get(0);
BasicBlock invokeSuccessor = invokeBlock.getSuccessors().get(0);
- // Invalidate position-on-throwing-instructions property if it does not hold for the inlinee.
- if (!inlinee.doAllThrowingInstructionsHavePositions()) {
- code.setAllThrowingInstructionsHavePositions(false);
- }
-
Set<Value> argumentUsers = Sets.newIdentityHashSet();
// Map all argument values. The first one needs special handling if there is a downcast type.
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index 1544bdd..53fa359 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -125,10 +125,6 @@
private boolean numbered = false;
private int nextInstructionNumber = 0;
- // Initial value indicating if the code does have actual positions on all throwing instructions.
- // If this is the case, which holds for javac code, then we want to ensure that it remains so.
- private boolean allThrowingInstructionsHavePositions;
-
private final IRMetadata metadata;
private final InternalOptions options;
@@ -154,8 +150,6 @@
this.basicBlockNumberGenerator = basicBlockNumberGenerator;
this.metadata = metadata;
this.origin = origin;
- // TODO(zerny): Remove or update this property now that all instructions have positions.
- allThrowingInstructionsHavePositions = computeAllThrowingInstructionsHavePositions();
}
public IRMetadata metadata() {
@@ -642,7 +636,7 @@
assert consistentCatchHandlers();
assert consistentBlockInstructions(appView, ssa);
assert consistentMetadata();
- assert !allThrowingInstructionsHavePositions || computeAllThrowingInstructionsHavePositions();
+ assert verifyAllThrowingInstructionsHavePositions();
return true;
}
@@ -1233,15 +1227,7 @@
return createNumberConstant(0, TypeElement.getNull(), local);
}
- public boolean doAllThrowingInstructionsHavePositions() {
- return allThrowingInstructionsHavePositions;
- }
-
- public void setAllThrowingInstructionsHavePositions(boolean value) {
- this.allThrowingInstructionsHavePositions = value;
- }
-
- private boolean computeAllThrowingInstructionsHavePositions() {
+ private boolean verifyAllThrowingInstructionsHavePositions() {
for (Instruction instruction : instructions()) {
if (instruction.instructionTypeCanThrow()
&& !instruction.isConstString()
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
index cdf992b..c13800c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
@@ -197,7 +197,7 @@
AppView<? extends AppInfoWithClassHierarchy> appViewWithClassHierarchy =
appView.withClassHierarchy();
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appViewWithClassHierarchy
.appInfo()
.resolveMethod(getInvokedMethod(), getInterfaceBit())
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
index 7aa20ad..f39baa4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -196,7 +196,7 @@
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
AppInfoWithLiveness appInfoWithLiveness = appViewWithLiveness.appInfo();
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appViewWithLiveness
.appInfo()
.resolveMethod(getInvokedMethod(), isInterface)
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 9ccaec9..fc23097 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.experimental.startup.StartupInstrumentation;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.Code;
@@ -468,6 +469,8 @@
D8CfInstructionDesugaringEventConsumer desugaringEventConsumer,
D8MethodProcessor methodProcessor,
InterfaceProcessor interfaceProcessor) {
+ new StartupInstrumentation(appView).instrumentClass(clazz);
+
// When converting all methods on a class always convert <clinit> first.
ProgramMethod classInitializer = clazz.getProgramClassInitializer();
@@ -483,7 +486,6 @@
for (ProgramMethod method : methods) {
if (!method.getDefinition().isClassInitializer()) {
- DexEncodedMethod definition = method.getDefinition();
methodProcessor.processMethod(method, desugaringEventConsumer);
if (interfaceProcessor != null) {
interfaceProcessor.processMethod(method, desugaringEventConsumer);
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 eb5a30b..c039f9d 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
@@ -249,7 +249,7 @@
return;
}
AndroidApiLevel theApi = apiLevel.asKnownApiLevel().getApiLevel();
- if (appView.typeRewriter.hasRewrittenType(type, appView)) {
+ if (typeIsInDesugaredLibrary(type)) {
assert theApi.equals(appView.options().getMinApiLevel());
return;
}
@@ -258,6 +258,15 @@
return true;
}
+ private boolean typeIsInDesugaredLibrary(DexType type) {
+ return appView.typeRewriter.hasRewrittenType(type, appView)
+ || appView
+ .options()
+ .machineDesugaredLibrarySpecification
+ .getMaintainType()
+ .contains(type);
+ }
+
private boolean typeIsAbsentOrPresentWithoutBackportsFrom(
DexType type, AndroidApiLevel apiLevel) {
return !typeIsPresent(type) || typeIsPresentWithoutBackportsFrom(type, apiLevel);
@@ -268,7 +277,7 @@
}
private boolean typeIsPresentWithoutBackportsFrom(DexType type, AndroidApiLevel methodsMinAPI) {
- if (appView.typeRewriter.hasRewrittenType(type, appView)) {
+ if (typeIsInDesugaredLibrary(type)) {
// Desugared library is enabled, the methods are present if desugared library specifies it.
return methodsMinAPI.isGreaterThan(AndroidApiLevel.N)
&& !appView.options().machineDesugaredLibrarySpecification.includesJDK11Methods();
@@ -284,7 +293,7 @@
private boolean typeIsPresent(DexType type) {
// TODO(b/224954240): Always use the apiDatabase when always available.
return appView.options().getMinApiLevel().isGreaterThanOrEqualTo(typeMinApi.get(type))
- || appView.typeRewriter.hasRewrittenType(type, appView);
+ || typeIsInDesugaredLibrary(type);
}
boolean isEmpty() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index d01f7fc..012c31c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -381,7 +381,7 @@
appView.dexItemFactory().icceType,
descriptor.implHandle.isInterface);
}
- SingleResolutionResult result = resolution.asSingleResolution();
+ SingleResolutionResult<?> result = resolution.asSingleResolution();
assert result.getResolvedMethod().isStatic();
assert result.getResolvedHolder().isProgramClass();
return new StaticLambdaImplTarget(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
index 48dca2d..8935159 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
@@ -123,7 +123,7 @@
.resolveMethod(bootstrapMethodReference, bootstrapMethodHandle.isInterface);
if (resolution.isSingleResolution()
&& resolution.asSingleResolution().getResolvedMethod().isStatic()) {
- SingleResolutionResult result = resolution.asSingleResolution();
+ SingleResolutionResult<?> result = resolution.asSingleResolution();
if (bootstrapMethodHandle.isInterface
&& appView.options().isInterfaceMethodDesugaringEnabled()) {
bootstrapMethodReference =
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
index b0c7920..50e98e1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
@@ -172,6 +172,7 @@
public boolean isEmpty() {
return rewritePrefix.isEmpty()
&& rewriteDerivedPrefix.isEmpty()
+ && maintainPrefix.isEmpty()
&& emulatedInterfaces.isEmpty()
&& retargetMethod.isEmpty()
&& retargetMethodEmulatedDispatch.isEmpty()
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
index d974c56..7367257 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
@@ -52,6 +52,10 @@
this.rewritingFlags = rewritingFlags;
}
+ public boolean isEmpty() {
+ return rewritingFlags.isEmpty();
+ }
+
public boolean isLibraryCompilation() {
return libraryCompilation;
}
@@ -167,7 +171,10 @@
public boolean isSupported(DexReference reference) {
// Support through type rewriting.
- if (rewritingFlags.getRewriteType().containsKey(reference.getContextType())) {
+ if (getRewriteType().containsKey(reference.getContextType())) {
+ return true;
+ }
+ if (getMaintainType().contains(reference.getContextType())) {
return true;
}
if (!reference.isDexMethod()) {
@@ -193,6 +200,10 @@
return topLevelFlags.getRequiredCompilationAPILevel();
}
+ public boolean requiresTypeRewriting() {
+ return !getRewriteType().isEmpty() || !getRewriteDerivedTypeOnly().isEmpty();
+ }
+
private int getLeadingVersionNumber() {
if (leadingVersionNumberCache != -1) {
return leadingVersionNumberCache;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
index dcaa744..1a301da 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
@@ -196,6 +196,15 @@
return emulatedInterfaces.get(method.getHolderType()).getEmulatedMethods().get(method);
}
+ public boolean isEmpty() {
+ return rewriteType.isEmpty()
+ && maintainType.isEmpty()
+ && rewriteDerivedTypeOnly.isEmpty()
+ && !hasRetargeting()
+ && emulatedInterfaces.isEmpty()
+ && legacyBackport.isEmpty();
+ }
+
public static class Builder {
Builder() {}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java
index ef19eb6..567cc79 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java
@@ -26,6 +26,8 @@
levelType = app.dexItemFactory.createType("Ljava/time/LocalTime;");
} else if (requiredCompilationAPILevel.isEqualTo(AndroidApiLevel.R)) {
levelType = app.dexItemFactory.createType("Ljava/util/concurrent/Flow;");
+ } else if (requiredCompilationAPILevel.isEqualTo(AndroidApiLevel.N)) {
+ levelType = app.dexItemFactory.createType("Ljava/util/function/Supplier;");
} else {
app.options.reporter.warning(
"Unsupported requiredCompilationAPILevel: " + requiredCompilationAPILevel);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
index 4446275..266b1c5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -377,7 +377,8 @@
this.dexItemFactory = appView.dexItemFactory();
this.helper = new InterfaceDesugaringSyntheticHelper(appView);
needsLibraryInfo =
- appView.options().machineDesugaredLibrarySpecification.hasEmulatedInterfaces();
+ !appView.options().canUseDefaultAndStaticInterfaceMethods()
+ && !appView.options().machineDesugaredLibrarySpecification.isEmpty();
this.isLiveMethod = isLiveMethod;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index 778ed23..7b3bb07 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -405,7 +405,7 @@
.build();
}
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appView
.appInfoForDesugaring()
.resolveMethodOnInterface(holder, invoke.getMethod())
@@ -435,7 +435,7 @@
private DesugarDescription computeInvokeVirtualDispatch(
DexClass holder, CfInvoke invoke, ProgramMethod context) {
AppInfoWithClassHierarchy appInfoForDesugaring = appView.appInfoForDesugaring();
- SingleResolutionResult resolution =
+ SingleResolutionResult<?> resolution =
appInfoForDesugaring
.resolveMethod(invoke.getMethod(), invoke.isInterface())
.asSingleResolution();
@@ -497,7 +497,7 @@
return computeInvokeAsThrowRewrite(invoke, null, context);
}
- SingleResolutionResult singleResolution = resolution.asSingleResolution();
+ SingleResolutionResult<?> singleResolution = resolution.asSingleResolution();
if (singleResolution == null) {
return DesugarDescription.nothing();
}
@@ -573,7 +573,7 @@
}
private DesugarDescription computeInvokeAsThrowRewrite(
- CfInvoke invoke, SingleResolutionResult resolution, ProgramMethod context) {
+ CfInvoke invoke, SingleResolutionResult<?> resolution, ProgramMethod context) {
assert !isAlreadyDesugared(invoke, context);
return AlwaysThrowingInstructionDesugaring.computeInvokeAsThrowRewrite(
appView, invoke, resolution);
@@ -637,7 +637,7 @@
.build();
}
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appView.appInfoForDesugaring().resolveMethodOn(clazz, invokedMethod).asSingleResolution();
if (clazz.isInterface() && shouldRewriteToInvokeToThrow(resolutionResult, false)) {
return computeInvokeAsThrowRewrite(invoke, resolutionResult, context);
@@ -760,7 +760,7 @@
}
private boolean shouldRewriteToInvokeToThrow(
- SingleResolutionResult resolutionResult, boolean isInvokeStatic) {
+ SingleResolutionResult<?> resolutionResult, boolean isInvokeStatic) {
return resolutionResult == null
|| resolutionResult.getResolvedMethod().isStatic() != isInvokeStatic;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
index 97c8d47..1c49fc9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
@@ -227,7 +227,7 @@
private boolean computeAssumedValuesFromSingleTarget(
IRCode code, InvokeMethod invoke, AssumedValues.Builder assumedValuesBuilder) {
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appView
.appInfo()
.unsafeResolveMethodDueToDexFormat(invoke.getInvokedMethod())
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 9490667..3788e98 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
@@ -124,7 +124,7 @@
@Override
public boolean passesInliningConstraints(
InvokeMethod invoke,
- SingleResolutionResult resolutionResult,
+ SingleResolutionResult<?> resolutionResult,
ProgramMethod singleTarget,
Reason reason,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
@@ -261,7 +261,7 @@
public InlineResult computeInlining(
IRCode code,
InvokeMethod invoke,
- SingleResolutionResult resolutionResult,
+ SingleResolutionResult<?> resolutionResult,
ProgramMethod singleTarget,
ProgramMethod context,
ClassInitializationAnalysis classInitializationAnalysis,
@@ -336,7 +336,7 @@
private boolean neverInline(
InvokeMethod invoke,
- SingleResolutionResult resolutionResult,
+ SingleResolutionResult<?> resolutionResult,
ProgramMethod singleTarget,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
AppInfoWithLiveness appInfo = appView.appInfo();
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 cd5b8b2..9ba2beb 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
@@ -368,7 +368,7 @@
return target;
}
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appView.appInfo().resolveMethodOnClass(target.getHolderType(), target).asSingleResolution();
if (resolutionResult == null
|| resolutionResult
@@ -384,7 +384,7 @@
return target;
}
- SingleResolutionResult newResolutionResult =
+ SingleResolutionResult<?> newResolutionResult =
appView.appInfo().resolveMethodOnClass(receiverType, target).asSingleResolution();
if (newResolutionResult == null
|| newResolutionResult
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 493b052..be64b57 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
@@ -50,7 +50,7 @@
@Override
public boolean passesInliningConstraints(
InvokeMethod invoke,
- SingleResolutionResult resolutionResult,
+ SingleResolutionResult<?> resolutionResult,
ProgramMethod candidate,
Reason reason,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
@@ -70,7 +70,7 @@
public InlineResult computeInlining(
IRCode code,
InvokeMethod invoke,
- SingleResolutionResult resolutionResult,
+ SingleResolutionResult<?> resolutionResult,
ProgramMethod singleTarget,
ProgramMethod context,
ClassInitializationAnalysis classInitializationAnalysis,
@@ -88,7 +88,7 @@
private InlineAction computeForInvoke(
InvokeMethod invoke,
- SingleResolutionResult resolutionResult,
+ SingleResolutionResult<?> resolutionResult,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
Inliner.InliningInfo info = invokesToInline.get(invoke);
if (info == null) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
index 4b3bb6f..265d097 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
@@ -137,7 +137,7 @@
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
AppInfoWithLiveness appInfoWithLiveness = appViewWithLiveness.appInfo();
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appInfoWithLiveness
.resolveMethod(invoke.getInvokedMethod(), invoke.getInterfaceBit())
.asSingleResolution();
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 577dbf2..4ca07c5 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
@@ -960,7 +960,7 @@
InvokeMethod invoke = current.asInvokeMethod();
// TODO(b/142116551): This should be equivalent to invoke.lookupSingleTarget()!
DexMethod invokedMethod = invoke.getInvokedMethod();
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appView
.appInfo()
.resolveMethod(invokedMethod, invoke.getInterfaceBit())
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
index ddc31c2..b484dda 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
@@ -26,7 +26,7 @@
boolean passesInliningConstraints(
InvokeMethod invoke,
- SingleResolutionResult resolutionResult,
+ SingleResolutionResult<?> resolutionResult,
ProgramMethod candidate,
Reason reason,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter);
@@ -34,7 +34,7 @@
InlineResult computeInlining(
IRCode code,
InvokeMethod invoke,
- SingleResolutionResult resolutionResult,
+ SingleResolutionResult<?> resolutionResult,
ProgramMethod singleTarget,
ProgramMethod context,
ClassInitializationAnalysis classInitializationAnalysis,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index 7e494f3..09b614e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -289,7 +289,7 @@
return;
}
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appView.appInfo().unsafeResolveMethodDueToDexFormat(invokedMethod).asSingleResolution();
if (resolutionResult == null) {
return;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MultiCallerInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/MultiCallerInliner.java
index 8312643..1a35898 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MultiCallerInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MultiCallerInliner.java
@@ -88,7 +88,7 @@
continue;
}
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appView
.appInfo()
.resolveMethod(invoke.getInvokedMethod(), invoke.getInterfaceBit())
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 974bedd..0b876a7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -291,7 +291,7 @@
if (user.isInvokeMethod()) {
InvokeMethod invoke = user.asInvokeMethod();
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appView
.appInfo()
.resolveMethod(invoke.getInvokedMethod(), invoke.getInterfaceBit())
@@ -1071,7 +1071,7 @@
//
private boolean isEligibleDirectMethodCall(
InvokeMethod invoke,
- SingleResolutionResult resolutionResult,
+ SingleResolutionResult<?> resolutionResult,
ProgramMethod singleTarget,
Supplier<InliningOracle> defaultOracle,
Set<Instruction> indirectUsers) {
@@ -1175,7 +1175,7 @@
if (!usage.isBottom()) {
NonEmptyParameterUsage nonEmptyUsage = usage.asNonEmpty();
for (DexMethod invokedMethod : nonEmptyUsage.getMethodCallsWithParameterAsReceiver()) {
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appView.appInfo().resolveMethodOn(eligibleClass, invokedMethod).asSingleResolution();
if (resolutionResult == null || !resolutionResult.getResolvedHolder().isProgramClass()) {
return false;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
index 0e4a8dd..ebad797 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
@@ -247,7 +247,7 @@
return state.abandonClassInliningInCurrentContexts(receiverRoot);
}
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appView
.appInfo()
.resolveMethodOnClassHolder(invoke.getInvokedMethod())
@@ -286,7 +286,7 @@
return state;
}
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appView
.appInfo()
.resolveMethodOnInterfaceHolder(invoke.getInvokedMethod())
@@ -302,7 +302,7 @@
private ParameterUsages analyzeInvokeStatic(InvokeStatic invoke, NonEmptyParameterUsages state) {
// We generally don't class inline instances that escape through invoke-static calls, but we
// make an exception for calls to Objects.requireNonNull().
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appView
.appInfo()
.unsafeResolveMethodDueToDexFormat(invoke.getInvokedMethod())
@@ -328,7 +328,7 @@
return state;
}
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appView
.appInfo()
.resolveMethodOnClassHolder(invoke.getInvokedMethod())
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
index f455175..b2d2531 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
@@ -1338,12 +1338,12 @@
// Calls to java.lang.System.
if (targetHolder.getType() == factory.javaLangSystemType) {
- if (singleTargetReference == factory.javaLangSystemMethods.arraycopy) {
+ if (singleTargetReference == factory.javaLangSystemMembers.arraycopy) {
// Important for Kotlin 1.5 enums, which use arraycopy to create a copy of $VALUES instead
// of int[].clone().
return Reason.ELIGIBLE;
}
- if (singleTargetReference == factory.javaLangSystemMethods.identityHashCode) {
+ if (singleTargetReference == factory.javaLangSystemMembers.identityHashCode) {
// Important for proto enum unboxing.
return Reason.ELIGIBLE;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index bc981ea..cfec469 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -440,9 +440,9 @@
// Calls to java.lang.System.
if (invokedMethod.getHolderType() == factory.javaLangSystemType) {
- if (invokedMethod == factory.javaLangSystemMethods.arraycopy) {
+ if (invokedMethod == factory.javaLangSystemMembers.arraycopy) {
// Intentionally empty.
- } else if (invokedMethod == factory.javaLangSystemMethods.identityHashCode) {
+ } else if (invokedMethod == factory.javaLangSystemMembers.identityHashCode) {
assert invoke.arguments().size() == 1;
Value argument = invoke.getFirstArgument();
DexType enumType = getEnumTypeOrNull(argument, convertedEnums);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java
index 55d5c50..5edfe2e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java
@@ -314,7 +314,7 @@
new CfConstNumber(0, ValueType.INT),
new CfLoad(ValueType.INT, argumentLocalSlot),
new CfInvoke(
- Opcodes.INVOKESTATIC, dexItemFactory.javaLangSystemMethods.arraycopy, false),
+ Opcodes.INVOKESTATIC, dexItemFactory.javaLangSystemMembers.arraycopy, false),
// return result
new CfLoad(ValueType.OBJECT, resultLocalSlot),
new CfReturn(ValueType.OBJECT)));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/sideeffects/JavaLangObjectsSideEffectCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/library/sideeffects/JavaLangObjectsSideEffectCollection.java
index 34514e3..9d4ae82 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/sideeffects/JavaLangObjectsSideEffectCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/sideeffects/JavaLangObjectsSideEffectCollection.java
@@ -48,7 +48,7 @@
// Check if this is a program class with a toString() method that does not have side effects.
DexClass clazz = appInfo.definitionFor(classType);
if (clazz != null && clazz.isEffectivelyFinal(appView)) {
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appInfo.resolveMethodOn(clazz, toStringMethodReference).asSingleResolution();
if (resolutionResult != null
&& !resolutionResult.getResolvedMethod().getOptimizationInfo().mayHaveSideEffects()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfoLookup.java b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfoLookup.java
index b28520c..d207ac7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfoLookup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfoLookup.java
@@ -18,7 +18,7 @@
public static AssumeInfo lookupAssumeInfo(
AppView<AppInfoWithLiveness> appView,
- SingleResolutionResult resolutionResult,
+ SingleResolutionResult<?> resolutionResult,
DexClassAndMethod singleTarget) {
AssumeInfo resolutionLookup = lookupAssumeInfo(appView, resolutionResult.getResolutionPair());
if (resolutionLookup == null) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java b/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java
index 93f6242..ef2f42d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java
@@ -173,7 +173,7 @@
return null;
}
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appView.appInfo().resolveMethodOn(superClass, method.getReference()).asSingleResolution();
if (resolutionResult == null) {
return null;
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
index d67d177..7657adc 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -126,8 +126,8 @@
boolean allowEmptyMappedRanges,
boolean allowExperimentalMapping)
throws IOException {
- return mapperFromBufferedReader(
- CharSource.wrap(contents).openBufferedStream(),
+ return mapperFromLineReader(
+ LineReader.fromBufferedReader(CharSource.wrap(contents).openBufferedStream()),
diagnosticsHandler,
allowEmptyMappedRanges,
allowExperimentalMapping);
@@ -144,6 +144,19 @@
boolean allowEmptyMappedRanges,
boolean allowExperimentalMapping)
throws IOException {
+ return mapperFromLineReader(
+ LineReader.fromBufferedReader(reader),
+ diagnosticsHandler,
+ allowEmptyMappedRanges,
+ allowExperimentalMapping);
+ }
+
+ public static ClassNameMapper mapperFromLineReader(
+ LineReader reader,
+ DiagnosticsHandler diagnosticsHandler,
+ boolean allowEmptyMappedRanges,
+ boolean allowExperimentalMapping)
+ throws IOException {
try (ProguardMapReader proguardReader =
new ProguardMapReader(
reader,
@@ -156,8 +169,8 @@
}
}
- public static ClassNameMapper mapperFromBufferedReaderWithFiltering(
- BufferedReader reader,
+ public static ClassNameMapper mapperFromLineReaderWithFiltering(
+ LineReader reader,
DiagnosticsHandler diagnosticsHandler,
boolean allowEmptyMappedRanges,
boolean allowExperimentalMapping,
diff --git a/src/main/java/com/android/tools/r8/naming/LineReader.java b/src/main/java/com/android/tools/r8/naming/LineReader.java
new file mode 100644
index 0000000..a3f7cc3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/LineReader.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+
+/** This is an abstraction over BufferedReader */
+public interface LineReader {
+
+ String readLine() throws IOException;
+
+ void close() throws IOException;
+
+ static LineReader fromBufferedReader(BufferedReader bufferedReader) {
+ return new BufferedLineReader(bufferedReader);
+ }
+
+ class BufferedLineReader implements LineReader {
+
+ private final BufferedReader bufferedReader;
+
+ private BufferedLineReader(BufferedReader bufferedReader) {
+ this.bufferedReader = bufferedReader;
+ }
+
+ @Override
+ public String readLine() throws IOException {
+ return bufferedReader.readLine();
+ }
+
+ @Override
+ public void close() throws IOException {
+ bufferedReader.close();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index 1ba7556..2177ea3 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -53,7 +53,9 @@
ClassNameMinifier classNameMinifier =
new ClassNameMinifier(
appView,
- new MinificationClassNamingStrategy(appView),
+ appView.options().synthesizedClassPrefix.isEmpty()
+ ? new MinificationClassNamingStrategy(appView)
+ : new L8MinificationClassNamingStrategy(appView),
// Use deterministic class order to make sure renaming is deterministic.
appView.appInfo().classesWithDeterministicOrder());
ClassRenaming classRenaming = classNameMinifier.computeRenaming(timing);
@@ -122,6 +124,11 @@
String nextName(char[] packagePrefix, InternalNamingState state) {
StringBuilder nextName = new StringBuilder();
nextName.append(packagePrefix);
+ nextName.append(nextString(packagePrefix, state));
+ return nextName.toString();
+ }
+
+ String nextString(char[] packagePrefix, InternalNamingState state) {
String nextString;
do {
if (state.getDictionaryIndex() < obfuscationDictionary.size()) {
@@ -133,8 +140,36 @@
} while (obfuscationDictionaryForLookup.contains(nextString));
}
} while (RESERVED_NAMES.contains(nextString));
- nextName.append(nextString);
- return nextName.toString();
+ return nextString;
+ }
+ }
+
+ static class L8MinificationClassNamingStrategy extends MinificationClassNamingStrategy {
+
+ private final String prefix;
+
+ L8MinificationClassNamingStrategy(AppView<AppInfoWithLiveness> appView) {
+ super(appView);
+ String synthesizedClassPrefix = appView.options().synthesizedClassPrefix;
+ prefix = synthesizedClassPrefix.substring(0, synthesizedClassPrefix.length() - 1);
+ }
+
+ private boolean startsWithPrefix(char[] packagePrefix) {
+ if (packagePrefix.length < prefix.length() + 1) {
+ return false;
+ }
+ for (int i = 0; i < prefix.length(); i++) {
+ if (prefix.charAt(i) != packagePrefix[i + 1]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ String nextString(char[] packagePrefix, InternalNamingState state) {
+ String nextString = super.nextString(packagePrefix, state);
+ return startsWithPrefix(packagePrefix) ? nextString : prefix + nextString;
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
index 3e6816b..bbea4c4 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
@@ -17,7 +17,6 @@
import com.android.tools.r8.utils.StringUtils;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
-import java.io.BufferedReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
@@ -62,7 +61,7 @@
*/
public class ProguardMapReader implements AutoCloseable {
- private final BufferedReader reader;
+ private final LineReader reader;
private final JsonParser jsonParser = new JsonParser();
private final DiagnosticsHandler diagnosticsHandler;
private final boolean allowEmptyMappedRanges;
@@ -74,7 +73,7 @@
}
ProguardMapReader(
- BufferedReader reader,
+ LineReader reader,
DiagnosticsHandler diagnosticsHandler,
boolean allowEmptyMappedRanges,
boolean allowExperimentalMapping) {
diff --git a/src/main/java/com/android/tools/r8/naming/SeedMapper.java b/src/main/java/com/android/tools/r8/naming/SeedMapper.java
index 40fc12f..bf8044e 100644
--- a/src/main/java/com/android/tools/r8/naming/SeedMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/SeedMapper.java
@@ -81,7 +81,9 @@
private static SeedMapper seedMapperFromInputStream(Reporter reporter, InputStream in)
throws IOException {
- BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
+ LineReader reader =
+ LineReader.fromBufferedReader(
+ new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)));
try (ProguardMapReader proguardReader = new ProguardMapReader(reader, reporter, false, false)) {
SeedMapper.Builder builder = SeedMapper.builder(reporter);
proguardReader.parse(builder);
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index e13e860..2e373e0 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -62,7 +62,7 @@
private DexMethod validMemberRebindingTargetForNonProgramMethod(
DexClassAndMethod resolvedMethod,
- SingleResolutionResult resolutionResult,
+ SingleResolutionResult<?> resolutionResult,
ProgramMethodSet contexts,
Type invokeType,
DexMethod original) {
@@ -95,7 +95,7 @@
}
LibraryMethod eligibleLibraryMethod = null;
- SingleResolutionResult currentResolutionResult = resolutionResult;
+ SingleResolutionResult<?> currentResolutionResult = resolutionResult;
while (currentResolutionResult != null) {
DexClassAndMethod currentResolvedMethod = currentResolutionResult.getResolutionPair();
if (canRebindDirectlyToLibraryMethod(
@@ -138,7 +138,7 @@
private boolean canRebindDirectlyToLibraryMethod(
DexClassAndMethod resolvedMethod,
- SingleResolutionResult resolutionResult,
+ SingleResolutionResult<?> resolutionResult,
ProgramMethodSet contexts,
Type invokeType) {
// TODO(b/194422791): It could potentially be that `original.holder` is not a subtype of
@@ -154,7 +154,7 @@
private boolean isAccessibleInAllContexts(
DexClassAndMethod resolvedMethod,
- SingleResolutionResult resolutionResult,
+ SingleResolutionResult<?> resolutionResult,
ProgramMethodSet contexts) {
if (resolvedMethod.getHolder().isPublic() && resolvedMethod.getDefinition().isPublic()) {
return true;
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
index ebf61af..b819518 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
@@ -215,7 +215,7 @@
}
DexClass holder = appView.contextIndependentDefinitionFor(reference.getHolderType());
if (holder != null) {
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appView.appInfo().resolveMethodOn(holder, reference).asSingleResolution();
if (resolutionResult != null && resolutionResult.getResolvedHolder() != holder) {
recordNonReboundMethodAccess(
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
index 1363a68..38c08c6 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
@@ -215,7 +215,7 @@
if (holder == null) {
return;
}
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appInfo.resolveMethodOn(holder, method).asSingleResolution();
if (resolutionResult == null) {
return;
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingUtils.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingUtils.java
index 745cf99..cbb7761 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingUtils.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingUtils.java
@@ -21,7 +21,7 @@
if (clazz == null) {
return false;
}
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appView.appInfo().resolveMethodOn(clazz, method).asSingleResolution();
return resolutionResult != null
&& resolutionResult.getResolvedHolder().getType() != method.getHolderType();
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
index ba4b05a..150f7ba 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
@@ -163,7 +163,7 @@
return;
}
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appView.appInfo().unsafeResolveMethodDueToDexFormat(invokedMethod).asSingleResolution();
if (resolutionResult == null) {
// Nothing to propagate; the invoke instruction fails.
@@ -540,7 +540,7 @@
// If the bootstrap method is program declared it will be called. The call is with runtime
// provided arguments so ensure that the argument information is unknown.
DexMethodHandle bootstrapMethod = invoke.getCallSite().bootstrapMethod;
- SingleResolutionResult resolution =
+ SingleResolutionResult<?> resolution =
appView
.appInfo()
.resolveMethod(bootstrapMethod.asMethod(), bootstrapMethod.isInterface)
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
index fdf81ab..f7f7d06 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
@@ -187,7 +187,7 @@
}
private void registerInvokeMethod(DexMethod method) {
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appView.appInfo().unsafeResolveMethodDueToDexFormat(method).asSingleResolution();
if (resolutionResult == null || !resolutionResult.getResolvedHolder().isProgramClass()) {
return;
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/VirtualDispatchMethodArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/VirtualDispatchMethodArgumentPropagator.java
index 787ae13..59d8ba7 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/VirtualDispatchMethodArgumentPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/VirtualDispatchMethodArgumentPropagator.java
@@ -102,7 +102,7 @@
inactiveMethodStates.forEach(
(signature, methodState) -> {
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appView.appInfo().resolveMethodOn(clazz, signature).asSingleResolution();
// Find the first virtual method in the super class hierarchy.
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ArgumentPropagatorReprocessingCriteriaCollection.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ArgumentPropagatorReprocessingCriteriaCollection.java
index 90e9ead..87f26e7 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ArgumentPropagatorReprocessingCriteriaCollection.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ArgumentPropagatorReprocessingCriteriaCollection.java
@@ -124,7 +124,7 @@
}
// Do not reprocess the method if the invoke resolves to a library method.
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appView
.appInfo()
.unsafeResolveMethodDueToDexFormat(invoke.getInvokedMethod())
diff --git a/src/main/java/com/android/tools/r8/retrace/ProguardMapProducer.java b/src/main/java/com/android/tools/r8/retrace/ProguardMapProducer.java
index c3f94be..f2b6783 100644
--- a/src/main/java/com/android/tools/r8/retrace/ProguardMapProducer.java
+++ b/src/main/java/com/android/tools/r8/retrace/ProguardMapProducer.java
@@ -6,28 +6,53 @@
import com.android.tools.r8.Keep;
import com.google.common.primitives.Bytes;
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
-import java.io.Reader;
-import java.io.StringReader;
+import java.io.InputStream;
import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
import java.nio.file.Path;
/** Interface for producing a string format of a mapping file. */
@Keep
public interface ProguardMapProducer {
- Reader get() throws IOException;
+ InputStream get() throws IOException;
+
+ default boolean isFileBacked() {
+ return false;
+ }
+
+ default Path getPath() throws FileNotFoundException {
+ return null;
+ }
static ProguardMapProducer fromString(String proguardMapString) {
- return () -> new StringReader(proguardMapString);
+ return () -> new ByteArrayInputStream(proguardMapString.getBytes(StandardCharsets.UTF_8));
}
static ProguardMapProducer fromPath(Path path) {
- return () -> Files.newBufferedReader(path, StandardCharsets.UTF_8);
+ return new ProguardMapProducer() {
+ @Override
+ public InputStream get() throws IOException {
+ return new BufferedInputStream(new FileInputStream(path.toFile()));
+ }
+
+ @Override
+ public boolean isFileBacked() {
+ return true;
+ }
+
+ @Override
+ public Path getPath() {
+ return path;
+ }
+ };
}
static ProguardMapProducer fromBytes(byte[]... partitions) {
- return fromString(new String(Bytes.concat(partitions), StandardCharsets.UTF_8));
+ return () -> new ByteArrayInputStream(Bytes.concat(partitions));
}
}
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 01dc051..82a04e6 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -33,9 +33,9 @@
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.io.CharStreams;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
-import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -302,9 +302,10 @@
Timing timing = Timing.create("R8 retrace", command.printMemory());
RetraceOptions options = command.getOptions();
if (command.getOptions().isVerifyMappingFileHash()) {
- try (Reader reader = options.getProguardMapProducer().get()) {
+ try (InputStream reader = options.getProguardMapProducer().get()) {
VerifyMappingFileHashResult checkResult =
- ProguardMapChecker.validateProguardMapHash(CharStreams.toString(reader));
+ ProguardMapChecker.validateProguardMapHash(
+ CharStreams.toString(new InputStreamReader(reader, Charsets.UTF_8)));
if (checkResult.isError()) {
command
.getOptions()
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapReaderWithFiltering.java b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapReaderWithFiltering.java
new file mode 100644
index 0000000..279a038
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapReaderWithFiltering.java
@@ -0,0 +1,214 @@
+// Copyright (c) 2022, 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.internal;
+
+import static java.lang.Integer.MAX_VALUE;
+
+import com.android.tools.r8.naming.LineReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileChannel.MapMode;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+
+public abstract class ProguardMapReaderWithFiltering implements LineReader {
+
+ private int startIndex = 0;
+ private int endIndex = 0;
+
+ public abstract byte[] read() throws IOException;
+
+ public abstract int getStartIndex();
+
+ public abstract int getEndIndex();
+
+ public abstract boolean exceedsBuffer();
+
+ @Override
+ public String readLine() throws IOException {
+ byte[] bytes = readLineFromMultipleReads();
+ if (bytes == null) {
+ return null;
+ }
+ return new String(bytes, startIndex, endIndex - startIndex, StandardCharsets.UTF_8);
+ }
+
+ private byte[] readLineFromMultipleReads() throws IOException {
+ startIndex = 0;
+ endIndex = 0;
+ byte[] currentReadBytes = null;
+ do {
+ byte[] readBytes = read();
+ if (readBytes == null) {
+ return currentReadBytes;
+ }
+ if (exceedsBuffer() || currentReadBytes != null) {
+ // We are building up a partial result where all bytes will be present in the
+ // currentReadBytes array.
+ int thisLength = getEndIndex() - getStartIndex();
+ int currentReadBytesLength = currentReadBytes == null ? 0 : currentReadBytes.length;
+ byte[] newReadBytes = new byte[thisLength + currentReadBytesLength];
+ if (currentReadBytes != null) {
+ System.arraycopy(currentReadBytes, 0, newReadBytes, 0, currentReadBytes.length);
+ }
+ System.arraycopy(
+ readBytes, getStartIndex(), newReadBytes, currentReadBytesLength, thisLength);
+ currentReadBytes = newReadBytes;
+ endIndex = newReadBytes.length;
+ } else {
+ currentReadBytes = readBytes;
+ startIndex = getStartIndex();
+ endIndex = getEndIndex();
+ }
+ } while (exceedsBuffer());
+ return currentReadBytes;
+ }
+
+ public static class ProguardMapReaderWithFilteringMappedBuffer
+ extends ProguardMapReaderWithFiltering {
+
+ private final int PAGE_SIZE = 1024 * 8;
+
+ private final FileChannel fileChannel;
+ private MappedByteBuffer mappedByteBuffer;
+ private final long channelSize;
+ private final byte[] buffer = new byte[PAGE_SIZE];
+
+ private int currentPosition = 0;
+ private int temporaryBufferPosition = 0;
+
+ public ProguardMapReaderWithFilteringMappedBuffer(Path mappingFile) throws IOException {
+ fileChannel = FileChannel.open(mappingFile, StandardOpenOption.READ);
+ channelSize = fileChannel.size();
+ readFromChannel();
+ }
+
+ private void readFromChannel() throws IOException {
+ mappedByteBuffer =
+ fileChannel.map(
+ MapMode.READ_ONLY,
+ currentPosition,
+ Math.min(channelSize - currentPosition, MAX_VALUE));
+ }
+
+ @Override
+ public byte[] read() throws IOException {
+ if (currentPosition >= channelSize) {
+ return null;
+ }
+ temporaryBufferPosition = 0;
+ while (currentPosition < channelSize) {
+ if (!mappedByteBuffer.hasRemaining()) {
+ readFromChannel();
+ }
+ byte readByte = readByte();
+ if (readByte == '\n') {
+ break;
+ }
+ buffer[temporaryBufferPosition++] = readByte;
+ if (temporaryBufferPosition == PAGE_SIZE) {
+ break;
+ }
+ }
+ return buffer;
+ }
+
+ @Override
+ public int getStartIndex() {
+ return 0;
+ }
+
+ @Override
+ public int getEndIndex() {
+ if (temporaryBufferPosition > 0 && buffer[temporaryBufferPosition - 1] == '\r') {
+ return temporaryBufferPosition - 1;
+ }
+ return temporaryBufferPosition;
+ }
+
+ @Override
+ public boolean exceedsBuffer() {
+ return temporaryBufferPosition == PAGE_SIZE;
+ }
+
+ private byte readByte() {
+ currentPosition += 1;
+ return mappedByteBuffer.get();
+ }
+
+ @Override
+ public void close() throws IOException {
+ fileChannel.close();
+ }
+ }
+
+ public static class ProguardMapReaderWithFilteringInputBuffer
+ extends ProguardMapReaderWithFiltering {
+
+ private final InputStream inputStream;
+
+ private final int BUFFER_SIZE = 1024 * 8;
+ private final byte[] buffer = new byte[BUFFER_SIZE];
+
+ private int bufferIndex = BUFFER_SIZE;
+ private int startIndex = 0;
+ private int endIndex = 0;
+ private int endReadIndex = 0;
+
+ public ProguardMapReaderWithFilteringInputBuffer(InputStream inputStream) {
+ this.inputStream = inputStream;
+ }
+
+ @Override
+ public void close() throws IOException {
+ inputStream.close();
+ }
+
+ @Override
+ public byte[] read() throws IOException {
+ if (bufferIndex >= endReadIndex) {
+ endReadIndex = inputStream.read(buffer);
+ if (endReadIndex == -1) {
+ return null;
+ }
+ bufferIndex = 0;
+ }
+ startIndex = bufferIndex;
+ boolean foundLineBreak = false;
+ for (endIndex = startIndex; endIndex < endReadIndex; endIndex++) {
+ if (buffer[endIndex] == '\n') {
+ foundLineBreak = true;
+ break;
+ }
+ }
+ bufferIndex = endIndex;
+ if (foundLineBreak) {
+ bufferIndex += 1;
+ }
+ return buffer;
+ }
+
+ @Override
+ public int getStartIndex() {
+ return startIndex;
+ }
+
+ @Override
+ public int getEndIndex() {
+ if (endIndex > 0 && buffer[endIndex - 1] == '\r') {
+ return endIndex - 1;
+ }
+ return endIndex;
+ }
+
+ @Override
+ public boolean exceedsBuffer() {
+ return endReadIndex == endIndex;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingProviderBuilderImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingProviderBuilderImpl.java
index e18adca..cb6991e 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingProviderBuilderImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingProviderBuilderImpl.java
@@ -6,11 +6,13 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.LineReader;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.retrace.InvalidMappingFileException;
import com.android.tools.r8.retrace.ProguardMapProducer;
import com.android.tools.r8.retrace.ProguardMappingProvider;
-import java.io.BufferedReader;
+import com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.ProguardMapReaderWithFilteringInputBuffer;
+import com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.ProguardMapReaderWithFilteringMappedBuffer;
import java.util.HashSet;
import java.util.Set;
@@ -62,23 +64,17 @@
@Override
public ProguardMappingProvider build() {
try {
- if (allowLookupAllClasses) {
- return new ProguardMappingProviderImpl(
- ClassNameMapper.mapperFromBufferedReader(
- new BufferedReader(proguardMapProducer.get()),
- diagnosticsHandler,
- true,
- allowExperimental));
- } else {
- return new ProguardMappingProviderImpl(
- ClassNameMapper.mapperFromBufferedReaderWithFiltering(
- new BufferedReader(proguardMapProducer.get()),
- diagnosticsHandler,
- true,
- allowExperimental,
- allowedLookup),
- allowedLookup);
- }
+ LineReader reader =
+ proguardMapProducer.isFileBacked()
+ ? new ProguardMapReaderWithFilteringMappedBuffer(proguardMapProducer.getPath())
+ : new ProguardMapReaderWithFilteringInputBuffer(proguardMapProducer.get());
+ return new ProguardMappingProviderImpl(
+ ClassNameMapper.mapperFromLineReaderWithFiltering(
+ reader,
+ diagnosticsHandler,
+ true,
+ allowExperimental,
+ allowLookupAllClasses ? null : allowedLookup));
} catch (Exception e) {
throw new InvalidMappingFileException(e);
}
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 eac02d2..34425b3 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -1387,7 +1387,7 @@
singleTargetLookupCache.getCachedItem(refinedReceiverType, method);
return cachedItem;
}
- SingleResolutionResult resolution =
+ SingleResolutionResult<?> resolution =
resolveMethodOn(initialResolutionHolder, method).asSingleResolution();
if (resolution == null
|| resolution.isAccessibleForVirtualDispatchFrom(context.getHolder(), this).isFalse()) {
@@ -1460,7 +1460,7 @@
private DexEncodedMethod getMethodTargetFromExactRuntimeInformation(
DexType refinedReceiverType,
ClassTypeElement receiverLowerBoundType,
- SingleResolutionResult resolution,
+ SingleResolutionResult<?> resolution,
DexClass refinedReceiverClass) {
// If the lower-bound on the receiver type is the same as the upper-bound, then we have exact
// runtime type information. In this case, the invoke will dispatch to the resolution result
@@ -1555,6 +1555,7 @@
public SubtypingInfo computeSubtypingInfo() {
return SubtypingInfo.create(this);
}
+
/** Predicate on types that *must* never be merged horizontally. */
public boolean isNoHorizontalClassMergingOfType(DexType type) {
return noClassMerging.contains(type) || noHorizontalClassMerging.contains(type);
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 7d54b09..98562f9 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -27,6 +27,7 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ClassDefinition;
+import com.android.tools.r8.graph.ClassResolutionResult;
import com.android.tools.r8.graph.ClasspathOrLibraryClass;
import com.android.tools.r8.graph.ClasspathOrLibraryDefinition;
import com.android.tools.r8.graph.Definition;
@@ -160,6 +161,7 @@
import com.google.common.collect.Sets.SetView;
import it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap;
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
+import java.lang.reflect.InvocationHandler;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
@@ -667,26 +669,39 @@
return definitionFor(type, context, this::recordNonProgramClass, this::reportMissingClass);
}
+ public boolean hasAlternativeLibraryDefinition(DexProgramClass programClass) {
+ ClassResolutionResult classResolutionResult =
+ internalDefinitionFor(
+ programClass.type, programClass, this::recordNonProgramClass, this::reportMissingClass);
+ assert classResolutionResult.hasClassResolutionResult();
+ DexClass alternativeClass = classResolutionResult.toAlternativeClassWithProgramOverLibrary();
+ assert alternativeClass == null || alternativeClass.isLibraryClass();
+ return alternativeClass != null;
+ }
+
private DexClass definitionFor(
DexType type,
ProgramDerivedContext context,
BiConsumer<DexClass, ProgramDerivedContext> foundClassConsumer,
BiConsumer<DexType, ProgramDerivedContext> missingClassConsumer) {
- return internalDefinitionFor(type, context, foundClassConsumer, missingClassConsumer);
+ return internalDefinitionFor(type, context, foundClassConsumer, missingClassConsumer)
+ .toSingleClassWithProgramOverLibrary();
}
- private DexClass internalDefinitionFor(
+ private ClassResolutionResult internalDefinitionFor(
DexType type,
ProgramDerivedContext context,
BiConsumer<DexClass, ProgramDerivedContext> foundClassConsumer,
BiConsumer<DexType, ProgramDerivedContext> missingClassConsumer) {
- DexClass clazz = appInfo().definitionFor(type);
- if (clazz == null) {
+ ClassResolutionResult classResolutionResult =
+ appInfo().contextIndependentDefinitionForWithResolutionResult(type);
+ if (classResolutionResult.hasClassResolutionResult()) {
+ classResolutionResult.forEachClassResolutionResult(
+ clazz -> foundClassConsumer.accept(clazz, context));
+ } else {
missingClassConsumer.accept(type, context);
- return null;
}
- foundClassConsumer.accept(clazz, context);
- return clazz;
+ return classResolutionResult;
}
public FieldAccessInfoCollectionImpl getFieldAccessInfoCollection() {
@@ -783,12 +798,18 @@
if (!type.isClassType()) {
return;
}
- DexClass clazz = appView.definitionFor(type);
- if (clazz == null) {
+ ClassResolutionResult classResolutionResult =
+ appView.contextIndependentDefinitionForWithResolutionResult(type);
+ if (!classResolutionResult.hasClassResolutionResult()) {
missingClassConsumer.accept(type, context);
- } else if (!clazz.isProgramClass()) {
- classAdder.accept(clazz.asClasspathOrLibraryClass());
+ return;
}
+ classResolutionResult.forEachClassResolutionResult(
+ clazz -> {
+ if (!clazz.isProgramClass()) {
+ classAdder.accept(clazz.asClasspathOrLibraryClass());
+ }
+ });
}
private DexProgramClass getProgramClassOrNull(DexType type, ProgramDefinition context) {
@@ -1119,7 +1140,7 @@
}
private void disableClosedWorldReasoning(DexMethod reference, ProgramMethod context) {
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
resolveMethod(reference, context, KeepReason.methodHandleReferencedIn(context));
if (resolutionResult != null && resolutionResult.getResolvedHolder().isProgramClass()) {
applyMinimumKeepInfoWhenLiveOrTargeted(
@@ -2086,6 +2107,9 @@
// Update keep info.
applyMinimumKeepInfo(clazz);
applyMinimumKeepInfoDependentOn(new LiveClassEnqueuerEvent(clazz));
+ if (hasAlternativeLibraryDefinition(clazz)) {
+ getKeepInfo().keepClass(clazz);
+ }
processAnnotations(clazz);
@@ -2272,7 +2296,7 @@
return fieldResolutionResult;
}
- private SingleResolutionResult resolveMethod(
+ private SingleResolutionResult<?> resolveMethod(
DexMethod method, ProgramDefinition context, KeepReason reason) {
// Record the references in case they are not program types.
MethodResolutionResult resolutionResult = appInfo.unsafeResolveMethodDueToDexFormat(method);
@@ -2286,7 +2310,7 @@
return resolutionResult.asSingleResolution();
}
- private SingleResolutionResult resolveMethod(
+ private SingleResolutionResult<?> resolveMethod(
DexMethod method, ProgramDefinition context, KeepReason reason, boolean interfaceInvoke) {
// Record the references in case they are not program types.
MethodResolutionResult resolutionResult = appInfo.resolveMethod(method, interfaceInvoke);
@@ -2304,7 +2328,7 @@
private void handleInvokeOfStaticTarget(
DexMethod reference, ProgramDefinition context, KeepReason reason) {
- SingleResolutionResult resolution = resolveMethod(reference, context, reason);
+ SingleResolutionResult<?> resolution = resolveMethod(reference, context, reason);
if (resolution == null || resolution.getResolvedHolder().isNotProgramClass()) {
return;
}
@@ -2472,6 +2496,16 @@
if (clazz == null) {
return;
}
+ DexClass alternativeResolutionResult =
+ appInfo()
+ .contextIndependentDefinitionForWithResolutionResult(type)
+ .toAlternativeClassWithProgramOverLibrary();
+ if (alternativeResolutionResult != null && alternativeResolutionResult.isLibraryClass()) {
+ // We are in a situation where a library class inherits from a library class, which has a
+ // program class duplicated version for low API levels.
+ recordNonProgramClass(alternativeResolutionResult, clazz);
+ return;
+ }
if (forceProguardCompatibility) {
// To ensure that the program works correctly we have to pin all super types and members
// in the tree.
@@ -2758,7 +2792,7 @@
getReachableVirtualTargets(currentClass)
.forEach(
(resolutionSearchKey, contexts) -> {
- SingleResolutionResult singleResolution =
+ SingleResolutionResult<?> singleResolution =
appInfo
.resolveMethod(resolutionSearchKey.method, resolutionSearchKey.isInterface)
.asSingleResolution();
@@ -3000,6 +3034,9 @@
// Update keep info.
applyMinimumKeepInfo(field);
+ if (hasAlternativeLibraryDefinition(field.getHolder()) && !field.getDefinition().isPrivate()) {
+ getKeepInfo().keepField(field);
+ }
// Notify analyses.
analyses.forEach(analysis -> analysis.processNewlyLiveField(field, context, workList));
@@ -3203,7 +3240,7 @@
return;
}
- SingleResolutionResult resolution = resolveMethod(method, context, reason, interfaceInvoke);
+ SingleResolutionResult<?> resolution = resolveMethod(method, context, reason, interfaceInvoke);
if (resolution == null) {
return;
}
@@ -3349,7 +3386,7 @@
// Package protected due to entry point from worklist.
void markSuperMethodAsReachable(DexMethod reference, ProgramMethod from) {
KeepReason reason = KeepReason.targetedBySuperFrom(from);
- SingleResolutionResult resolution = resolveMethod(reference, from, reason);
+ SingleResolutionResult<?> resolution = resolveMethod(reference, from, reason);
if (resolution == null) {
return;
}
@@ -4561,6 +4598,10 @@
// Update keep info.
applyMinimumKeepInfo(method);
+ if (hasAlternativeLibraryDefinition(method.getHolder())
+ && !method.getDefinition().isPrivateMethod()) {
+ getKeepInfo().keepMethod(method);
+ }
}
private void traceNonDesugaredCode(ProgramMethod method) {
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 484cd3a..3b96973 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -598,7 +598,7 @@
}
private void tryAndKeepMethodOnClass(DexClassAndMethod method, ProguardMemberRule rule) {
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appView
.appInfo()
.resolveMethodOn(originalClazz, method.getReference())
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 309e92a5..8942e41 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -32,7 +32,7 @@
import com.android.tools.r8.errors.MissingNestHostNestDesugarDiagnostic;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
-import com.android.tools.r8.experimental.startup.StartupConfiguration;
+import com.android.tools.r8.experimental.startup.StartupOptions;
import com.android.tools.r8.features.FeatureSplitConfiguration;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
@@ -168,7 +168,6 @@
public DataResourceConsumer dataResourceConsumer;
public FeatureSplitConfiguration featureSplitConfiguration;
- public StartupConfiguration startupConfiguration;
public List<Consumer<InspectorImpl>> outputInspections = Collections.emptyList();
@@ -786,6 +785,7 @@
new KotlinOptimizationOptions();
private final ApiModelTestingOptions apiModelTestingOptions = new ApiModelTestingOptions();
private final DesugarSpecificOptions desugarSpecificOptions = new DesugarSpecificOptions();
+ private final StartupOptions startupOptions = new StartupOptions();
public final TestingOptions testing = new TestingOptions();
public List<ProguardConfigurationRule> mainDexKeepRules = ImmutableList.of();
@@ -837,6 +837,10 @@
return openClosedInterfacesOptions;
}
+ public StartupOptions getStartupOptions() {
+ return startupOptions;
+ }
+
private static Set<String> getExtensiveLoggingFilter() {
String property = System.getProperty("com.android.tools.r8.extensiveLoggingFilter");
if (property != null) {
@@ -862,13 +866,24 @@
return ImmutableSet.of();
}
- private static boolean isSystemPropertyForDevelopmentSet(String propertyName) {
+ public static boolean isSystemPropertyForDevelopmentSet(String propertyName) {
if (Version.isDevelopmentVersion()) {
return System.getProperty(propertyName) != null;
}
return false;
}
+ public static String getSystemPropertyForDevelopmentOrDefault(
+ String propertyName, String defaultValue) {
+ if (Version.isDevelopmentVersion()) {
+ String propertyValue = System.getProperty(propertyName);
+ if (propertyValue != null) {
+ return propertyValue;
+ }
+ }
+ return defaultValue;
+ }
+
private static int parseSystemPropertyForDevelopmentOrDefault(
String propertyName, int defaultValue) {
if (Version.isDevelopmentVersion()) {
@@ -969,9 +984,9 @@
MachineDesugaredLibrarySpecification.empty();
public TypeRewriter getTypeRewriter() {
- return machineDesugaredLibrarySpecification.getRewriteType().isEmpty()
- ? TypeRewriter.empty()
- : new MachineDesugarPrefixRewritingMapper(machineDesugaredLibrarySpecification);
+ return machineDesugaredLibrarySpecification.requiresTypeRewriting()
+ ? new MachineDesugarPrefixRewritingMapper(machineDesugaredLibrarySpecification)
+ : TypeRewriter.empty();
}
public boolean relocatorCompilation = false;
diff --git a/src/main/java/com/android/tools/r8/utils/ZipUtils.java b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
index e0863ce..9d8cedd 100644
--- a/src/main/java/com/android/tools/r8/utils/ZipUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
@@ -239,6 +239,10 @@
return new ZipBuilder(zipFile);
}
+ public ZipOutputStream getOutputStream() {
+ return stream;
+ }
+
public ZipBuilder addFilesRelative(Path basePath, Collection<Path> filesToAdd)
throws IOException {
for (Path path : filesToAdd) {
@@ -269,6 +273,14 @@
return this;
}
+ public ZipBuilder addText(String path, String text) throws IOException {
+ ZipEntry zipEntry = new ZipEntry(path);
+ stream.putNextEntry(zipEntry);
+ stream.write(text.getBytes(StandardCharsets.UTF_8));
+ stream.closeEntry();
+ return this;
+ }
+
public Path build() throws IOException {
stream.close();
return zipFile;
diff --git a/src/main/java/com/android/tools/r8/utils/classhierarchy/MethodOverridesCollector.java b/src/main/java/com/android/tools/r8/utils/classhierarchy/MethodOverridesCollector.java
index 7407b59..7dc7b3e 100644
--- a/src/main/java/com/android/tools/r8/utils/classhierarchy/MethodOverridesCollector.java
+++ b/src/main/java/com/android/tools/r8/utils/classhierarchy/MethodOverridesCollector.java
@@ -108,7 +108,7 @@
}
for (DexMethodSignature interfaceMethod : interfaceMethodsOfInterestForClass) {
- SingleResolutionResult resolutionResult =
+ SingleResolutionResult<?> resolutionResult =
appView
.appInfo()
.resolveMethodOnClass(implementer, interfaceMethod)
diff --git a/src/test/examples/mockito_interface/InterfaceTest.java b/src/test/examples/mockito_interface/InterfaceTest.java
index 2502b13..f6ea73c 100644
--- a/src/test/examples/mockito_interface/InterfaceTest.java
+++ b/src/test/examples/mockito_interface/InterfaceTest.java
@@ -6,15 +6,11 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(Parameterized.class)
public class InterfaceTest {
+
@Mock
private Interface fld;
@@ -22,22 +18,23 @@
private boolean flag;
- @Parameterized.Parameters(name = "flag: {0}")
- public static Boolean[] data() {
- return new Boolean[] {true, false};
+ public static void main(String[] args) {
+ for (boolean flag : new boolean[] {true, false}) {
+ InterfaceTest test = new InterfaceTest(flag);
+ test.setUp();
+ test.test();
+ }
}
public InterfaceTest(boolean flag) {
this.flag = flag;
}
- @Before
public void setUp() {
MockitoAnnotations.initMocks(this);
user = new InterfaceUser(fld);
}
- @Test
public void test() {
if (flag) {
user.consume();
diff --git a/src/test/examples/mockito_interface/keep-rules-conditional-on-mock.txt b/src/test/examples/mockito_interface/keep-rules-conditional-on-mock.txt
index edb4c08..e82228a 100644
--- a/src/test/examples/mockito_interface/keep-rules-conditional-on-mock.txt
+++ b/src/test/examples/mockito_interface/keep-rules-conditional-on-mock.txt
@@ -8,7 +8,7 @@
public static void main(...);
}
--keep @**.RunWith class * { *; }
+-keep class **.InterfaceTest { *; }
# Mockito generates mocks of interface types at runtime. If interface methods are optimized, i.e.,
# stripped out, mock-based tests will fail. So, keep all methods of interfaces if they are used as
diff --git a/src/test/examples/mockito_interface/keep-rules.txt b/src/test/examples/mockito_interface/keep-rules.txt
index b398a9b..f821af4 100644
--- a/src/test/examples/mockito_interface/keep-rules.txt
+++ b/src/test/examples/mockito_interface/keep-rules.txt
@@ -8,4 +8,4 @@
public static void main(...);
}
--keep @**.RunWith class * { *; }
\ No newline at end of file
+-keep class **.InterfaceTest { *; }
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/OriginMatcher.java b/src/test/java/com/android/tools/r8/OriginMatcher.java
index 8484ea3..4f5ad82 100644
--- a/src/test/java/com/android/tools/r8/OriginMatcher.java
+++ b/src/test/java/com/android/tools/r8/OriginMatcher.java
@@ -30,4 +30,18 @@
}
};
}
+
+ public static Matcher<Origin> hasPart(String part) {
+ return new OriginMatcher() {
+ @Override
+ protected boolean matchesSafely(Origin origin) {
+ return origin.part().equals(part);
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("part is not " + part);
+ }
+ };
+ }
}
diff --git a/src/test/java/com/android/tools/r8/R8TestCompileResult.java b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
index b96cef4..fe500f8 100644
--- a/src/test/java/com/android/tools/r8/R8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
@@ -111,6 +111,19 @@
return self();
}
+ @SafeVarargs
+ public final <E extends Throwable> R8TestCompileResult inspectMultiDex(
+ ThrowingConsumer<CodeInspector, E>... consumers) throws IOException, E {
+ Path out = state.getNewTempFolder();
+ getApp().writeToDirectory(out, OutputMode.DexIndexed);
+ consumers[0].accept(new CodeInspector(out.resolve("classes.dex"), getProguardMap()));
+ for (int i = 1; i < consumers.length; i++) {
+ consumers[i].accept(
+ new CodeInspector(out.resolve("classes" + (i + 1) + ".dex"), getProguardMap()));
+ }
+ return self();
+ }
+
public final <E extends Throwable> R8TestCompileResult inspectGraph(
ThrowingConsumer<GraphInspector, E> consumer) throws IOException, E {
consumer.accept(graphInspector());
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 3a482a5..21ca89e 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -690,8 +690,8 @@
protected Path jarTestClasses(Iterable<Class<?>> classes, List<DataResource> dataResources)
throws IOException {
Path jar = File.createTempFile("junit", ".jar", temp.getRoot()).toPath();
- try (JarOutputStream out = new JarOutputStream(new FileOutputStream(jar.toFile()))) {
- addTestClassesToJar(out, classes);
+ try (ZipOutputStream out = new ZipOutputStream(new FileOutputStream(jar.toFile()))) {
+ addTestClassesToZip(out, classes);
if (dataResources != null) {
addDataResourcesToJar(out, dataResources);
}
@@ -700,7 +700,7 @@
}
/** Create a temporary JAR file containing the specified test classes. */
- protected void addTestClassesToJar(JarOutputStream out, Iterable<Class<?>> classes)
+ protected void addTestClassesToZip(ZipOutputStream out, Iterable<Class<?>> classes)
throws IOException {
for (Class<?> clazz : classes) {
try (FileInputStream in =
@@ -714,7 +714,7 @@
/** Create a temporary JAR file containing the specified data resources. */
protected void addDataResourcesToJar(
- JarOutputStream out, List<? extends DataResource> dataResources) throws IOException {
+ ZipOutputStream out, List<? extends DataResource> dataResources) throws IOException {
try {
for (DataResource dataResource : dataResources) {
String name = dataResource.getName();
diff --git a/src/test/java/com/android/tools/r8/TestBuilder.java b/src/test/java/com/android/tools/r8/TestBuilder.java
index ae8a263..c4d8683 100644
--- a/src/test/java/com/android/tools/r8/TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestBuilder.java
@@ -16,6 +16,7 @@
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.function.BiFunction;
+import java.util.function.Function;
public abstract class TestBuilder<RR extends TestRunResult<RR>, T extends TestBuilder<RR, T>> {
@@ -154,6 +155,14 @@
return addLibraryFiles(Arrays.asList(files));
}
+ public T addLibraryClassFileData(byte[]... classes) {
+ return addLibraryClassFileData(Arrays.asList(classes));
+ }
+
+ public T addLibraryClassFileData(Collection<byte[]> classes) {
+ return addByteCollectionToJar("library.jar", classes, this::addLibraryFiles);
+ }
+
public T addDefaultRuntimeLibrary(TestParameters parameters) {
if (parameters.getBackend() == Backend.DEX) {
addLibraryFiles(ToolHelper.getFirstSupportedAndroidJar(parameters.getApiLevel()));
@@ -181,9 +190,14 @@
}
public T addClasspathClassFileData(Collection<byte[]> classes) {
+ return addByteCollectionToJar("cp.jar", classes, this::addClasspathFiles);
+ }
+
+ private T addByteCollectionToJar(
+ String name, Collection<byte[]> classes, Function<Path, T> outputConsumer) {
Path out;
try {
- out = getState().getNewTempFolder().resolve("cp.jar");
+ out = getState().getNewTempFolder().resolve(name);
} catch (IOException e) {
throw new RuntimeException(e);
}
@@ -192,7 +206,7 @@
consumer.accept(ByteDataView.of(bytes), TestBase.extractClassDescriptor(bytes), null);
}
consumer.finished(null);
- return addClasspathFiles(out);
+ return outputConsumer.apply(out);
}
public final T addTestingAnnotationsAsProgramClasses() {
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index a476d13..719dbd8 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -36,6 +36,7 @@
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ObjectArrays;
import java.io.File;
import java.io.IOException;
@@ -286,7 +287,8 @@
try {
AndroidApp.Builder appBuilder = AndroidApp.builder();
for (byte[] clazz : classes) {
- appBuilder.addClassProgramData(clazz, Origin.unknown());
+ appBuilder.addClassProgramData(
+ clazz, Origin.unknown(), ImmutableSet.of(TestBase.extractClassDescriptor(clazz)));
}
Path path = state.getNewTempFolder().resolve("runtime-classes.jar");
appBuilder.build().writeToZip(path, OutputMode.ClassFile);
diff --git a/src/test/java/com/android/tools/r8/TestParametersBuilder.java b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
index cb24356..48e94f0 100644
--- a/src/test/java/com/android/tools/r8/TestParametersBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
@@ -232,6 +232,10 @@
return withApiLevelsEndingAtExcluding(AndroidApiLevel.L);
}
+ public TestParametersBuilder apiLevelWithDefaultMethodsSupport() {
+ return withApiLevelsStartingAtIncluding(TestBase.apiLevelWithDefaultInterfaceMethodsSupport());
+ }
+
public TestParametersBuilder withCustomRuntime(TestRuntime runtime) {
assert getUnfilteredAvailableRuntimes().noneMatch(r -> r == runtime);
customRuntimes.add(runtime);
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 5b8bb7a..7bcdfb6 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -196,6 +196,10 @@
return System.getProperty("desugar_jdk_json_dir", "src/library_desugar");
}
+ public static Path getDesugarLibJsonMinimalForTesting() {
+ return Paths.get(getDesugarLibraryJsonDir(), "desugar_jdk_libs_minimal.json");
+ }
+
public static Path getDesugarLibJsonForTesting() {
return Paths.get(getDesugarLibraryJsonDir(), "desugar_jdk_libs.json");
}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkEnvironment.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkEnvironment.java
index f558932..fe36c52 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkEnvironment.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkEnvironment.java
@@ -29,7 +29,11 @@
public Path translateDependencyPath(String directoryName, Path location) {
return isGolem
- ? Paths.get("benchmarks", config.getDependencyDirectoryName(), directoryName)
+ ? getGolemDependencyRoot().resolve(directoryName)
: location.resolve(directoryName);
}
+
+ public Path getGolemDependencyRoot() {
+ return Paths.get("benchmarks", config.getDependencyDirectoryName());
+ }
}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkMainEntryRunner.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkMainEntryRunner.java
index 3151343..d5f84a8 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkMainEntryRunner.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkMainEntryRunner.java
@@ -3,6 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.benchmarks;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import org.junit.rules.TemporaryFolder;
public class BenchmarkMainEntryRunner {
@@ -19,18 +25,40 @@
throw new RuntimeException("Invalid identifier identifier: " + benchmarkName);
}
boolean isGolem = isGolemArg.equals("golem");
- if (!isGolem && !isGolemArg.equals("local")) {
- throw new RuntimeException(
- "Invalid value for arg 3, expected 'golem' or 'local', got '" + isGolemArg + "'");
- }
BenchmarkCollection collection = BenchmarkCollection.computeCollection();
BenchmarkConfig config = collection.getBenchmark(identifier);
if (config == null) {
throw new RuntimeException("Unknown identifier: " + identifier);
}
+
TemporaryFolder temp = new TemporaryFolder();
temp.create();
- config.run(new BenchmarkEnvironment(config, temp, isGolem));
- temp.delete();
+ try {
+ // When running locally we still setup a "golem" environment and manually unpack dependencies.
+ BenchmarkEnvironment environment = new BenchmarkEnvironment(config, temp, true /* isGolem */);
+ if (!isGolem) {
+ // When not running with golem, the python wrapper will run the benchmark in a temp
+ // directory.
+ // In this case the argument is the absolute path to the R8 repo.
+ Path repoRoot = Paths.get(isGolemArg);
+ Path dependencyDirectory = Files.createDirectories(environment.getGolemDependencyRoot());
+ for (BenchmarkDependency dependency : config.getDependencies()) {
+ untar(repoRoot.resolve(dependency.getTarball()), dependencyDirectory);
+ }
+ }
+ System.out.println("Running benchmark");
+ config.run(environment);
+ } finally {
+ temp.delete();
+ }
+ }
+
+ private static void untar(Path tarball, Path target) throws IOException {
+ ProcessBuilder builder =
+ new ProcessBuilder("tar", "zxf", tarball.toString(), "-C", target.toString());
+ ProcessResult result = ToolHelper.runProcess(builder);
+ if (result.exitCode != 0) {
+ throw new IOException(result.toString());
+ }
}
}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java b/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java
index f8b16df..ecfb059 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java
@@ -224,6 +224,7 @@
TestBase.testForD8(environment.getTemp(), Backend.DEX)
.addProgramFiles(programOutputs)
+ .addLibraryFiles(dump.getLibraryArchive())
.setMinApi(dumpProperties.getMinApi())
.benchmarkCompile(results.getSubResults(builder.nameForMergePart()));
});
diff --git a/src/test/java/com/android/tools/r8/cf/stackmap/ImpreciseFrameAtCatchHandlerTest.java b/src/test/java/com/android/tools/r8/cf/stackmap/ImpreciseFrameAtCatchHandlerTest.java
new file mode 100644
index 0000000..f734f79
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/stackmap/ImpreciseFrameAtCatchHandlerTest.java
@@ -0,0 +1,85 @@
+// Copyright (c) 2022, 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.cf.stackmap;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.transformers.MethodTransformer;
+import com.android.tools.r8.utils.IntBox;
+import java.io.IOException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ImpreciseFrameAtCatchHandlerTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClassFileData(getTransformedMain())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Caught");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Caught");
+ }
+
+ private static byte[] getTransformedMain() throws IOException {
+ IntBox witness = new IntBox();
+ byte[] transformed =
+ transformer(Main.class)
+ .addMethodTransformer(
+ new MethodTransformer() {
+
+ @Override
+ public void visitFrame(
+ int type, int numLocal, Object[] local, int numStack, Object[] stack) {
+ if (getContext().method.getMethodName().equals("main")
+ && numLocal == 0
+ && numStack == 1
+ && stack[0].equals(binaryName(Exception.class))) {
+ stack = new Object[] {binaryName(Object.class)};
+ witness.increment();
+ }
+ super.visitFrame(type, numLocal, local, numStack, stack);
+ }
+ })
+ .transform();
+ assertEquals(1, witness.get());
+ return transformed;
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ try {
+ System.out.println(args[0]);
+ } catch (Exception e) {
+ System.out.println("Caught");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/stackmap/SharedExceptionTypeTest.java b/src/test/java/com/android/tools/r8/cf/stackmap/SharedExceptionTypeTest.java
new file mode 100644
index 0000000..1f30f40
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/stackmap/SharedExceptionTypeTest.java
@@ -0,0 +1,84 @@
+// Copyright (c) 2022, 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.cf.stackmap;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class SharedExceptionTypeTest extends TestBase {
+
+ private final TestParameters parameters;
+ private final String EXPECTED = "Hello World!";
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().build();
+ }
+
+ public SharedExceptionTypeTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testForJvm()
+ .addInnerClasses(SharedExceptionTypeTest.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8(parameters.getBackend())
+ .addInnerClasses(SharedExceptionTypeTest.class)
+ .setMinApi(AndroidApiLevel.B)
+ .addOptionsModification(options -> options.testing.readInputStackMaps = true)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ public static class ExceptionSuper extends RuntimeException {
+
+ public void printError() {
+ System.out.println("Hello World!");
+ }
+ }
+
+ public static class ExceptionA extends ExceptionSuper {}
+
+ public static class ExceptionB extends ExceptionSuper {}
+
+ public static class Main {
+
+ public static void foo(String[] args) {
+ if (args.length == 0) {
+ throw new ExceptionA();
+ } else if (args.length == 1) {
+ throw new ExceptionB();
+ } else {
+ throw new RuntimeException("FOO BAR");
+ }
+ }
+
+ public static void main(String[] args) {
+ try {
+ foo(args);
+ } catch (ExceptionA | ExceptionB e) {
+ e.printError();
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingInD8WithClInitOnCCTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingInD8WithClInitOnCCTest.java
new file mode 100644
index 0000000..4e955d0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingInD8WithClInitOnCCTest.java
@@ -0,0 +1,74 @@
+// Copyright (c) 2022, 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.CompilationMode;
+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.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+/** This is a regression test for b/227791663 */
+@RunWith(Parameterized.class)
+public class HorizontalClassMergingInD8WithClInitOnCCTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ testForD8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMode(CompilationMode.RELEASE)
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(
+ options -> {
+ options.horizontalClassMergerOptions().enable();
+ options.horizontalClassMergerOptions().setRestrictToSynthetics();
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("0", "1");
+ }
+
+ public interface A {
+
+ int value = Main.getAndIncrement();
+
+ static int getValue() {
+ return value;
+ }
+ }
+
+ public interface B {
+
+ int value = Main.getAndIncrement();
+
+ static int getValue() {
+ return value;
+ }
+ }
+
+ public static class Main {
+
+ private static int value = 0;
+
+ public static int getAndIncrement() {
+ return value++;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(A.getValue());
+ System.out.println(B.getValue());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java
index d2e91ec..ae8d0b6 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java
@@ -44,12 +44,14 @@
.addOptionsModification(
options -> {
DexItemFactory dexItemFactory = options.dexItemFactory();
- options.startupConfiguration =
- new StartupConfiguration(
- startupClasses.stream()
- .map(clazz -> toDexType(clazz, dexItemFactory))
- .collect(Collectors.toList()),
- Collections.emptyList());
+ options
+ .getStartupOptions()
+ .setStartupConfiguration(
+ new StartupConfiguration(
+ startupClasses.stream()
+ .map(clazz -> toDexType(clazz, dexItemFactory))
+ .collect(Collectors.toList()),
+ Collections.emptyList()));
})
.addHorizontallyMergedClassesInspector(
inspector ->
diff --git a/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
index a292274..265bb19 100644
--- a/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
+++ b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
@@ -55,9 +55,6 @@
.addAll(
findAllJarsIn(Paths.get(ToolHelper.EXAMPLES_ANDROID_N_BUILD_DIR)),
ContinuousSteppingTest::fromAndroidN)
- // TODO(b/79911828) Investigate timeout issues for Android O examples.
- // .addAll(findAllJarsIn(Paths.get(ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR)),
- // ContinuousSteppingTest::fromAndroidO)
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
index 3b41b98..6ce91e1 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
@@ -30,6 +30,7 @@
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import java.nio.file.Path;
import java.util.ArrayList;
+import org.hamcrest.CoreMatchers;
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -130,7 +131,13 @@
}
private void assertCorrect(CodeInspector inspector) {
- inspector.allClasses().forEach(clazz -> assertThat(clazz.getOriginalName(), startsWith("j$.")));
+ inspector
+ .allClasses()
+ .forEach(
+ clazz ->
+ assertThat(
+ clazz.getOriginalName(),
+ CoreMatchers.anyOf(startsWith("j$."), startsWith("java."))));
assertThat(inspector.clazz("j$.time.Clock"), isPresent());
// Above N the following classes are removed instead of being desugared.
if (parameters.getApiLevel().getLevel() >= AndroidApiLevel.N.getLevel()) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/FunctionOnlyTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/FunctionOnlyTest.java
new file mode 100644
index 0000000..0b443a8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/FunctionOnlyTest.java
@@ -0,0 +1,277 @@
+// Copyright (c) 2022, 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 static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.L8Command;
+import com.android.tools.r8.LibraryDesugaringTestConfiguration;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.function.BiFunction;
+import java.util.function.BinaryOperator;
+import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
+import java.util.function.DoublePredicate;
+import java.util.function.DoubleSupplier;
+import java.util.function.Function;
+import java.util.function.IntSupplier;
+import java.util.function.LongConsumer;
+import java.util.function.LongSupplier;
+import java.util.function.Supplier;
+import org.junit.Assume;
+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 FunctionOnlyTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+ private static final String EXPECTED_RESULT =
+ StringUtils.lines(" true true true", "2", "false", "3", "true", "5", "42.0");
+
+ @Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withDexRuntimes().withAllApiLevelsAlsoForCf().build(),
+ BooleanUtils.values());
+ }
+
+ public FunctionOnlyTest(TestParameters parameters, boolean shrinkDesugaredLibrary) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testFunctionCompositionD8() throws Exception {
+ testForD8()
+ .addLibraryFiles(getLibraryFile())
+ .setMinApi(parameters.getApiLevel())
+ .addInnerClasses(getClass())
+ .enableCoreLibraryDesugaring(minimalConfiguration())
+ .compile()
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ private LibraryDesugaringTestConfiguration minimalConfiguration() {
+ if (!isJDK11DesugaredLibrary()) {
+ return LibraryDesugaringTestConfiguration.builder()
+ .setMinApi(parameters.getApiLevel())
+ .setMode(CompilationMode.RELEASE)
+ .withKeepRuleConsumer()
+ .build();
+ }
+ return LibraryDesugaringTestConfiguration.builder()
+ .setMinApi(parameters.getApiLevel())
+ .addDesugaredLibraryConfiguration(
+ StringResource.fromFile(ToolHelper.getDesugarLibJsonMinimalForTesting()))
+ .setMode(shrinkDesugaredLibrary ? CompilationMode.RELEASE : CompilationMode.DEBUG)
+ .withKeepRuleConsumer()
+ .build();
+ }
+
+ @Test
+ public void testFunctionCompositionD8Cf2Cf() throws Exception {
+ Assume.assumeTrue(isJDK11DesugaredLibrary());
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ Path jar =
+ testForD8(Backend.CF)
+ .addLibraryFiles(getLibraryFile())
+ .setMinApi(parameters.getApiLevel())
+ .addInnerClasses(FunctionOnlyTest.class)
+ .enableCoreLibraryDesugaring(
+ parameters.getApiLevel(),
+ keepRuleConsumer,
+ StringResource.fromFile(ToolHelper.getDesugarLibJsonMinimalForTesting()))
+ .compile()
+ .writeToZip();
+ String desugaredLibraryKeepRules = "";
+ if (shrinkDesugaredLibrary && keepRuleConsumer.get() != null) {
+ // Collection keep rules is only implemented in the DEX writer.
+ assertEquals(0, keepRuleConsumer.get().length());
+ desugaredLibraryKeepRules = "-keep class java.util.function.* { *; }";
+ }
+ if (parameters.getRuntime().isDex()) {
+ testForD8()
+ .addProgramFiles(jar)
+ .setMinApi(parameters.getApiLevel())
+ .disableDesugaring()
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ (api, keep, shrink) ->
+ buildDesugaredLibrary(
+ api,
+ keep,
+ shrink,
+ ImmutableList.of(),
+ opt ->
+ opt.setDesugaredLibrarySpecification(
+ DesugaredLibrarySpecificationParser
+ .parseDesugaredLibrarySpecification(
+ StringResource.fromFile(
+ ToolHelper.getDesugarLibJsonMinimalForTesting()),
+ opt.itemFactory,
+ opt.reporter,
+ true,
+ parameters.getApiLevel().getLevel()))),
+ parameters.getApiLevel(),
+ desugaredLibraryKeepRules,
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+
+ } else {
+ testForJvm()
+ .addProgramFiles(jar)
+ .addRunClasspathFiles(getDesugaredLibraryInCFMinimal(parameters, options -> {}))
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+ }
+
+ public Path getDesugaredLibraryInCFMinimal(
+ TestParameters parameters, Consumer<InternalOptions> configurationForLibraryCompilation)
+ throws IOException, CompilationFailedException {
+ Path desugaredLib = temp.newFolder().toPath().resolve("desugar_jdk_libs.jar");
+ L8Command.Builder l8Builder =
+ L8Command.builder()
+ .addLibraryFiles(getLibraryFile())
+ .addProgramFiles(ToolHelper.getDesugarJDKLibs())
+ .addProgramFiles(ToolHelper.DESUGAR_LIB_CONVERSIONS)
+ .setMode(CompilationMode.DEBUG)
+ .addDesugaredLibraryConfiguration(
+ StringResource.fromFile(ToolHelper.getDesugarLibJsonMinimalForTesting()))
+ .setMinApiLevel(parameters.getApiLevel().getLevel())
+ .setOutput(desugaredLib, OutputMode.ClassFile);
+ ToolHelper.runL8(l8Builder.build(), configurationForLibraryCompilation);
+ return desugaredLib;
+ }
+
+ @Test
+ public void testFunctionCompositionR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addLibraryFiles(getLibraryFile())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Executor.class)
+ .addInnerClasses(getClass())
+ .enableCoreLibraryDesugaring(minimalConfiguration())
+ .allowStdoutMessages()
+ .compile()
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ static class Executor {
+
+ public static void main(String[] args) {
+ Function<Executor.Object1, Executor.Object3> function =
+ FunctionClass.composeFunction(Executor.Object2::new, Executor.Object3::new);
+ System.out.println(function.apply(new Executor.Object1()).toString());
+ BiFunction<String, String, Character> biFunction =
+ FunctionClass.composeBiFunctions(
+ (String i, String j) -> i + j, (String s) -> s.charAt(1));
+ System.out.println(biFunction.apply("1", "2"));
+ BooleanSupplier booleanSupplier =
+ () -> FunctionClass.composeBoolSuppliers(() -> true, () -> false).get();
+ System.out.println(booleanSupplier.getAsBoolean());
+ LongConsumer longConsumer = FunctionClass.composeLong(() -> 1L, System.out::println);
+ longConsumer.accept(2L);
+ DoublePredicate doublePredicate =
+ FunctionClass.composePredicate(d -> d > 1.0, d -> d == 2.0, d -> d < 3.0);
+ System.out.println(doublePredicate.test(2.0));
+ System.out.println(FunctionClass.extractInt(() -> 5));
+ System.out.println(FunctionClass.getDoubleSupplier().get());
+ }
+
+ static class Object1 {}
+
+ static class Object2 {
+
+ private Executor.Object1 field;
+
+ Object2(Executor.Object1 o) {
+ this.field = o;
+ }
+ }
+
+ static class Object3 {
+
+ private Executor.Object2 field;
+
+ Object3(Executor.Object2 o) {
+ this.field = o;
+ }
+
+ @Override
+ public String toString() {
+ return " "
+ + (field.field.getClass() == Executor.Object1.class)
+ + " "
+ + (field.getClass() == Executor.Object2.class)
+ + " "
+ + (getClass() == Executor.Object3.class);
+ }
+ }
+ }
+
+ static class FunctionClass {
+
+ public static <T, Q, R> Function<T, R> composeFunction(Function<T, Q> f1, Function<Q, R> f2) {
+ return f1.andThen(f2);
+ }
+
+ public static <T, R> BiFunction<T, T, R> composeBiFunctions(
+ BinaryOperator<T> operator, Function<T, R> function) {
+ return operator.andThen(function);
+ }
+
+ // BooleanSupplier is not a wrapped type, so it can't be placed on the boundary.
+ public static Supplier<Boolean> composeBoolSuppliers(
+ Supplier<Boolean> supplier1, Supplier<Boolean> supplier2) {
+ BooleanSupplier wrap1 = supplier1::get;
+ BooleanSupplier wrap2 = supplier2::get;
+ return () -> wrap1.getAsBoolean() && wrap2.getAsBoolean();
+ }
+
+ // LongSupplier is not a wrapped type, so it can't be placed on the boundary.
+ public static LongConsumer composeLong(Supplier<Long> supplier, LongConsumer consumer) {
+ LongSupplier wrap = supplier::get;
+ return l -> consumer.accept(l + wrap.getAsLong());
+ }
+
+ public static DoublePredicate composePredicate(
+ DoublePredicate predicate1, DoublePredicate predicate2, DoublePredicate predicate3) {
+ return predicate1.and(predicate2).and(predicate3);
+ }
+
+ // IntSupplier is not a wrapped type, so it can't be placed on the boundary.
+ public static int extractInt(Supplier<Integer> supplier) {
+ IntSupplier wrap = supplier::get;
+ return wrap.getAsInt();
+ }
+
+ // DoubleSupplier is not a wrapped type, so it can't be placed on the boundary.
+ public static Supplier<Double> getDoubleSupplier() {
+ DoubleSupplier supplier = () -> 42.0;
+ return supplier::getAsDouble;
+ }
+ }
+}
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 ef978a7..e05e09a 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
@@ -18,7 +18,6 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -69,21 +68,15 @@
private final boolean shrinkDesugaredLibrary = false;
private final Path androidJar;
- @Parameters(name = "{0}, libraryDesugarJavaUtilObjects: {1}")
+ @Parameters(name = "{0}")
public static List<Object[]> data() {
return buildParameters(
- getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(),
- BooleanUtils.values());
+ getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build());
}
- public ObjectsTest(TestParameters parameters, boolean libraryDesugarJavaUtilObjects) {
+ public ObjectsTest(TestParameters parameters) {
this.parameters = parameters;
- if (libraryDesugarJavaUtilObjects) {
- Assume.assumeTrue(
- "The alternative 3 configuration is available only in JDK 11 desugared library.",
- isJDK11DesugaredLibrary());
- }
- this.libraryDesugarJavaUtilObjects = libraryDesugarJavaUtilObjects;
+ this.libraryDesugarJavaUtilObjects = isJDK11DesugaredLibrary();
this.androidJar =
ToolHelper.getAndroidJar(
Ordered.max(parameters.getApiLevel(), getRequiredCompilationAPILevel()));
@@ -92,10 +85,7 @@
DesugaredLibrarySpecification desugaredLibrarySpecification(
InternalOptions options, boolean libraryCompilation, TestParameters parameters) {
return DesugaredLibrarySpecificationParser.parseDesugaredLibrarySpecification(
- StringResource.fromFile(
- libraryDesugarJavaUtilObjects
- ? ToolHelper.getDesugarLibJsonForTestingAlternative3()
- : ToolHelper.getDesugarLibJsonForTesting()),
+ StringResource.fromFile(ToolHelper.getDesugarLibJsonForTesting()),
options.dexItemFactory(),
options.reporter,
libraryCompilation,
diff --git a/src/test/java/com/android/tools/r8/desugar/suppressedexceptions/TwrSuppressedExceptionsTest.java b/src/test/java/com/android/tools/r8/desugar/suppressedexceptions/TwrSuppressedExceptionsTest.java
index e80af63..443c7ed 100644
--- a/src/test/java/com/android/tools/r8/desugar/suppressedexceptions/TwrSuppressedExceptionsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/suppressedexceptions/TwrSuppressedExceptionsTest.java
@@ -20,6 +20,7 @@
import java.io.Closeable;
import java.util.List;
import java.util.stream.Collectors;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -58,6 +59,7 @@
}
@Test
+ @Ignore("b/228587114")
public void testD8() throws Exception {
testForDesugaring(parameters)
.addProgramClasses(TestClass.class, MyClosable.class)
@@ -94,6 +96,7 @@
}
@Test
+ @Ignore("b/228587114")
public void testR8() throws Exception {
assumeTrue(
"R8 does not desugar CF so only run the high API variant.",
diff --git a/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java b/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
index 10d15e9..2bee502 100644
--- a/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
@@ -16,6 +16,7 @@
import java.io.IOException;
import java.util.List;
import java.util.jar.JarFile;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -67,6 +68,7 @@
}
@Test
+ @Ignore("b/228587114")
public void testD8() throws Exception {
testForD8(parameters.getBackend())
.addInnerClasses(getClass())
@@ -89,6 +91,7 @@
}
@Test
+ @Ignore("b/228587114")
public void testR8() throws Exception {
assumeTrue(parameters.isDexRuntime());
testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/ProgramAndLibraryDefinitionTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/ProgramAndLibraryDefinitionTest.java
new file mode 100644
index 0000000..d0c2f5c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/ProgramAndLibraryDefinitionTest.java
@@ -0,0 +1,231 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.resolution.interfacetargets;
+
+import static com.android.tools.r8.resolution.interfacetargets.ProgramAndLibraryDefinitionTest.ClassTestParam.DEFINED_WITH_METHOD;
+import static com.android.tools.r8.resolution.interfacetargets.ProgramAndLibraryDefinitionTest.ClassTestParam.NOT_DEFINED;
+
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
+import java.util.List;
+import java.util.function.Consumer;
+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 ProgramAndLibraryDefinitionTest extends TestBase {
+
+ public enum ClassTestParam {
+ NOT_DEFINED,
+ DEFINED_NO_METHOD,
+ DEFINED_WITH_METHOD
+ }
+
+ private final TestParameters parameters;
+ private final ClassTestParam aInLibrary;
+ private final ClassTestParam bInLibrary;
+ private final ClassTestParam aInProgram;
+ private final ClassTestParam bInProgram;
+
+ @Parameters(name = "{0}, aInLibrary: {1}, bInLibrary: {2}, aInProgram: {3}, bInProgram: {4}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters()
+ .withDefaultDexRuntime()
+ .withDefaultCfRuntime()
+ .apiLevelWithDefaultMethodsSupport()
+ .build(),
+ ClassTestParam.values(),
+ ClassTestParam.values(),
+ ClassTestParam.values(),
+ ClassTestParam.values());
+ }
+
+ public ProgramAndLibraryDefinitionTest(
+ TestParameters parameters,
+ ClassTestParam aInLibrary,
+ ClassTestParam bInLibrary,
+ ClassTestParam aInProgram,
+ ClassTestParam bInProgram) {
+ this.parameters = parameters;
+ this.aInLibrary = aInLibrary;
+ this.bInLibrary = bInLibrary;
+ this.aInProgram = aInProgram;
+ this.bInProgram = bInProgram;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ R8FullTestBuilder testBuilder =
+ testForR8(parameters.getBackend())
+ .addDefaultRuntimeLibrary(parameters)
+ .addProgramClasses(Main.class, Implementer.class, I.class)
+ .setMinApi(parameters.getApiLevel())
+ .addDontWarn(A.class, B.class)
+ .addKeepMainRule(Main.class)
+ .noMinification()
+ .allowUnusedDontWarnPatterns();
+ byte[] libraryA = getAFromClassTestParam(this.aInLibrary);
+ addIfNotNull(libraryA, testBuilder::addLibraryClassFileData);
+ byte[] libraryB = getBFromClassTestParam(bInLibrary);
+ addIfNotNull(libraryB, testBuilder::addLibraryClassFileData);
+ addIfNotNull(getAFromClassTestParam(aInProgram), testBuilder::addProgramClassFileData);
+ addIfNotNull(getBFromClassTestParam(bInProgram), testBuilder::addProgramClassFileData);
+ R8TestCompileResult compileResult = testBuilder.compile();
+ if (libraryA != null) {
+ compileResult.addRunClasspathClassFileData(libraryA);
+ }
+ if (libraryB != null) {
+ compileResult.addRunClasspathClassFileData(libraryB);
+ }
+ R8TestRunResult runResult = compileResult.run(parameters.getRuntime(), Main.class);
+ if (isExpectedToFailWithNoClassDefError()) {
+ runResult.assertFailureWithErrorThatThrows(NoClassDefFoundError.class);
+ } else if (isExpectedToFailWithNoSuchMethodError()) {
+ runResult.assertFailureWithErrorThatThrows(NoSuchMethodError.class);
+ } else if (isExpectedToFailWithICCE()) {
+ runResult.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
+ } else if (isDefinedOnAProgram() || (isDefinedOnALibrary() && !isAInProgram())) {
+ runResult.assertSuccessWithOutputLines("A::foo");
+ } else {
+ assert isDefinedOnBProgram() || (isDefinedOnBLibrary() && !isBInProgram());
+ runResult.assertSuccessWithOutputLines("B::foo");
+ }
+ }
+
+ private boolean isAInProgram() {
+ return aInProgram != NOT_DEFINED;
+ }
+
+ private boolean isBInProgram() {
+ return bInProgram != NOT_DEFINED;
+ }
+
+ private boolean isAInLibrary() {
+ return aInLibrary != NOT_DEFINED;
+ }
+
+ private boolean isBInLibrary() {
+ return bInLibrary != NOT_DEFINED;
+ }
+
+ private boolean isDefinedOnAProgram() {
+ return aInProgram == DEFINED_WITH_METHOD;
+ }
+
+ private boolean isDefinedOnBProgram() {
+ return bInProgram == DEFINED_WITH_METHOD;
+ }
+
+ private boolean isDefinedOnALibrary() {
+ return aInLibrary == DEFINED_WITH_METHOD;
+ }
+
+ private boolean isDefinedOnBLibrary() {
+ return bInLibrary == DEFINED_WITH_METHOD;
+ }
+
+ private boolean isExpectedToFailWithNoClassDefError() {
+ return (!isAInLibrary() && !isAInProgram()) || (!isBInLibrary() && !isBInProgram());
+ }
+
+ private boolean isExpectedToFailWithNoSuchMethodError() {
+ boolean notDefinedInProgram = !isDefinedOnAProgram() && !isDefinedOnBProgram();
+ boolean notDefinedInLibrary = !isDefinedOnALibrary() && !isDefinedOnBLibrary();
+ if (notDefinedInLibrary && notDefinedInProgram) {
+ return true;
+ }
+ if (notDefinedInProgram) {
+ // TODO(b/214382176): Currently, a program definition will shadow the library definition and
+ // R8 will optimize the interfaces away.
+ if (isDefinedOnALibrary() && isDefinedOnBLibrary()) {
+ return isAInProgram() && isBInProgram();
+ } else if (isDefinedOnALibrary()) {
+ return isAInProgram();
+ } else {
+ assert isDefinedOnBLibrary();
+ return isBInProgram();
+ }
+ }
+ return false;
+ }
+
+ private boolean isExpectedToFailWithICCE() {
+ if (isDefinedOnAProgram() && isDefinedOnBProgram()) {
+ return true;
+ }
+ if (!isAInProgram() && !isBInProgram()) {
+ return isDefinedOnALibrary() && isDefinedOnBLibrary();
+ }
+ if (isDefinedOnALibrary() && !isAInProgram() && isDefinedOnBProgram()) {
+ return true;
+ }
+ if (isDefinedOnBLibrary() && !isBInProgram() && isDefinedOnAProgram()) {
+ return true;
+ }
+ return false;
+ }
+
+ private void addIfNotNull(byte[] clazz, Consumer<byte[]> consumer) {
+ if (clazz != null) {
+ consumer.accept(clazz);
+ }
+ }
+
+ private byte[] getAFromClassTestParam(ClassTestParam param) throws Exception {
+ switch (param) {
+ case NOT_DEFINED:
+ return null;
+ case DEFINED_NO_METHOD:
+ return transformer(A.class).removeMethods(MethodPredicate.onName("foo")).transform();
+ default:
+ assert param == DEFINED_WITH_METHOD;
+ return transformer(A.class).transform();
+ }
+ }
+
+ private byte[] getBFromClassTestParam(ClassTestParam param) throws Exception {
+ switch (param) {
+ case NOT_DEFINED:
+ return null;
+ case DEFINED_NO_METHOD:
+ return transformer(B.class).removeMethods(MethodPredicate.onName("bar")).transform();
+ default:
+ assert param == DEFINED_WITH_METHOD;
+ return transformer(B.class).renameMethod(MethodPredicate.onName("bar"), "foo").transform();
+ }
+ }
+
+ public interface A {
+
+ default void foo() {
+ System.out.println("A::foo");
+ }
+ }
+
+ public interface B {
+
+ default void /* foo */ bar() { // Will be renamed to foo.
+ System.out.println("B::foo");
+ }
+ }
+
+ public interface I extends A {}
+
+ public static class Implementer implements I, B {}
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new Implementer().foo();
+ }
+ }
+}
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 d348b54..b1e37b4 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
@@ -126,6 +126,16 @@
}
@Test
+ public void testWindowsLineEndings() throws IOException {
+ ActualRetraceBotStackTrace stackTrace = new ActualRetraceBotStackTrace();
+ runTest(
+ stackTrace.mapping().replace("\n", "\r\n"),
+ StringUtils.joinLines(stackTrace.obfuscatedStackTrace()),
+ false,
+ StringUtils.joinLines(stackTrace.retracedStackTrace()) + StringUtils.LINE_SEPARATOR);
+ }
+
+ @Test
public void testRegularExpression() throws IOException {
ActualRetraceBotStackTrace stackTrace = new ActualRetraceBotStackTrace();
runTest(
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceWhitespaceTest.java b/src/test/java/com/android/tools/r8/retrace/RetraceWhitespaceTest.java
new file mode 100644
index 0000000..7b9c08d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceWhitespaceTest.java
@@ -0,0 +1,86 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import com.android.tools.r8.ProguardVersion;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+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.List;
+import org.junit.Before;
+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 RetraceWhitespaceTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ private final String MAPPING =
+ StringUtils.lines(
+ " foo -> bar:", "void someMethod() -> a", " baz -> qux:", "void someOtherMethod() -> b");
+ private final List<String> STACKTRACE =
+ ImmutableList.of(" at bar.a(SourceFile)", " at qux.b(SourceFile)");
+ private final String EXPECTED =
+ StringUtils.lines(" at foo.someMethod(foo.java)", " at baz.someOtherMethod(baz.java)");
+
+ private Path mappingFile;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public RetraceWhitespaceTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Before
+ public void before() throws Exception {
+ mappingFile = temp.newFile("mapping.txt").toPath();
+ Files.write(mappingFile, MAPPING.getBytes());
+ }
+
+ @Test
+ public void testR8Retrace() {
+ // TODO(b/228154323): R8 Retrace should not be white space sensitive.
+ assertThrows(
+ InvalidMappingFileException.class,
+ () ->
+ Retrace.run(
+ RetraceCommand.builder()
+ .setProguardMapProducer(ProguardMapProducer.fromPath(mappingFile))
+ .setStackTrace(STACKTRACE)
+ .setRetracedStackTraceConsumer(lines -> {})
+ .build()));
+ }
+
+ @Test
+ public void testPGRetrace() throws Exception {
+ Path stackTraceFile = temp.newFile("stacktrace.txt").toPath();
+ Files.write(stackTraceFile, StringUtils.joinLines(STACKTRACE).getBytes(StandardCharsets.UTF_8));
+ List<String> command = new ArrayList<>();
+ command.add(ProguardVersion.V7_0_0.getRetraceScript().toString());
+ command.add(mappingFile.toString());
+ command.add(stackTraceFile.toString());
+ ProcessBuilder builder = new ProcessBuilder(command);
+ ProcessResult processResult = ToolHelper.runProcess(builder);
+ assertEquals(0, processResult.exitCode);
+ assertEquals(EXPECTED, processResult.stdout);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTest.java b/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTest.java
index c688182..a0029a0 100644
--- a/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTest.java
@@ -4,50 +4,45 @@
package com.android.tools.r8.shaking;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticOrigin;
+import static com.android.tools.r8.OriginMatcher.hasPart;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeTrue;
-import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.DataResourceProvider;
-import com.android.tools.r8.DexIndexedConsumer;
-import com.android.tools.r8.DiagnosticsChecker;
-import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.ProgramResource;
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.ProgramResourceProvider;
-import com.android.tools.r8.R8Command;
import com.android.tools.r8.ResourceException;
import com.android.tools.r8.TestBase;
-import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.TestParameters;
import com.android.tools.r8.origin.ArchiveEntryOrigin;
import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.origin.PathOrigin;
-import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteStreams;
-import com.google.common.io.CharSource;
-import java.io.FileOutputStream;
import java.io.IOException;
-import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import java.util.jar.JarOutputStream;
-import java.util.zip.ZipEntry;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
@@ -60,7 +55,7 @@
public static void main(String[] args) {
try {
- Class bClass = Class.forName(buildClassName("B"));
+ Class.forName(buildClassName("B"));
System.out.println("YES");
} catch (ClassNotFoundException e) {
System.out.println("NO");
@@ -70,113 +65,139 @@
static class B {}
- private Backend backend;
+ enum LibraryType {
+ JAR_WITH_RULES,
+ AAR_WITH_RULES,
+ AAR_WITH_RULES_ONLY_IN_JAR,
+ AAR_WITH_RULES_BOTH_IN_JAR_AND_IN_AAR;
- @Parameters(name = "Backend: {0}")
- public static Backend[] data() {
- return ToolHelper.getBackends();
+ boolean isAar() {
+ return this != JAR_WITH_RULES;
+ }
+
+ boolean hasRulesInJar() {
+ return this != AAR_WITH_RULES;
+ }
+
+ boolean hasRulesInAar() {
+ return this == AAR_WITH_RULES || this == AAR_WITH_RULES_BOTH_IN_JAR_AND_IN_AAR;
+ }
}
- public LibraryProvidedProguardRulesTest(Backend backend) {
- this.backend = backend;
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameter(1)
+ public LibraryType libraryType;
+
+ @Parameters(name = "{0} AAR: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(), LibraryType.values());
}
- private void addTextJarEntry(JarOutputStream out, String name, String content) throws Exception {
- out.putNextEntry(new ZipEntry(name));
- ByteStreams.copy(
- CharSource.wrap(content).asByteSource(StandardCharsets.UTF_8).openBufferedStream(), out);
- out.closeEntry();
- }
-
- private AndroidApp runTest(List<String> rules, DiagnosticsHandler handler) throws Exception {
- Path jar = temp.newFile("test.jar").toPath();
- try (JarOutputStream out = new JarOutputStream(new FileOutputStream(jar.toFile()))) {
- addTestClassesToJar(out, ImmutableList.of(A.class, B.class));
- for (int i = 0; i < rules.size(); i++) {
+ private Path buildLibrary(List<String> rules) throws Exception {
+ ZipBuilder jarBuilder =
+ ZipBuilder.builder(temp.newFile(libraryType.isAar() ? "classes.jar" : "test.jar").toPath());
+ addTestClassesToZip(jarBuilder.getOutputStream(), ImmutableList.of(A.class, B.class));
+ if (libraryType.hasRulesInJar()) {
+ for (int i = 0; i < rules.size(); i++) {
String name = "META-INF/proguard/jar" + (i == 0 ? "" : i) + ".rules";
- addTextJarEntry(out, name, rules.get(i));
+ jarBuilder.addText(name, rules.get(i));
}
}
-
- try {
- R8Command.Builder builder =
- (handler != null ? R8Command.builder(handler) : R8Command.builder()).addProgramFiles(jar);
- if (backend == Backend.DEX) {
- builder
- .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
- .addLibraryFiles(ToolHelper.getDefaultAndroidJar());
- } else {
- assert backend == Backend.CF;
- builder
- .setProgramConsumer(ClassFileConsumer.emptyConsumer())
- .addLibraryFiles(ToolHelper.getJava8RuntimeJar());
+ if (libraryType.isAar()) {
+ Path jar = jarBuilder.build();
+ String allRules = StringUtils.lines(rules);
+ ZipBuilder aarBuilder = ZipBuilder.builder(temp.newFile("test.aar").toPath());
+ aarBuilder.addFilesRelative(jar.getParent(), jar);
+ if (libraryType.hasRulesInAar()) {
+ aarBuilder.addText("proguard.txt", allRules);
}
- return ToolHelper.runR8(builder.build());
- } catch (CompilationFailedException e) {
- assertNotNull(handler);
- return null;
+ return aarBuilder.build();
+ } else {
+ return jarBuilder.build();
}
}
- private AndroidApp runTest(String rules, DiagnosticsHandler handler) throws Exception {
- return runTest(ImmutableList.of(rules), handler);
+ private CodeInspector runTest(List<String> rules) throws Exception {
+ return testForR8(parameters.getBackend())
+ .addProgramFiles(buildLibrary(rules))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspector();
}
+ private CodeInspector runTest(String rules) throws Exception {
+ return runTest(ImmutableList.of(rules));
+ }
@Test
public void keepOnlyA() throws Exception {
- AndroidApp app = runTest("-keep class " + A.class.getTypeName() +" {}", null);
- CodeInspector inspector = new CodeInspector(app);
- assertThat(inspector.clazz(A.class), isPresent());
+ CodeInspector inspector = runTest("-keep class " + A.class.getTypeName() + " {}");
+ // TODO(b/228319861): Read Proguard rules from AAR's.
+ assertThat(inspector.clazz(A.class), notIf(isPresent(), libraryType.isAar()));
assertThat(inspector.clazz(B.class), not(isPresent()));
}
@Test
public void keepOnlyB() throws Exception {
- AndroidApp app = runTest("-keep class **B {}", null);
- CodeInspector inspector = new CodeInspector(app);
+ CodeInspector inspector = runTest("-keep class **B {}");
assertThat(inspector.clazz(A.class), not(isPresent()));
- assertThat(inspector.clazz(B.class), isPresent());
+ // TODO(b/228319861): Read Proguard rules from AAR's.
+ assertThat(inspector.clazz(B.class), notIf(isPresent(), libraryType.isAar()));
}
@Test
public void keepBoth() throws Exception {
- AndroidApp app = runTest("-keep class ** {}", null);
- CodeInspector inspector = new CodeInspector(app);
- assertThat(inspector.clazz(A.class), isPresent());
- assertThat(inspector.clazz(B.class), isPresent());
+ CodeInspector inspector = runTest("-keep class ** {}");
+ // TODO(b/228319861): Read Proguard rules from AAR's.
+ assertThat(inspector.clazz(A.class), notIf(isPresent(), libraryType.isAar()));
+ assertThat(inspector.clazz(B.class), notIf(isPresent(), libraryType.isAar()));
}
@Test
public void multipleFiles() throws Exception {
- AndroidApp app = runTest(ImmutableList.of("-keep class **A {}", "-keep class **B {}"), null);
- CodeInspector inspector = new CodeInspector(app);
- assertThat(inspector.clazz(A.class), isPresent());
- assertThat(inspector.clazz(B.class), isPresent());
- }
-
- private void checkOrigin(Origin origin) {
- assertTrue(origin instanceof ArchiveEntryOrigin);
- assertEquals(origin.part(), "META-INF/proguard/jar.rules");
- assertTrue(origin.parent() instanceof PathOrigin);
+ CodeInspector inspector = runTest(ImmutableList.of("-keep class **A {}", "-keep class **B {}"));
+ // TODO(b/228319861): Read Proguard rules from AAR's.
+ assertThat(inspector.clazz(A.class), notIf(isPresent(), libraryType.isAar()));
+ assertThat(inspector.clazz(B.class), notIf(isPresent(), libraryType.isAar()));
}
@Test
public void syntaxError() throws Exception {
- DiagnosticsChecker checker = new DiagnosticsChecker();
- AndroidApp app = runTest("error", checker);
- assertNull(app);
- DiagnosticsChecker.checkDiagnostic(
- checker.errors.get(0), this::checkOrigin, 1, 1, "Expected char '-'");
+ // TODO(b/228319861): Read Proguard rules from AAR's.
+ assumeTrue(!libraryType.isAar());
+ assertThrows(
+ CompilationFailedException.class,
+ () ->
+ testForR8(parameters.getBackend())
+ .addProgramFiles(buildLibrary(ImmutableList.of("error")))
+ .setMinApi(parameters.getApiLevel())
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertErrorThatMatches(
+ allOf(
+ diagnosticMessage(containsString("Expected char '-'")),
+ diagnosticOrigin(hasPart("META-INF/proguard/jar.rules")),
+ diagnosticOrigin(instanceOf(ArchiveEntryOrigin.class))))));
}
@Test
public void includeError() throws Exception {
- DiagnosticsChecker checker = new DiagnosticsChecker();
- AndroidApp app = runTest("-include other.rules", checker);
- assertNull(app);
- DiagnosticsChecker.checkDiagnostic(checker.errors.get(0), this::checkOrigin, 1, 10,
- "Options with file names are not supported");
+ // TODO(b/228319861): Read Proguard rules from AAR's.
+ assumeTrue(!libraryType.isAar());
+ assertThrows(
+ CompilationFailedException.class,
+ () ->
+ testForR8(parameters.getBackend())
+ .addProgramFiles(buildLibrary(ImmutableList.of("-include other.rules")))
+ .setMinApi(parameters.getApiLevel())
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertErrorThatMatches(
+ diagnosticMessage(
+ containsString("Options with file names are not supported")))));
}
static class TestProvider implements ProgramResourceProvider, DataResourceProvider {
@@ -207,27 +228,20 @@
@Test
public void throwingDataResourceProvider() throws Exception {
- DiagnosticsChecker checker = new DiagnosticsChecker();
- try {
- R8Command.Builder builder =
- R8Command.builder(checker).addProgramResourceProvider(new TestProvider());
- if (backend == Backend.DEX) {
- builder
- .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
- .addLibraryFiles(ToolHelper.getDefaultAndroidJar());
- } else {
- assert backend == Backend.CF;
- builder
- .setProgramConsumer(ClassFileConsumer.emptyConsumer())
- .addLibraryFiles(ToolHelper.getJava8RuntimeJar());
- }
- builder.build();
- fail("Should not succeed");
- } catch (CompilationFailedException e) {
- DiagnosticsChecker.checkDiagnostic(
- checker.errors.get(0),
- origin -> assertSame(origin, Origin.unknown()),
- "Cannot provide data resources after all");
- }
+ // TODO(b/228319861): Read Proguard rules from AAR's.
+ assumeTrue(!libraryType.isAar());
+ assertThrows(
+ CompilationFailedException.class,
+ () ->
+ testForR8(parameters.getBackend())
+ .addProgramResourceProviders(new TestProvider())
+ .setMinApi(parameters.getApiLevel())
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertErrorThatMatches(
+ allOf(
+ diagnosticMessage(
+ containsString("Cannot provide data resources after all")),
+ diagnosticOrigin(is(Origin.unknown()))))));
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/proxy/MockitoTest.java b/src/test/java/com/android/tools/r8/shaking/proxy/MockitoTest.java
index d31da79..8a4113a 100644
--- a/src/test/java/com/android/tools/r8/shaking/proxy/MockitoTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/proxy/MockitoTest.java
@@ -51,7 +51,7 @@
testForR8(parameters.getBackend())
.addProgramFiles(MOCKITO_INTERFACE_JAR)
.addKeepRuleFiles(flagToKeepTestRunner)
- .addDontWarn("org.junit.**", "org.mockito.**")
+ .addDontWarn("org.mockito.**")
.minification(minify)
.setMinApi(parameters.getApiLevel())
.compile()
@@ -70,7 +70,7 @@
testForR8(parameters.getBackend())
.addProgramFiles(MOCKITO_INTERFACE_JAR)
.addKeepRuleFiles(flagToKeepInterfaceConditionally)
- .addDontWarn("org.junit.**", "org.mockito.**")
+ .addDontWarn("org.mockito.**")
.minification(minify)
.setMinApi(parameters.getApiLevel())
.compile()
diff --git a/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java b/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java
new file mode 100644
index 0000000..a6dbbb7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java
@@ -0,0 +1,126 @@
+// Copyright (c) 2022, 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.startup;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertTrue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+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 com.android.tools.r8.experimental.startup.StartupConfiguration;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.Lists;
+import java.util.Collections;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MinimalStartupDexTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withDexRuntimes()
+ .withApiLevelsStartingAtIncluding(AndroidApiLevel.L)
+ .build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepClassAndMembersRules(Main.class)
+ .addOptionsModification(
+ options ->
+ options
+ .getStartupOptions()
+ .setEnableMinimalStartupDex()
+ .setEnableStartupCompletenessCheckForTesting()
+ .setStartupConfiguration(
+ new StartupConfiguration(
+ Lists.newArrayList(
+ toDexType(Main.class, options.dexItemFactory()),
+ toDexType(StartupClass.class, options.dexItemFactory())),
+ Collections.emptyList())))
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspectMultiDex(
+ primaryDexInspector -> {
+ // StartupClass should be in the primary dex.
+ ClassSubject startupClassSubject = primaryDexInspector.clazz(StartupClass.class);
+ assertThat(startupClassSubject, isPresent());
+
+ MethodSubject startupMethodSubject = startupClassSubject.uniqueMethodWithName("foo");
+ assertThat(startupMethodSubject, isPresent());
+ assertTrue(
+ startupMethodSubject.streamInstructions().noneMatch(InstructionSubject::isThrow));
+ },
+ secondaryDexInspector -> {
+ // NonStartupClass should be in the secondary dex and should be transformed such that
+ // all methods throw null.
+ ClassSubject nonStartupClassSubject =
+ secondaryDexInspector.clazz(NonStartupClass.class);
+ assertThat(nonStartupClassSubject, isPresent());
+
+ MethodSubject nonStartupClinitSubject = nonStartupClassSubject.clinit();
+ assertThat(nonStartupClinitSubject, isPresent());
+ assertTrue(
+ nonStartupClinitSubject
+ .streamInstructions()
+ .anyMatch(InstructionSubject::isThrow));
+
+ MethodSubject nonStartupMethodSubject =
+ nonStartupClassSubject.uniqueMethodWithName("bar");
+ assertThat(nonStartupMethodSubject, isPresent());
+ assertTrue(
+ nonStartupMethodSubject
+ .streamInstructions()
+ .anyMatch(InstructionSubject::isThrow));
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("foo");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ StartupClass.foo();
+ }
+
+ // @Keep
+ public void onClick() {
+ NonStartupClass.bar();
+ }
+ }
+
+ static class StartupClass {
+
+ @NeverInline
+ static void foo() {
+ System.out.println("foo");
+ }
+ }
+
+ static class NonStartupClass {
+
+ @NeverInline
+ static void bar() {
+ System.out.println("bar");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java b/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java
new file mode 100644
index 0000000..8a73ac4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java
@@ -0,0 +1,93 @@
+// Copyright (c) 2022, 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.startup;
+
+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 com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class StartupInstrumentationTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addOptionsModification(
+ options -> options.getStartupOptions().setEnableStartupInstrumentation())
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(getExpectedOutput());
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepClassAndMembersRules(Main.class)
+ .addOptionsModification(
+ options -> options.getStartupOptions().setEnableStartupInstrumentation())
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(getExpectedOutput());
+ }
+
+ private static List<String> getExpectedOutput() {
+ return ImmutableList.of(
+ "Lcom/android/tools/r8/startup/StartupInstrumentationTest$Main;",
+ "Lcom/android/tools/r8/startup/StartupInstrumentationTest$StartupClass;",
+ "foo");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ StartupClass.foo();
+ }
+
+ // @Keep
+ public void onClick() {
+ NonStartupClass.bar();
+ }
+ }
+
+ static class StartupClass {
+
+ @NeverInline
+ static void foo() {
+ System.out.println("foo");
+ }
+ }
+
+ static class NonStartupClass {
+
+ @NeverInline
+ static void bar() {
+ System.out.println("bar");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/testingannotations/NeverInlineStaticInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/testingannotations/NeverInlineStaticInterfaceMethodTest.java
new file mode 100644
index 0000000..c5bfa0d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/testingannotations/NeverInlineStaticInterfaceMethodTest.java
@@ -0,0 +1,78 @@
+// Copyright (c) 2022, 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.testingannotations;
+
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+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 com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class NeverInlineStaticInterfaceMethodTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withCfRuntimes()
+ .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
+ .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
+ .build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject iClassSubject = inspector.clazz(I.class);
+ assertThat(iClassSubject, isPresent());
+
+ MethodSubject iMethodSubject = iClassSubject.uniqueMethodWithName("m");
+ assertThat(iClassSubject, isPresent());
+
+ MethodSubject mainMethodSubject = inspector.clazz(Main.class).mainMethod();
+ assertThat(mainMethodSubject, isPresent());
+ assertThat(mainMethodSubject, invokesMethod(iMethodSubject));
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("I.m()");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ I.m();
+ }
+ }
+
+ interface I {
+
+ @NeverInline
+ static void m() {
+ System.out.println("I.m()");
+ }
+ }
+}
diff --git a/tools/archive.py b/tools/archive.py
index a3f906f..893fc19 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -127,55 +127,7 @@
if not utils.IsWindows():
PrintResourceInfo()
- # Create maven release which uses a build that exclude dependencies.
- create_maven_release.generate_r8_maven_zip(utils.MAVEN_ZIP)
- create_maven_release.generate_r8_maven_zip(
- utils.MAVEN_ZIP_LIB, is_r8lib=True)
-
- # Generate and copy a full build without dependencies.
- gradle.RunGradleExcludeDeps([utils.R8, utils.R8_SRC])
- shutil.copyfile(utils.R8_JAR, utils.R8_FULL_EXCLUDE_DEPS_JAR)
-
- # Ensure all archived artifacts has been built before archiving.
- # The target tasks postfixed by 'lib' depend on the actual target task so
- # building it invokes the original task first.
- # The '-Pno_internal' flag is important because we generate the lib based on uses in tests.
- gradle.RunGradle([
- utils.R8,
- utils.D8,
- utils.R8LIB,
- utils.R8LIB_NO_DEPS,
- utils.R8RETRACE,
- utils.R8RETRACE_NO_DEPS,
- utils.LIBRARY_DESUGAR_CONVERSIONS,
- '-Pno_internal'
- ])
-
- # Create maven release of the desuage_jdk_libs configuration. This require
- # an r8.jar with dependencies to have been built.
- create_maven_release.generate_desugar_configuration_maven_zip(
- utils.DESUGAR_CONFIGURATION_MAVEN_ZIP,
- utils.DESUGAR_CONFIGURATION,
- utils.DESUGAR_IMPLEMENTATION)
- create_maven_release.generate_desugar_configuration_maven_zip(
- utils.DESUGAR_CONFIGURATION_LEGACY_JDK11_MAVEN_ZIP,
- utils.DESUGAR_CONFIGURATION_JDK11_LEGACY,
- utils.DESUGAR_IMPLEMENTATION_JDK11)
-
- version = GetVersion()
- is_main = IsMain(version)
- if is_main:
- # On main we use the git hash to archive with
- print('On main, using git hash for archiving')
- version = GetGitHash()
-
- destination = GetVersionDestination('gs://', version, is_main)
- if utils.cloud_storage_exists(destination) and not options.dry_run:
- raise Exception('Target archive directory %s already exists' % destination)
with utils.TempDir() as temp:
- # Create pom file for our maven repository that we build for testing.
- default_pom_file = os.path.join(temp, 'r8.pom')
- create_maven_release.write_default_r8_pom_file(default_pom_file, version)
version_file = os.path.join(temp, 'r8-version.properties')
with open(version_file,'w') as version_writer:
@@ -190,6 +142,56 @@
version_writer.write(releaser)
version_writer.write('version-file.version.code=1\n')
+ # Create maven release which uses a build that exclude dependencies.
+ create_maven_release.generate_r8_maven_zip(utils.MAVEN_ZIP, version_file=version_file)
+ create_maven_release.generate_r8_maven_zip(
+ utils.MAVEN_ZIP_LIB, is_r8lib=True, version_file=version_file)
+
+ # Generate and copy a full build without dependencies.
+ gradle.RunGradleExcludeDeps([utils.R8, utils.R8_SRC])
+ shutil.copyfile(utils.R8_JAR, utils.R8_FULL_EXCLUDE_DEPS_JAR)
+
+ # Ensure all archived artifacts has been built before archiving.
+ # The target tasks postfixed by 'lib' depend on the actual target task so
+ # building it invokes the original task first.
+ # The '-Pno_internal' flag is important because we generate the lib based on uses in tests.
+ gradle.RunGradle([
+ utils.R8,
+ utils.D8,
+ utils.R8LIB,
+ utils.R8LIB_NO_DEPS,
+ utils.R8RETRACE,
+ utils.R8RETRACE_NO_DEPS,
+ utils.LIBRARY_DESUGAR_CONVERSIONS,
+ '-Pno_internal'
+ ])
+
+ # Create maven release of the desuage_jdk_libs configuration. This require
+ # an r8.jar with dependencies to have been built.
+ create_maven_release.generate_desugar_configuration_maven_zip(
+ utils.DESUGAR_CONFIGURATION_MAVEN_ZIP,
+ utils.DESUGAR_CONFIGURATION,
+ utils.DESUGAR_IMPLEMENTATION)
+ create_maven_release.generate_desugar_configuration_maven_zip(
+ utils.DESUGAR_CONFIGURATION_LEGACY_JDK11_MAVEN_ZIP,
+ utils.DESUGAR_CONFIGURATION_JDK11_LEGACY,
+ utils.DESUGAR_IMPLEMENTATION_JDK11)
+
+ version = GetVersion()
+ is_main = IsMain(version)
+ if is_main:
+ # On main we use the git hash to archive with
+ print('On main, using git hash for archiving')
+ version = GetGitHash()
+
+ destination = GetVersionDestination('gs://', version, is_main)
+ if utils.cloud_storage_exists(destination) and not options.dry_run:
+ raise Exception('Target archive directory %s already exists' % destination)
+
+ # Create pom file for our maven repository that we build for testing.
+ default_pom_file = os.path.join(temp, 'r8.pom')
+ create_maven_release.write_default_r8_pom_file(default_pom_file, version)
+
for file in [
utils.D8_JAR,
utils.R8_JAR,
diff --git a/tools/create_maven_release.py b/tools/create_maven_release.py
index 10bbea4..6250c75 100755
--- a/tools/create_maven_release.py
+++ b/tools/create_maven_release.py
@@ -9,7 +9,7 @@
import jdk
import json
from os import makedirs
-from os.path import join
+from os.path import join, basename
from shutil import copyfile, make_archive, move, rmtree
import subprocess
import sys
@@ -309,7 +309,7 @@
base_no_zip = out[0:len(out)-4]
make_archive(base_no_zip, 'zip', tmp_dir)
-def generate_r8_maven_zip(out, is_r8lib=False):
+def generate_r8_maven_zip(out, is_r8lib=False, version_file=None):
# Build the R8 no deps artifact.
if not is_r8lib:
gradle.RunGradleExcludeDeps([utils.R8])
@@ -318,6 +318,13 @@
version = determine_version()
with utils.TempDir() as tmp_dir:
+ file_copy = join(tmp_dir, 'copy_of_jar.jar')
+ copyfile(utils.R8LIB_JAR if is_r8lib else utils.R8_JAR, file_copy)
+
+ if version_file:
+ with zipfile.ZipFile(file_copy, 'a') as zip:
+ zip.write(version_file, basename(version_file))
+
# Generate the pom file.
pom_file = join(tmp_dir, 'r8.pom')
write_pom_file(
@@ -331,7 +338,7 @@
'r8',
version,
pom_file,
- utils.R8LIB_JAR if is_r8lib else utils.R8_JAR,
+ file_copy,
out)
# Write the desugaring configuration of a jar file with the following content:
diff --git a/tools/jdk.py b/tools/jdk.py
index ea6be86..c9cb628 100755
--- a/tools/jdk.py
+++ b/tools/jdk.py
@@ -11,7 +11,7 @@
JDK_DIR = os.path.join(defines.THIRD_PARTY, 'openjdk')
def GetJdkHome():
- return GetJdk9Home()
+ return GetJdk11Home()
def GetJdk11Home():
root = os.path.join(JDK_DIR, 'jdk-11')
diff --git a/tools/r8_release.py b/tools/r8_release.py
index ef7ea7a..eab96be 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -15,7 +15,7 @@
import utils
-R8_DEV_BRANCH = '3.3'
+R8_DEV_BRANCH = '4.0'
R8_VERSION_FILE = os.path.join(
'src', 'main', 'java', 'com', 'android', 'tools', 'r8', 'Version.java')
THIS_FILE_RELATIVE = os.path.join('tools', 'r8_release.py')
diff --git a/tools/run_benchmark.py b/tools/run_benchmark.py
index 904b509..41da451 100755
--- a/tools/run_benchmark.py
+++ b/tools/run_benchmark.py
@@ -8,10 +8,10 @@
import subprocess
import sys
+import compiledump
import gradle
import jdk
import utils
-import compiledump
NONLIB_BUILD_TARGET = 'R8WithRelocatedDeps'
NONLIB_TEST_BUILD_TARGETS = [utils.R8_TESTS_TARGET, utils.R8_TESTS_DEPS_TARGET]
@@ -99,7 +99,13 @@
if not options.no_build:
gradle.RunGradle(buildTargets + ['-Pno_internal'])
- return run(options, r8jar, testjars)
+ if not options.golem:
+ # When running locally, change the working directory to be in 'temp'.
+ # This is hard to do properly within the JVM so we do it here.
+ with utils.ChangedWorkingDirectory(temp):
+ return run(options, r8jar, testjars)
+ else:
+ return run(options, r8jar, testjars)
def run(options, r8jar, testjars):
jdkhome = get_jdk_home(options, options.benchmark)
@@ -113,7 +119,9 @@
'com.android.tools.r8.benchmarks.BenchmarkMainEntryRunner',
options.benchmark,
options.target,
- 'golem' if options.golem else 'local',
+ # When running locally the working directory is moved and we pass the
+ # repository root as an argument. The runner can then setup dependencies.
+ 'golem' if options.golem else utils.REPO_ROOT,
])
return subprocess.check_call(cmd)
diff --git a/tools/startup/adb_utils.py b/tools/startup/adb_utils.py
index 136c99a..981bcbd 100644
--- a/tools/startup/adb_utils.py
+++ b/tools/startup/adb_utils.py
@@ -91,7 +91,7 @@
def get_minor_major_page_faults(app_id, device_id=None):
pid = get_pid(app_id, device_id)
- cmd = create_adb_cmd('shell ps -p %i -o MINFL,MAJFL' % pid)
+ cmd = create_adb_cmd('shell ps -p %i -o MINFL,MAJFL' % pid, device_id)
stdout = subprocess.check_output(cmd).decode('utf-8')
lines_it = iter(stdout.splitlines())
first_line = next(lines_it)
@@ -145,7 +145,7 @@
cmd = create_adb_cmd(
'shell profman --dump-classes-and-methods'
' --profile-file=%s --apk=%s --dex-location=%s'
- % (profile_path, apk_path, apk_path))
+ % (profile_path, apk_path, apk_path), device_id)
stdout = subprocess.check_output(cmd).decode('utf-8').strip()
lines = stdout.splitlines()
classes_and_methods = []
diff --git a/tools/startup/generate_startup_descriptors.py b/tools/startup/generate_startup_descriptors.py
index a3eb72c..ea0fdf3 100755
--- a/tools/startup/generate_startup_descriptors.py
+++ b/tools/startup/generate_startup_descriptors.py
@@ -22,6 +22,7 @@
print(
'Found %i new startup descriptors in iteration %i'
% (number_of_new_startup_descriptors, iteration + 1))
+ return number_of_new_startup_descriptors
def generate_startup_profile_on_device(options):
if not options.use_existing_profile:
@@ -74,6 +75,8 @@
def parse_options(argv):
result = argparse.ArgumentParser(
description='Generate a perfetto trace file.')
+ result.add_argument('--apk',
+ help='Path to the APK')
result.add_argument('--app-id',
help='The application ID of interest',
required=True)
@@ -104,6 +107,11 @@
'descriptors are found',
action='store_true',
default=False)
+ result.add_argument('--until-stable-iterations',
+ help='Number of times that profile generation must must '
+ 'not find new startup descriptors before exiting',
+ default=1,
+ type=int)
result.add_argument('--use-existing-profile',
help='Do not launch app to generate startup profile',
action='store_true',
@@ -116,13 +124,22 @@
def main(argv):
(options, args) = parse_options(argv)
+ adb_utils.root(options.device_id)
+ if options.apk:
+ adb_utils.uninstall(options.app_id, options.device_id)
+ adb_utils.install(options.apk, options.device_id)
startup_descriptors = set()
if options.until_stable:
iteration = 0
+ stable_iterations = 0
while True:
diff = extend_startup_descriptors(startup_descriptors, iteration, options)
if diff == 0:
- break
+ stable_iterations = stable_iterations + 1
+ if stable_iterations == options.until_stable_iterations:
+ break
+ else:
+ stable_iterations = 0
iteration = iteration + 1
else:
for iteration in range(options.iterations):
diff --git a/tools/startup/trace_generator.py b/tools/startup/measure_startup.py
similarity index 95%
rename from tools/startup/trace_generator.py
rename to tools/startup/measure_startup.py
index 37f5c71..080f827 100755
--- a/tools/startup/trace_generator.py
+++ b/tools/startup/measure_startup.py
@@ -82,11 +82,14 @@
os.makedirs(out_dir, exist_ok=True)
def run(out_dir, options, tmp_dir):
- assert adb_utils.get_screen_state().is_on_and_unlocked()
+ assert adb_utils.get_screen_state(options.device_id).is_on_and_unlocked()
# Start perfetto trace collector.
- perfetto_process, perfetto_trace_path = perfetto_utils.record_android_trace(
- out_dir, tmp_dir)
+ perfetto_process = None
+ perfetto_trace_path = None
+ if not options.no_perfetto:
+ perfetto_process, perfetto_trace_path = perfetto_utils.record_android_trace(
+ out_dir, tmp_dir)
# Launch main activity.
launch_activity_result = adb_utils.launch_activity(
@@ -96,7 +99,8 @@
wait_for_activity_to_launch=True)
# Wait for perfetto trace collector to stop.
- perfetto_utils.stop_record_android_trace(perfetto_process, out_dir)
+ if not options.no_perfetto:
+ perfetto_utils.stop_record_android_trace(perfetto_process, out_dir)
# Get minor and major page faults from app process.
data = compute_data(launch_activity_result, perfetto_trace_path, options)
diff --git a/tools/utils.py b/tools/utils.py
index 3200b3a..2a4b18e 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -636,7 +636,7 @@
# Build metadata currently not suppported
def larger_than(self, other):
- if self.prepelease or other.prepelease:
+ if self.prerelease or other.prerelease:
raise Exception("Comparison with prerelease not implemented")
if self.major > other.major:
return True