Version 1.7.2-dev
Merge commit 'e727ab7eecb212a954e249f3ef138eb6d3581e65' into 1.7
diff --git a/infra/config/global/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
index 9b6281b..0c95e98 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -56,9 +56,8 @@
acl_sets: "default"
gitiles: {
repo: "https://r8.googlesource.com/r8"
- refs: "regexp:refs/heads/d8.*"
- refs: "regexp:refs/heads/1.*"
- refs: "regexp:refs/heads/2.*"
+ # Version branches are named d8-x.y (up until d8-1.5) or just x.y (from 1.6)
+ refs: "regexp:refs/heads/(?:d8-)?[0-9]+\\.[0-9]+"
}
triggers: "archive_release"
triggers: "linux-android-4.0.4_release"
diff --git a/src/main/java/com/android/tools/r8/DesugaredLibraryConfigurationForTesting.java b/src/main/java/com/android/tools/r8/DesugaredLibraryConfigurationForTesting.java
index 4a07bf4..2f03241 100644
--- a/src/main/java/com/android/tools/r8/DesugaredLibraryConfigurationForTesting.java
+++ b/src/main/java/com/android/tools/r8/DesugaredLibraryConfigurationForTesting.java
@@ -126,6 +126,7 @@
private static Map<String, String> buildPrefixRewritingForCoreLibCompilationAndroidNPlus() {
return ImmutableMap.<String, String>builder()
+ .put("j$.time.", "java.time.")
.put("java.time.", "j$.time.")
.put("java.util.Desugar", "j$.util.Desugar")
.build();
diff --git a/src/main/java/com/android/tools/r8/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
new file mode 100644
index 0000000..2c63a69
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
@@ -0,0 +1,335 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8;
+
+import com.android.tools.r8.cf.code.CfConstNull;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfThrow;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.dex.Marker.Tool;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexLibraryClass;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.graph.ParameterAnnotationsList;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
+import com.android.tools.r8.jar.CfApplicationWriter;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.Sets;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.function.BiPredicate;
+import java.util.function.Predicate;
+
+public class GenerateLintFiles {
+
+ private static final String ANDROID_JAR_PATTERN = "third_party/android_jar/lib-v%d/android.jar";
+
+ private final DexItemFactory factory = new DexItemFactory();
+ private final Reporter reporter = new Reporter();
+ private final InternalOptions options = new InternalOptions(factory, reporter);
+
+ private final DesugaredLibraryConfiguration desugaredLibraryConfiguration;
+ private final String outputDirectory;
+
+ private final Set<DexMethod> parallelMethods = Sets.newIdentityHashSet();
+
+ private GenerateLintFiles(String desugarConfigurationPath, String outputDirectory) {
+ this.desugaredLibraryConfiguration =
+ readDesugaredLibraryConfiguration(desugarConfigurationPath);
+ this.outputDirectory =
+ outputDirectory.endsWith("/") ? outputDirectory : outputDirectory + File.separator;
+
+ DexType streamType = factory.createType(factory.createString("Ljava/util/stream/Stream;"));
+ DexMethod parallelMethod =
+ factory.createMethod(
+ factory.collectionType,
+ factory.createProto(streamType),
+ factory.createString("parallelStream"));
+ parallelMethods.add(parallelMethod);
+ for (String typePrefix : new String[] {"Base", "Double", "Int", "Long"}) {
+ streamType =
+ factory.createType(factory.createString("Ljava/util/stream/" + typePrefix + "Stream;"));
+ parallelMethod =
+ factory.createMethod(
+ streamType, factory.createProto(streamType), factory.createString("parallel"));
+ parallelMethods.add(parallelMethod);
+ }
+ }
+
+ private static Path getAndroidJarPath(AndroidApiLevel apiLevel) {
+ String jar = String.format(ANDROID_JAR_PATTERN, apiLevel.getLevel());
+ return Paths.get(jar);
+ }
+
+ private DesugaredLibraryConfiguration readDesugaredLibraryConfiguration(
+ String desugarConfigurationPath) {
+ return new DesugaredLibraryConfigurationParser(
+ factory, reporter, false, AndroidApiLevel.B.getLevel())
+ .parse(StringResource.fromFile(Paths.get(desugarConfigurationPath)));
+ }
+
+ private CfCode buildEmptyThrowingCfCode(DexMethod method) {
+ CfInstruction insn[] = {new CfConstNull(), new CfThrow()};
+ return new CfCode(
+ method.holder,
+ 1,
+ method.proto.parameters.size() + 1,
+ Arrays.asList(insn),
+ Collections.emptyList(),
+ Collections.emptyList());
+ }
+
+ private void addMethodsToHeaderJar(
+ DexApplication.Builder builder, DexClass clazz, List<DexEncodedMethod> methods) {
+ if (methods.size() == 0) {
+ return;
+ }
+
+ List<DexEncodedMethod> directMethods = new ArrayList<>();
+ List<DexEncodedMethod> virtualMethods = new ArrayList<>();
+ for (DexEncodedMethod method : methods) {
+ assert method.method.holder == clazz.type;
+ CfCode code = null;
+ if (!method.accessFlags.isAbstract() /*&& !method.accessFlags.isNative()*/) {
+ code = buildEmptyThrowingCfCode(method.method);
+ }
+ DexEncodedMethod throwingMethod =
+ new DexEncodedMethod(
+ method.method,
+ method.accessFlags,
+ DexAnnotationSet.empty(),
+ ParameterAnnotationsList.empty(),
+ code,
+ 50);
+ if (method.accessFlags.isStatic()) {
+ directMethods.add(throwingMethod);
+ } else {
+ virtualMethods.add(throwingMethod);
+ }
+ }
+
+ DexEncodedMethod[] directMethodsArray = new DexEncodedMethod[directMethods.size()];
+ DexEncodedMethod[] virtualMethodsArray = new DexEncodedMethod[virtualMethods.size()];
+ directMethods.toArray(directMethodsArray);
+ virtualMethods.toArray(virtualMethodsArray);
+ builder.addProgramClass(
+ new DexProgramClass(
+ clazz.type,
+ null,
+ Origin.unknown(),
+ clazz.accessFlags,
+ clazz.superType,
+ clazz.interfaces,
+ null,
+ null,
+ Collections.emptyList(),
+ null,
+ Collections.emptyList(),
+ DexAnnotationSet.empty(),
+ DexEncodedField.EMPTY_ARRAY,
+ DexEncodedField.EMPTY_ARRAY,
+ directMethodsArray,
+ virtualMethodsArray,
+ false));
+ }
+
+ private Map<DexClass, List<DexEncodedMethod>> collectSupportedMethods(
+ AndroidApiLevel compilationApiLevel, Predicate<DexEncodedMethod> supported)
+ throws IOException, ExecutionException {
+
+ // Read the android.jar for the compilation API level.
+ AndroidApp library =
+ AndroidApp.builder().addLibraryFiles(getAndroidJarPath(compilationApiLevel)).build();
+ DirectMappedDexApplication dexApplication =
+ new ApplicationReader(library, options, new Timing()).read().toDirect();
+
+ // collect all the methods that the library desugar configuration adds support for.
+ Map<DexClass, List<DexEncodedMethod>> supportedMethods = new LinkedHashMap<>();
+ for (DexLibraryClass clazz : dexApplication.libraryClasses()) {
+ String className = clazz.toSourceString();
+ // All the methods with the rewritten prefix are supported.
+ for (String prefix : desugaredLibraryConfiguration.getRewritePrefix().keySet()) {
+ if (clazz.accessFlags.isPublic() && className.startsWith(prefix)) {
+ for (DexEncodedMethod method : clazz.methods()) {
+ if (supported.test(method)) {
+ supportedMethods.computeIfAbsent(clazz, k -> new ArrayList<>()).add(method);
+ }
+ }
+ }
+ }
+
+ // All retargeted methods are supported.
+ for (DexEncodedMethod method : clazz.methods()) {
+ if (desugaredLibraryConfiguration
+ .getRetargetCoreLibMember()
+ .keySet()
+ .contains(method.method.name)) {
+ if (desugaredLibraryConfiguration
+ .getRetargetCoreLibMember()
+ .get(method.method.name)
+ .containsKey(clazz.type)) {
+ if (supported.test(method)) {
+ supportedMethods.computeIfAbsent(clazz, k -> new ArrayList<>()).add(method);
+ }
+ }
+ }
+ }
+ // All emulated interfaces methods are supported.
+ if (desugaredLibraryConfiguration.getEmulateLibraryInterface().containsKey(clazz.type)) {
+ for (DexEncodedMethod method : clazz.methods()) {
+ if (supported.test(method)) {
+ supportedMethods.computeIfAbsent(clazz, k -> new ArrayList<>()).add(method);
+ }
+ }
+ }
+ }
+
+ return supportedMethods;
+ }
+
+ private String lintBaseFileName(
+ AndroidApiLevel compilationApiLevel, AndroidApiLevel minApiLevel) {
+ return "desugared_apis_" + compilationApiLevel.getLevel() + "_" + minApiLevel.getLevel();
+ }
+
+ private Path lintFile(
+ AndroidApiLevel compilationApiLevel, AndroidApiLevel minApiLevel, String extension)
+ throws Exception {
+ Path directory =
+ Paths.get(outputDirectory + "compile_api_level_" + compilationApiLevel.getLevel());
+ Files.createDirectories(directory);
+ return Paths.get(
+ directory
+ + File.separator
+ + lintBaseFileName(compilationApiLevel, minApiLevel)
+ + extension);
+ }
+
+ private void writeLintFiles(
+ AndroidApiLevel compilationApiLevel,
+ AndroidApiLevel minApiLevel,
+ Map<DexClass, List<DexEncodedMethod>> supportedMethods)
+ throws Exception {
+ // Build a plain text file with the desugared APIs.
+ List<String> desugaredApisSignatures = new ArrayList<>();
+
+ DexApplication.Builder builder = DexApplication.builder(options, new Timing());
+ supportedMethods.forEach(
+ (clazz, methods) -> {
+ String classBinaryName =
+ DescriptorUtils.getClassBinaryNameFromDescriptor(clazz.type.descriptor.toString());
+ for (DexEncodedMethod method : methods) {
+ desugaredApisSignatures.add(
+ classBinaryName
+ + '/'
+ + method.method.name
+ + method.method.proto.toDescriptorString());
+ }
+
+ addMethodsToHeaderJar(builder, clazz, methods);
+ });
+ DexApplication app = builder.build();
+
+ // Write a plain text file with the desugared APIs.
+ FileUtils.writeTextFile(
+ lintFile(compilationApiLevel, minApiLevel, ".txt"), desugaredApisSignatures);
+
+ // Write a header jar with the desugared APIs.
+ AppInfo appInfo = new AppInfo(app);
+ AppView<?> appView = AppView.createForD8(appInfo, options);
+ CfApplicationWriter writer =
+ new CfApplicationWriter(
+ builder.build(),
+ appView,
+ options,
+ options.getMarker(Tool.L8),
+ GraphLense.getIdentityLense(),
+ NamingLens.getIdentityLens(),
+ null);
+ ClassFileConsumer consumer =
+ new ClassFileConsumer.ArchiveConsumer(
+ lintFile(compilationApiLevel, minApiLevel, FileUtils.JAR_EXTENSION));
+ writer.write(consumer, ThreadUtils.getExecutorService(options));
+ consumer.finished(options.reporter);
+ }
+
+ private void generateLintFiles(
+ AndroidApiLevel compilationApiLevel,
+ Predicate<AndroidApiLevel> generateForThisMinApiLevel,
+ BiPredicate<AndroidApiLevel, DexEncodedMethod> supportedForMinApiLevel)
+ throws Exception {
+ for (AndroidApiLevel value : AndroidApiLevel.values()) {
+ if (!generateForThisMinApiLevel.test(value)) {
+ continue;
+ }
+
+ Map<DexClass, List<DexEncodedMethod>> supportedMethods =
+ collectSupportedMethods(
+ compilationApiLevel, (method -> supportedForMinApiLevel.test(value, method)));
+ writeLintFiles(compilationApiLevel, value, supportedMethods);
+ }
+ }
+
+ private void run() throws Exception {
+ // Run over all the API levels that the desugared library can be compiled with.
+ for (int apiLevel = AndroidApiLevel.Q.getLevel();
+ apiLevel >= desugaredLibraryConfiguration.getRequiredCompilationApiLevel().getLevel();
+ apiLevel--) {
+ System.out.println("Generating lint files for compile API " + apiLevel);
+ generateLintFiles(
+ AndroidApiLevel.getAndroidApiLevel(apiLevel),
+ minApiLevel -> minApiLevel == AndroidApiLevel.L || minApiLevel == AndroidApiLevel.B,
+ (minApiLevel, method) -> {
+ assert minApiLevel == AndroidApiLevel.L || minApiLevel == AndroidApiLevel.B;
+ if (minApiLevel == AndroidApiLevel.L) {
+ return true;
+ }
+ assert minApiLevel == AndroidApiLevel.B;
+ return !parallelMethods.contains(method.method);
+ });
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ if (args.length != 2) {
+ System.out.println("Usage: GenerateLineFiles <desuage configuration> <output directory>");
+ System.exit(1);
+ }
+ new GenerateLintFiles(args[0], args[1]).run();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java b/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
index 946ac89..94633a0 100644
--- a/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
@@ -113,7 +113,7 @@
.append(r8Type("InternalOptions", "utils"))
.append(" options, ")
.append(r8Type("DexMethod", "graph"))
- .append(" method, String name) {");
+ .append(" method) {");
for (CfInstruction instruction : code.getInstructions()) {
if (instruction instanceof CfLabel) {
@@ -166,7 +166,7 @@
}
private String longValue(long value) {
- return (value < Integer.MIN_VALUE || Integer.MAX_VALUE < value) ? (value + "l") : ("" + value);
+ return (value < Integer.MIN_VALUE || Integer.MAX_VALUE < value) ? (value + "L") : ("" + value);
}
// Ensure a type import for a given type.
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 7b18fd8..02a6129 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -309,17 +309,21 @@
mainDexFile = new VirtualFile(0, writer.namingLens);
assert virtualFiles.isEmpty();
virtualFiles.add(mainDexFile);
- if (writer.markerStrings != null && !writer.markerStrings.isEmpty()) {
- for (DexString markerString : writer.markerStrings) {
- mainDexFile.transaction.addString(markerString);
- }
- mainDexFile.commitTransaction();
- }
+ addMarkers(mainDexFile);
classes = Sets.newHashSet(application.classes());
originalNames = computeOriginalNameMapping(classes, application.getProguardMap());
}
+ private void addMarkers(VirtualFile virtualFile) {
+ if (writer.markerStrings != null && !writer.markerStrings.isEmpty()) {
+ for (DexString markerString : writer.markerStrings) {
+ virtualFile.transaction.addString(markerString);
+ }
+ virtualFile.commitTransaction();
+ }
+ }
+
protected void fillForMainDexList(Set<DexProgramClass> classes) {
if (!application.mainDexList.isEmpty()) {
VirtualFile mainDexFile = virtualFiles.get(0);
@@ -418,7 +422,10 @@
for (Map.Entry<FeatureSplit, Set<DexProgramClass>> featureSplitSetEntry :
featureSplitClasses.entrySet()) {
// Add a new virtual file, start from index 0 again
- virtualFiles.add(new VirtualFile(0, writer.namingLens, featureSplitSetEntry.getKey()));
+ VirtualFile featureFile =
+ new VirtualFile(0, writer.namingLens, featureSplitSetEntry.getKey());
+ virtualFiles.add(featureFile);
+ addMarkers(featureFile);
Set<DexProgramClass> featureClasses =
sortClassesByPackage(featureSplitSetEntry.getValue(), originalNames);
filesForDistribution = virtualFiles.subList(virtualFiles.size() - 1, virtualFiles.size());
diff --git a/src/main/java/com/android/tools/r8/features/FeatureSplitConfiguration.java b/src/main/java/com/android/tools/r8/features/FeatureSplitConfiguration.java
index b7f6158..22e819e 100644
--- a/src/main/java/com/android/tools/r8/features/FeatureSplitConfiguration.java
+++ b/src/main/java/com/android/tools/r8/features/FeatureSplitConfiguration.java
@@ -59,7 +59,12 @@
return result;
}
- public boolean inSameFeatureOrBase(DexMethod a, DexMethod b) {
+ public boolean isInFeature(DexProgramClass clazz) {
+ return javaTypeToFeatureSplitMapping.containsKey(
+ DescriptorUtils.descriptorToJavaType(clazz.type.toDescriptorString()));
+ }
+
+ public boolean inSameFeatureOrBase(DexMethod a, DexMethod b){
return inSameFeatureOrBase(a.holder, b.holder);
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 3e3ed2b..bc4f6f0 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -1098,6 +1098,10 @@
return false;
}
+ public TrivialClassInitializer asTrivialClassInitializer() {
+ return null;
+ }
+
// Defines instance trivial initialized, see details in comments
// to CodeRewriter::computeInstanceInitializerInfo(...)
public static final class TrivialInstanceInitializer extends TrivialInitializer {
@@ -1122,6 +1126,11 @@
public TrivialClassInitializer(DexField field) {
this.field = field;
}
+
+ @Override
+ public TrivialClassInitializer asTrivialClassInitializer() {
+ return this;
+ }
}
}
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 36e4cc8..87d7ceb 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -161,6 +161,8 @@
public final DexString toStringMethodName = createString("toString");
public final DexString internMethodName = createString("intern");
+ public final DexString convertMethodName = createString("convert");
+
public final DexString getClassMethodName = createString("getClass");
public final DexString finalizeMethodName = createString("finalize");
public final DexString ordinalMethodName = createString("ordinal");
@@ -214,6 +216,10 @@
public final DexString proxyDescriptor = createString("Ljava/lang/reflect/Proxy;");
public final DexString serviceLoaderDescriptor = createString("Ljava/util/ServiceLoader;");
public final DexString listDescriptor = createString("Ljava/util/List;");
+ public final DexString setDescriptor = createString("Ljava/util/Set;");
+ public final DexString mapDescriptor = createString("Ljava/util/Map;");
+ public final DexString mapEntryDescriptor = createString("Ljava/util/Map$Entry;");
+ public final DexString collectionDescriptor = createString("Ljava/util/Collection;");
public final DexString comparatorDescriptor = createString("Ljava/util/Comparator;");
public final DexString callableDescriptor = createString("Ljava/util/concurrent/Callable;");
public final DexString supplierDescriptor = createString("Ljava/util/function/Supplier;");
@@ -302,6 +308,10 @@
public final DexType proxyType = createType(proxyDescriptor);
public final DexType serviceLoaderType = createType(serviceLoaderDescriptor);
public final DexType listType = createType(listDescriptor);
+ public final DexType setType = createType(setDescriptor);
+ public final DexType mapType = createType(mapDescriptor);
+ public final DexType mapEntryType = createType(mapEntryDescriptor);
+ public final DexType collectionType = createType(collectionDescriptor);
public final DexType comparatorType = createType(comparatorDescriptor);
public final DexType callableType = createType(callableDescriptor);
public final DexType supplierType = createType(supplierDescriptor);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/AbstractFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/AbstractFieldSet.java
new file mode 100644
index 0000000..90320b7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/AbstractFieldSet.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.fieldvalueanalysis;
+
+import com.android.tools.r8.graph.DexEncodedField;
+
+/**
+ * Implements a lifted subset lattice for fields.
+ *
+ * <p>An element can either be:
+ *
+ * <ol>
+ * <li>bottom, represented by {@link EmptyFieldSet},
+ * <li>a (possibly empty) set of fields, represented by {@link ConcreteMutableFieldSet}, or
+ * <li>top, represented by {@link UnknownFieldSet}.
+ * </ol>
+ *
+ * <p>Note that this class currently does not contain a {@code join()} method. Instead, the {@link
+ * ConcreteMutableFieldSet} has an {@link ConcreteMutableFieldSet#add(DexEncodedField)} and {@link
+ * ConcreteMutableFieldSet#addAll(ConcreteMutableFieldSet)} method. This is intentional, since the
+ * use of {@code join()} could lead to an excessive amount of set copying under the hood.
+ */
+public abstract class AbstractFieldSet {
+
+ public boolean isConcreteFieldSet() {
+ return false;
+ }
+
+ public ConcreteMutableFieldSet asConcreteFieldSet() {
+ return null;
+ }
+
+ public boolean isKnownFieldSet() {
+ return false;
+ }
+
+ public KnownFieldSet asKnownFieldSet() {
+ return null;
+ }
+
+ public abstract boolean contains(DexEncodedField field);
+
+ public boolean isBottom() {
+ return false;
+ }
+
+ public boolean isTop() {
+ return false;
+ }
+
+ public final boolean lessThanOrEqual(AbstractFieldSet other) {
+ if (isBottom() || other.isTop()) {
+ return true;
+ }
+ if (isTop() || other.isBottom()) {
+ return false;
+ }
+ assert isConcreteFieldSet();
+ assert other.isConcreteFieldSet();
+ return other.asConcreteFieldSet().getFields().containsAll(asConcreteFieldSet().getFields());
+ }
+
+ public final boolean strictlyLessThan(AbstractFieldSet other) {
+ return lessThanOrEqual(other) && !equals(other);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
new file mode 100644
index 0000000..0ac6de7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
@@ -0,0 +1,87 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.fieldvalueanalysis;
+
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.SetUtils;
+import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.Set;
+
+public class ConcreteMutableFieldSet extends AbstractFieldSet implements KnownFieldSet {
+
+ private final Set<DexEncodedField> fields;
+
+ public ConcreteMutableFieldSet() {
+ fields = Sets.newIdentityHashSet();
+ }
+
+ public ConcreteMutableFieldSet(DexEncodedField field) {
+ fields = SetUtils.newIdentityHashSet(field);
+ }
+
+ public void add(DexEncodedField field) {
+ fields.add(field);
+ }
+
+ public void addAll(ConcreteMutableFieldSet other) {
+ fields.addAll(other.fields);
+ }
+
+ Set<DexEncodedField> getFields() {
+ if (InternalOptions.assertionsEnabled()) {
+ return Collections.unmodifiableSet(fields);
+ }
+ return fields;
+ }
+
+ @Override
+ public boolean isConcreteFieldSet() {
+ return true;
+ }
+
+ @Override
+ public ConcreteMutableFieldSet asConcreteFieldSet() {
+ return this;
+ }
+
+ @Override
+ public boolean isKnownFieldSet() {
+ return true;
+ }
+
+ @Override
+ public ConcreteMutableFieldSet asKnownFieldSet() {
+ return this;
+ }
+
+ @Override
+ public boolean contains(DexEncodedField field) {
+ return fields.contains(field);
+ }
+
+ @Override
+ public int size() {
+ return fields.size();
+ }
+
+ @Override
+ public int hashCode() {
+ return fields.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == null) {
+ return false;
+ }
+ if (other.getClass() != getClass()) {
+ return false;
+ }
+ ConcreteMutableFieldSet concreteFieldSet = (ConcreteMutableFieldSet) other;
+ return fields.equals(concreteFieldSet.fields);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/EmptyFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/EmptyFieldSet.java
new file mode 100644
index 0000000..c50ded8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/EmptyFieldSet.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.fieldvalueanalysis;
+
+import com.android.tools.r8.graph.DexEncodedField;
+
+public class EmptyFieldSet extends AbstractFieldSet implements KnownFieldSet {
+
+ private static final EmptyFieldSet INSTANCE = new EmptyFieldSet();
+
+ private EmptyFieldSet() {}
+
+ public static EmptyFieldSet getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public boolean isKnownFieldSet() {
+ return true;
+ }
+
+ @Override
+ public EmptyFieldSet asKnownFieldSet() {
+ return this;
+ }
+
+ @Override
+ public boolean contains(DexEncodedField field) {
+ return false;
+ }
+
+ @Override
+ public boolean isBottom() {
+ return true;
+ }
+
+ @Override
+ public int size() {
+ return 0;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
new file mode 100644
index 0000000..2d67908
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
@@ -0,0 +1,253 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.fieldvalueanalysis;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.DominatorTree;
+import com.android.tools.r8.ir.code.DominatorTree.Assumption;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.StaticPut;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.DequeUtils;
+import java.util.Deque;
+import java.util.IdentityHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class FieldValueAnalysis {
+
+ private final AppView<AppInfoWithLiveness> appView;
+ private final IRCode code;
+ private final OptimizationFeedback feedback;
+ private final DexEncodedMethod method;
+
+ private Map<BasicBlock, AbstractFieldSet> fieldsMaybeReadBeforeBlockInclusiveCache;
+
+ private FieldValueAnalysis(
+ AppView<AppInfoWithLiveness> appView,
+ IRCode code,
+ OptimizationFeedback feedback,
+ DexEncodedMethod method) {
+ this.appView = appView;
+ this.code = code;
+ this.feedback = feedback;
+ this.method = method;
+ }
+
+ public static void run(
+ AppView<?> appView, IRCode code, OptimizationFeedback feedback, DexEncodedMethod method) {
+ if (appView.enableWholeProgramOptimizations() && method.isClassInitializer()) {
+ assert appView.appInfo().hasLiveness();
+ new FieldValueAnalysis(appView.withLiveness(), code, feedback, method)
+ .computeFieldOptimizationInfo();
+ }
+ }
+
+ private Map<BasicBlock, AbstractFieldSet> getOrCreateFieldsMaybeReadBeforeBlockInclusive() {
+ if (fieldsMaybeReadBeforeBlockInclusiveCache == null) {
+ fieldsMaybeReadBeforeBlockInclusiveCache = createFieldsMaybeReadBeforeBlockInclusive();
+ }
+ return fieldsMaybeReadBeforeBlockInclusiveCache;
+ }
+
+ /** This method analyzes initializers with the purpose of computing field optimization info. */
+ private void computeFieldOptimizationInfo() {
+ AppInfoWithLiveness appInfo = appView.appInfo();
+ DominatorTree dominatorTree = null;
+
+ if (method.isClassInitializer()) {
+ DexType context = method.method.holder;
+
+ // Find all the static-put instructions that assign a field in the enclosing class which is
+ // guaranteed to be assigned only in the current initializer.
+ boolean isStraightLineCode = true;
+ Map<DexEncodedField, LinkedList<StaticPut>> staticPutsPerField = new IdentityHashMap<>();
+ for (Instruction instruction : code.instructions()) {
+ if (instruction.isStaticPut()) {
+ StaticPut staticPut = instruction.asStaticPut();
+ DexField field = staticPut.getField();
+ DexEncodedField encodedField = appInfo.resolveField(field);
+ if (encodedField != null
+ && encodedField.field.holder == context
+ && appInfo.isStaticFieldWrittenOnlyInEnclosingStaticInitializer(encodedField)) {
+ staticPutsPerField
+ .computeIfAbsent(encodedField, ignore -> new LinkedList<>())
+ .add(staticPut);
+ }
+ }
+ if (instruction.isJumpInstruction()) {
+ if (!instruction.isGoto() && !instruction.isReturn()) {
+ isStraightLineCode = false;
+ }
+ }
+ }
+
+ List<BasicBlock> normalExitBlocks = code.computeNormalExitBlocks();
+ for (Entry<DexEncodedField, LinkedList<StaticPut>> entry : staticPutsPerField.entrySet()) {
+ DexEncodedField encodedField = entry.getKey();
+ LinkedList<StaticPut> staticPuts = entry.getValue();
+ if (staticPuts.size() > 1) {
+ continue;
+ }
+ StaticPut staticPut = staticPuts.getFirst();
+ if (!isStraightLineCode) {
+ if (dominatorTree == null) {
+ dominatorTree = new DominatorTree(code, Assumption.NO_UNREACHABLE_BLOCKS);
+ }
+ if (!dominatorTree.dominatesAllOf(staticPut.getBlock(), normalExitBlocks)) {
+ continue;
+ }
+ }
+ if (fieldMaybeReadBeforeInstruction(encodedField, staticPut)) {
+ continue;
+ }
+ updateFieldOptimizationInfo(encodedField, staticPut.value());
+ }
+ }
+ }
+
+ private boolean fieldMaybeReadBeforeInstruction(
+ DexEncodedField encodedField, Instruction instruction) {
+ BasicBlock block = instruction.getBlock();
+
+ // First check if the field may be read in any of the (transitive) predecessor blocks.
+ if (fieldMaybeReadBeforeBlock(encodedField, block)) {
+ return true;
+ }
+
+ // Then check if any of the instructions that precede the given instruction in the current block
+ // may read the field.
+ DexType context = method.method.holder;
+ InstructionIterator instructionIterator = block.iterator();
+ while (instructionIterator.hasNext()) {
+ Instruction current = instructionIterator.next();
+ if (current == instruction) {
+ break;
+ }
+ if (current.readSet(appView, context).contains(encodedField)) {
+ return true;
+ }
+ }
+
+ // Otherwise, the field is not read prior to the given instruction.
+ return false;
+ }
+
+ private boolean fieldMaybeReadBeforeBlock(DexEncodedField encodedField, BasicBlock block) {
+ for (BasicBlock predecessor : block.getPredecessors()) {
+ if (fieldMaybeReadBeforeBlockInclusive(encodedField, predecessor)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean fieldMaybeReadBeforeBlockInclusive(
+ DexEncodedField encodedField, BasicBlock block) {
+ return getOrCreateFieldsMaybeReadBeforeBlockInclusive().get(block).contains(encodedField);
+ }
+
+ /**
+ * Eagerly creates a mapping from each block to the set of fields that may be read in that block
+ * and its transitive predecessors.
+ */
+ private Map<BasicBlock, AbstractFieldSet> createFieldsMaybeReadBeforeBlockInclusive() {
+ DexType context = method.method.holder;
+ Map<BasicBlock, AbstractFieldSet> result = new IdentityHashMap<>();
+ Deque<BasicBlock> worklist = DequeUtils.newArrayDeque(code.entryBlock());
+ while (!worklist.isEmpty()) {
+ BasicBlock block = worklist.removeFirst();
+ boolean seenBefore = result.containsKey(block);
+ AbstractFieldSet readSet =
+ result.computeIfAbsent(block, ignore -> EmptyFieldSet.getInstance());
+ if (readSet.isTop()) {
+ // We already have unknown information for this block.
+ continue;
+ }
+
+ assert readSet.isKnownFieldSet();
+ KnownFieldSet knownReadSet = readSet.asKnownFieldSet();
+ int oldSize = seenBefore ? knownReadSet.size() : -1;
+
+ // Everything that is read in the predecessor blocks should also be included in the read set
+ // for the current block, so here we join the information from the predecessor blocks into the
+ // current read set.
+ boolean blockOrPredecessorMaybeReadAnyField = false;
+ for (BasicBlock predecessor : block.getPredecessors()) {
+ AbstractFieldSet predecessorReadSet =
+ result.getOrDefault(predecessor, EmptyFieldSet.getInstance());
+ if (predecessorReadSet.isBottom()) {
+ continue;
+ }
+ if (predecessorReadSet.isTop()) {
+ blockOrPredecessorMaybeReadAnyField = true;
+ break;
+ }
+ assert predecessorReadSet.isConcreteFieldSet();
+ if (!knownReadSet.isConcreteFieldSet()) {
+ knownReadSet = new ConcreteMutableFieldSet();
+ }
+ knownReadSet.asConcreteFieldSet().addAll(predecessorReadSet.asConcreteFieldSet());
+ }
+
+ if (!blockOrPredecessorMaybeReadAnyField) {
+ // Finally, we update the read set with the fields that are read by the instructions in the
+ // current block.
+ for (Instruction instruction : block.getInstructions()) {
+ AbstractFieldSet instructionReadSet = instruction.readSet(appView, context);
+ if (instructionReadSet.isBottom()) {
+ continue;
+ }
+ if (instructionReadSet.isTop()) {
+ blockOrPredecessorMaybeReadAnyField = true;
+ break;
+ }
+ if (!knownReadSet.isConcreteFieldSet()) {
+ knownReadSet = new ConcreteMutableFieldSet();
+ }
+ knownReadSet.asConcreteFieldSet().addAll(instructionReadSet.asConcreteFieldSet());
+ }
+ }
+
+ boolean changed = false;
+ if (blockOrPredecessorMaybeReadAnyField) {
+ // Record that this block reads all fields.
+ result.put(block, UnknownFieldSet.getInstance());
+ changed = true;
+ } else if (knownReadSet.size() != oldSize) {
+ assert knownReadSet.size() > oldSize;
+ changed = true;
+ }
+
+ if (changed) {
+ // Rerun the analysis for all successors because the state of the current block changed.
+ worklist.addAll(block.getSuccessors());
+ }
+ }
+ return result;
+ }
+
+ private void updateFieldOptimizationInfo(DexEncodedField field, Value value) {
+ TypeLatticeElement fieldType =
+ TypeLatticeElement.fromDexType(field.field.type, Nullability.maybeNull(), appView);
+ TypeLatticeElement valueType = value.getTypeLattice();
+ if (valueType.strictlyLessThan(fieldType, appView)) {
+ feedback.markFieldHasDynamicType(field, valueType);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/KnownFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/KnownFieldSet.java
new file mode 100644
index 0000000..b96918e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/KnownFieldSet.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.fieldvalueanalysis;
+
+public interface KnownFieldSet {
+
+ default boolean isConcreteFieldSet() {
+ return false;
+ }
+
+ default ConcreteMutableFieldSet asConcreteFieldSet() {
+ return null;
+ }
+
+ int size();
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/UnknownFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/UnknownFieldSet.java
new file mode 100644
index 0000000..9df87cc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/UnknownFieldSet.java
@@ -0,0 +1,28 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.fieldvalueanalysis;
+
+import com.android.tools.r8.graph.DexEncodedField;
+
+public class UnknownFieldSet extends AbstractFieldSet {
+
+ private static final UnknownFieldSet INSTANCE = new UnknownFieldSet();
+
+ private UnknownFieldSet() {}
+
+ public static UnknownFieldSet getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public boolean contains(DexEncodedField field) {
+ return true;
+ }
+
+ @Override
+ public boolean isTop() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/modeling/LibraryMethodReadSetModeling.java b/src/main/java/com/android/tools/r8/ir/analysis/modeling/LibraryMethodReadSetModeling.java
new file mode 100644
index 0000000..57f8d2e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/modeling/LibraryMethodReadSetModeling.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.modeling;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.EmptyFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.UnknownFieldSet;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import java.util.function.Predicate;
+
+/** Models if a given library method may cause a program field to be read. */
+public class LibraryMethodReadSetModeling {
+
+ public static AbstractFieldSet getModeledReadSetOrUnknown(
+ InvokeMethod invoke, DexItemFactory dexItemFactory) {
+ DexMethod invokedMethod = invoke.getInvokedMethod();
+
+ // Check if it is a library method that does not have side effects. In that case it is safe to
+ // assume that the method does not read any fields, since even if it did, it would not be able
+ // to do anything with the values it read (since we will remove such invocations without side
+ // effects).
+ Predicate<InvokeMethod> noSideEffectsPredicate =
+ dexItemFactory.libraryMethodsWithoutSideEffects.get(invokedMethod);
+ if (noSideEffectsPredicate != null && noSideEffectsPredicate.test(invoke)) {
+ return EmptyFieldSet.getInstance();
+ }
+
+ // Already handled above.
+ assert !dexItemFactory.classMethods.isReflectiveNameLookup(invokedMethod);
+
+ // Modeling of other library methods.
+ DexType holder = invokedMethod.holder;
+ if (holder == dexItemFactory.objectType) {
+ if (invokedMethod == dexItemFactory.objectMethods.constructor) {
+ return EmptyFieldSet.getInstance();
+ }
+ }
+ return UnknownFieldSet.getInstance();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Assume.java b/src/main/java/com/android/tools/r8/ir/code/Assume.java
index c4dfbc4..18ba962 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Assume.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Assume.java
@@ -153,6 +153,10 @@
return self;
}
+ public boolean mayAffectStaticType() {
+ return isAssumeNonNull();
+ }
+
@Override
public boolean couldIntroduceAnAlias(AppView<?> appView, Value root) {
assert root != null && root.getTypeLattice().isReference();
diff --git a/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java b/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java
index 72c64a0..120a2d4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java
@@ -95,9 +95,9 @@
/**
* Check if one basic block is dominated by another basic block.
*
- * @param subject subject to check for domination by {@code dominator}
+ * @param subject subject to check for domination by {@param dominator}
* @param dominator dominator to check against
- * @return wether {@code subject} is dominated by {@code dominator}
+ * @return whether {@param subject} is dominated by {@param dominator}
*/
public boolean dominatedBy(BasicBlock subject, BasicBlock dominator) {
assert !obsolete;
@@ -108,11 +108,27 @@
}
/**
+ * Checks if one basic block dominates a collection of other basic blocks.
+ *
+ * @param dominator dominator to check against
+ * @param subjects subjects to check for domination by {@param dominator}
+ * @return whether {@param subjects} are all dominated by {@param dominator}
+ */
+ public boolean dominatesAllOf(BasicBlock dominator, Iterable<BasicBlock> subjects) {
+ for (BasicBlock subject : subjects) {
+ if (!dominatedBy(subject, dominator)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* Check if one basic block is strictly dominated by another basic block.
*
- * @param subject subject to check for domination by {@code dominator}
+ * @param subject subject to check for domination by {@param dominator}
* @param dominator dominator to check against
- * @return wether {@code subject} is strictly dominated by {@code dominator}
+ * @return whether {@param subject} is strictly dominated by {@param dominator}
*/
public boolean strictlyDominatedBy(BasicBlock subject, BasicBlock dominator) {
assert !obsolete;
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
index e041d5a..b95376e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
@@ -11,6 +11,10 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.AbstractError;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.ConcreteMutableFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.EmptyFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.UnknownFieldSet;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.Collections;
import java.util.List;
@@ -131,4 +135,32 @@
// TODO(jsjeon): what if the target field is known to be non-null?
return true;
}
+
+ @Override
+ public AbstractFieldSet readSet(AppView<?> appView, DexType context) {
+ if (instructionMayTriggerMethodInvocation(appView, context)) {
+ // This may trigger class initialization, which could potentially read any field.
+ return UnknownFieldSet.getInstance();
+ }
+
+ if (isFieldGet()) {
+ DexField field = getField();
+ DexEncodedField encodedField = null;
+ if (appView.enableWholeProgramOptimizations()) {
+ encodedField = appView.appInfo().resolveField(field);
+ } else {
+ DexClass clazz = appView.definitionFor(field.holder);
+ if (clazz != null) {
+ encodedField = clazz.lookupField(field);
+ }
+ }
+ if (encodedField != null) {
+ return new ConcreteMutableFieldSet(encodedField);
+ }
+ return UnknownFieldSet.getInstance();
+ }
+
+ assert isFieldPut();
+ return EmptyFieldSet.getInstance();
+ }
}
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 b924c4a..cb32015 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
@@ -262,7 +262,6 @@
}
public boolean controlFlowMayDependOnEnvironment(AppView<?> appView) {
- DexType context = method.method.holder;
for (BasicBlock block : blocks) {
if (block.hasCatchHandlers()) {
// Whether an instruction throws may generally depend on the environment.
@@ -1009,14 +1008,22 @@
}
public void removeAllTrivialPhis() {
- removeAllTrivialPhis(null);
+ removeAllTrivialPhis(null, null);
}
public void removeAllTrivialPhis(IRBuilder builder) {
+ removeAllTrivialPhis(builder, null);
+ }
+
+ public void removeAllTrivialPhis(Set<Value> affectedValues) {
+ removeAllTrivialPhis(null, affectedValues);
+ }
+
+ public void removeAllTrivialPhis(IRBuilder builder, Set<Value> affectedValues) {
for (BasicBlock block : blocks) {
List<Phi> phis = new ArrayList<>(block.getPhis());
for (Phi phi : phis) {
- phi.removeTrivialPhi(builder);
+ phi.removeTrivialPhi(builder, affectedValues);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 03421cc..1a17a8b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -17,6 +17,9 @@
import com.android.tools.r8.ir.analysis.constant.Bottom;
import com.android.tools.r8.ir.analysis.constant.ConstRangeLatticeElement;
import com.android.tools.r8.ir.analysis.constant.LatticeElement;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.EmptyFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.UnknownFieldSet;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.code.Assume.DynamicTypeAssumption;
import com.android.tools.r8.ir.code.Assume.NoAssumption;
@@ -540,6 +543,18 @@
}
/**
+ * Returns an abstraction of the set of fields that may possibly be read as a result of executing
+ * this instruction.
+ */
+ public AbstractFieldSet readSet(AppView<?> appView, DexType context) {
+ if (instructionMayTriggerMethodInvocation(appView, context)
+ && instructionMayHaveSideEffects(appView, context)) {
+ return UnknownFieldSet.getInstance();
+ }
+ return EmptyFieldSet.getInstance();
+ }
+
+ /**
* Returns true if this instruction need this value in a register.
*/
public boolean needsValueInRegister(Value value) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index 9381fd1..48baef4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -12,11 +12,15 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.EmptyFieldSet;
+import com.android.tools.r8.ir.analysis.modeling.LibraryMethodReadSetModeling;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -277,4 +281,22 @@
return true;
}
+
+ @Override
+ public AbstractFieldSet readSet(AppView<?> appView, DexType context) {
+ DexMethod invokedMethod = getInvokedMethod();
+
+ // Trivial instance initializers do not read any fields.
+ if (appView.dexItemFactory().isConstructor(invokedMethod)) {
+ DexEncodedMethod singleTarget = lookupSingleTarget(appView, context);
+ if (singleTarget != null) {
+ TrivialInitializer info = singleTarget.getOptimizationInfo().getTrivialInitializerInfo();
+ if (info != null && info.isTrivialInstanceInitializer()) {
+ return EmptyFieldSet.getInstance();
+ }
+ }
+ }
+
+ return LibraryMethodReadSetModeling.getModeledReadSetOrUnknown(this, appView.dexItemFactory());
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index c3db7ea..178da3f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -11,6 +11,8 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
+import com.android.tools.r8.ir.analysis.modeling.LibraryMethodReadSetModeling;
import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
import com.android.tools.r8.ir.optimize.InliningOracle;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
@@ -119,4 +121,9 @@
public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
return true;
}
+
+ @Override
+ public AbstractFieldSet readSet(AppView<?> appView, DexType context) {
+ return LibraryMethodReadSetModeling.getModeledReadSetOrUnknown(this, appView.dexItemFactory());
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Phi.java b/src/main/java/com/android/tools/r8/ir/code/Phi.java
index e2f9cf3..7cd8ca2 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Phi.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Phi.java
@@ -123,7 +123,7 @@
builder.constrainType(operand, readConstraint);
appendOperand(operand);
}
- removeTrivialPhi(builder);
+ removeTrivialPhi(builder, null);
}
public void addOperands(List<Value> operands) {
@@ -224,10 +224,10 @@
}
public void removeTrivialPhi() {
- removeTrivialPhi(null);
+ removeTrivialPhi(null, null);
}
- public void removeTrivialPhi(IRBuilder builder) {
+ void removeTrivialPhi(IRBuilder builder, Set<Value> affectedValues) {
Value same = null;
for (Value op : operands) {
if (op == same || op == this) {
@@ -252,6 +252,9 @@
if (builder != null && typeLattice.isPreciseType() && !typeLattice.isBottom()) {
builder.constrainType(same, ValueTypeConstraint.fromTypeLattice(typeLattice));
}
+ if (affectedValues != null) {
+ affectedValues.addAll(this.affectedValues());
+ }
// Removing this phi, so get rid of it as a phi user from all of the operands to avoid
// recursively getting back here with the same phi. If the phi has itself as an operand
// that also removes the self-reference.
@@ -277,7 +280,7 @@
replaceUsers(same);
// Try to simplify phi users that might now have become trivial.
for (Phi user : phiUsersToSimplify) {
- user.removeTrivialPhi(builder);
+ user.removeTrivialPhi(builder, affectedValues);
}
}
// Get rid of the phi itself.
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index 2881de7..6648777 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.position.MethodPosition;
import com.android.tools.r8.utils.LongInterval;
import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableSet;
@@ -424,6 +425,22 @@
return users.getFirst();
}
+ public Set<Instruction> aliasedUsers() {
+ Set<Instruction> users = SetUtils.newIdentityHashSet(uniqueUsers());
+ collectAliasedUsersViaAssume(uniqueUsers(), users);
+ return users;
+ }
+
+ private static void collectAliasedUsersViaAssume(
+ Set<Instruction> usersToTest, Set<Instruction> collectedUsers) {
+ for (Instruction user : usersToTest) {
+ if (user.isAssume()) {
+ collectedUsers.addAll(user.outValue().uniqueUsers());
+ collectAliasedUsersViaAssume(user.outValue().uniqueUsers(), collectedUsers);
+ }
+ }
+ }
+
public Phi firstPhiUser() {
assert !phiUsers.isEmpty();
return phiUsers.getFirst();
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 e370192..a75ce9e 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
@@ -32,6 +32,7 @@
import com.android.tools.r8.ir.analysis.TypeChecker;
import com.android.tools.r8.ir.analysis.constant.SparseConditionalConstantPropagation;
import com.android.tools.r8.ir.analysis.fieldaccess.FieldBitAccessAnalysis;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.FieldValueAnalysis;
import com.android.tools.r8.ir.analysis.sideeffect.ClassInitializerSideEffectAnalysis;
import com.android.tools.r8.ir.analysis.sideeffect.ClassInitializerSideEffectAnalysis.ClassInitializerSideEffect;
import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement;
@@ -1110,8 +1111,7 @@
}
if (memberValuePropagation != null) {
- memberValuePropagation.rewriteWithConstantValues(
- code, method.method.holder, isProcessedConcurrently);
+ memberValuePropagation.rewriteWithConstantValues(code, method.method.holder);
}
if (options.enableEnumValueOptimization) {
assert appView.enableWholeProgramOptimizations();
@@ -1217,7 +1217,7 @@
codeRewriter.rewriteThrowNullPointerException(code);
if (classInitializerDefaultsOptimization != null && !isDebugMode) {
- classInitializerDefaultsOptimization.optimize(method, code, feedback);
+ classInitializerDefaultsOptimization.optimize(method, code);
}
if (Log.ENABLED) {
@@ -1249,26 +1249,6 @@
assert code.verifyTypes(appView);
- if (nonNullTracker != null) {
- // TODO(b/139246447): Once we extend this optimization to, e.g., constants of primitive args,
- // this may not be the right place to collect call site optimization info.
- // Collecting call-site optimization info depends on the existence of non-null IRs.
- // Arguments can be changed during the debug mode.
- if (!isDebugMode && appView.callSiteOptimizationInfoPropagator() != null) {
- appView.callSiteOptimizationInfoPropagator().collectCallSiteOptimizationInfo(code);
- }
- // Computation of non-null parameters on normal exits rely on the existence of non-null IRs.
- nonNullTracker.computeNonNullParamOnNormalExits(feedback, code);
- }
- if (aliasIntroducer != null || nonNullTracker != null || dynamicTypeOptimization != null) {
- codeRewriter.removeAssumeInstructions(code);
- assert code.isConsistentSSA();
- }
- // Assert that we do not have unremoved non-sense code in the output, e.g., v <- non-null NULL.
- assert code.verifyNoNullabilityBottomTypes();
-
- assert code.verifyTypes(appView);
-
previous = printMethod(code, "IR before class inlining (SSA)", previous);
if (classInliner != null) {
@@ -1337,6 +1317,26 @@
assert code.isConsistentSSA();
}
+ if (nonNullTracker != null) {
+ // TODO(b/139246447): Once we extend this optimization to, e.g., constants of primitive args,
+ // this may not be the right place to collect call site optimization info.
+ // Collecting call-site optimization info depends on the existence of non-null IRs.
+ // Arguments can be changed during the debug mode.
+ if (!isDebugMode && appView.callSiteOptimizationInfoPropagator() != null) {
+ appView.callSiteOptimizationInfoPropagator().collectCallSiteOptimizationInfo(code);
+ }
+ // Computation of non-null parameters on normal exits rely on the existence of non-null IRs.
+ nonNullTracker.computeNonNullParamOnNormalExits(feedback, code);
+ }
+ if (aliasIntroducer != null || nonNullTracker != null || dynamicTypeOptimization != null) {
+ codeRewriter.removeAssumeInstructions(code);
+ assert code.isConsistentSSA();
+ }
+ // Assert that we do not have unremoved non-sense code in the output, e.g., v <- non-null NULL.
+ assert code.verifyNoNullabilityBottomTypes();
+
+ assert code.verifyTypes(appView);
+
previous = printMethod(code, "IR after outline handler (SSA)", previous);
// TODO(mkroghj) Test if shorten live ranges is worth it.
@@ -1384,6 +1384,7 @@
}
computeDynamicReturnType(feedback, method, code);
+ FieldValueAnalysis.run(appView, code, feedback, method);
computeInitializedClassesOnNormalExit(feedback, method, code);
computeMayHaveSideEffects(feedback, method, code);
computeReturnValueOnlyDependsOnArguments(feedback, method, code);
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 0e13782..b796600 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
@@ -31,9 +31,9 @@
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.desugar.backports.BackportedMethods;
import com.android.tools.r8.ir.desugar.backports.BooleanMethodRewrites;
+import com.android.tools.r8.ir.desugar.backports.CollectionMethodGenerators;
+import com.android.tools.r8.ir.desugar.backports.CollectionMethodRewrites;
import com.android.tools.r8.ir.desugar.backports.FloatMethodRewrites;
-import com.android.tools.r8.ir.desugar.backports.ListMethodGenerators;
-import com.android.tools.r8.ir.desugar.backports.ListMethodRewrites;
import com.android.tools.r8.ir.desugar.backports.LongMethodRewrites;
import com.android.tools.r8.ir.desugar.backports.NumericMethodRewrites;
import com.android.tools.r8.ir.desugar.backports.ObjectsMethodRewrites;
@@ -999,19 +999,63 @@
name = factory.createString("of");
for (int i = 0; i <= 10; i++) {
final int formalCount = i;
- proto = factory.createProto(factory.listType, Collections.nCopies(i, factory.objectType));
+ proto = factory.createProto(type, Collections.nCopies(i, factory.objectType));
method = factory.createMethod(type, proto, name);
addProvider(
i == 0
- ? new InvokeRewriter(method, ListMethodRewrites::rewriteEmptyOf)
+ ? new InvokeRewriter(method, CollectionMethodRewrites::rewriteListOfEmpty)
: new MethodGenerator(
method,
- (options, methodArg, ignored) ->
- ListMethodGenerators.generateListOf(options, methodArg, formalCount)));
+ (options, methodArg) ->
+ CollectionMethodGenerators.generateListOf(options, methodArg, formalCount)));
}
- proto = factory.createProto(factory.listType, factory.objectArrayType);
+ proto = factory.createProto(type, factory.objectArrayType);
method = factory.createMethod(type, proto, name);
- addProvider(new MethodGenerator(method, BackportedMethods::ListMethods_ofArray, "ofArray"));
+ addProvider(
+ new MethodGenerator(
+ method, BackportedMethods::CollectionMethods_listOfArray, "ofArray"));
+
+ // Set<E> Set.of(<args>) for 0 to 10 arguments and Set.of(E[])
+ type = factory.setType;
+ name = factory.createString("of");
+ for (int i = 0; i <= 10; i++) {
+ final int formalCount = i;
+ proto = factory.createProto(type, Collections.nCopies(i, factory.objectType));
+ method = factory.createMethod(type, proto, name);
+ addProvider(
+ i == 0
+ ? new InvokeRewriter(method, CollectionMethodRewrites::rewriteSetOfEmpty)
+ : new MethodGenerator(
+ method,
+ (options, methodArg) ->
+ CollectionMethodGenerators.generateSetOf(options, methodArg, formalCount)));
+ }
+ proto = factory.createProto(type, factory.objectArrayType);
+ method = factory.createMethod(type, proto, name);
+ addProvider(
+ new MethodGenerator(
+ method, BackportedMethods::CollectionMethods_setOfArray, "ofArray"));
+
+ // Map<K, V> Map.of(<K, V args>) for 0 to 10 pairs and Map.ofEntries(Map.Entry<K, V>[])
+ type = factory.mapType;
+ name = factory.createString("of");
+ for (int i = 0; i <= 10; i++) {
+ final int formalCount = i;
+ proto = factory.createProto(type, Collections.nCopies(i * 2, factory.objectType));
+ method = factory.createMethod(type, proto, name);
+ addProvider(
+ i == 0
+ ? new InvokeRewriter(method, CollectionMethodRewrites::rewriteMapOfEmpty)
+ : new MethodGenerator(
+ method,
+ (options, methodArg) ->
+ CollectionMethodGenerators.generateMapOf(options, methodArg, formalCount)));
+ }
+ proto = factory.createProto(type, factory.createArrayType(1, factory.mapEntryType));
+ method = factory.createMethod(type, proto, "ofEntries");
+ addProvider(
+ new MethodGenerator(
+ method, BackportedMethods::CollectionMethods_mapOfEntries, "ofEntries"));
}
private void initializeJava11MethodProviders(DexItemFactory factory) {
@@ -1273,7 +1317,7 @@
@Override
public Code generateTemplateMethod(InternalOptions options, DexMethod method) {
- return factory.create(options, method, methodName);
+ return factory.create(options, method);
}
@Override
@@ -1311,7 +1355,7 @@
private interface TemplateMethodFactory {
- Code create(InternalOptions options, DexMethod method, String name);
+ Code create(InternalOptions options, DexMethod method);
}
private interface MethodInvokeRewriter {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
index 8f0e3f6..0922cd2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
@@ -5,23 +5,56 @@
package com.android.tools.r8.ir.desugar;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringDiagnostic;
+import java.util.Collections;
+// TODO(b/134732760): In progress.
+// I convert library calls with desugared parameters/return values so they can work normally.
+// In the JSON of the desugared library, one can specify conversions between desugared and
+// non-desugared types. If no conversion is specified, D8/R8 simply generate wrapper classes around
+// the types. Wrappers induce both memory and runtime performance overhead. Wrappers overload
+// all potential called APIs.
+// Since many types are going to be rewritten, I also need to change the signature of the method
+// called so that they are still called with the original types. Hence the vivified types.
+// Given a type from the library, the prefix rewriter rewrites (->) as follow:
+// vivifiedType -> type;
+// type -> desugarType;
+// No vivified types can be present in the compiled program (will necessarily be rewritten).
+// DesugarType is only a rewritten type (generated through rewriting of type).
+// The type, from the library, may either be rewritten to the desugarType,
+// or be a rewritten type (generated through rewriting of vivifiedType).
public class DesugaredLibraryAPIConverter {
- AppView<?> appView;
+ private static final String VIVIFIED_PREFIX = "$-vivified-$.";
+
+ private final AppView<?> appView;
+ private final DexItemFactory factory;
public DesugaredLibraryAPIConverter(AppView<?> appView) {
this.appView = appView;
+ this.factory = appView.dexItemFactory();
}
public void desugar(IRCode code) {
+ // TODO(b/134732760): The current code does not catch library calls into a program override
+ // which gets rewritten. If method signature has rewritten types and method overrides library,
+ // I should convert back.
+
InstructionListIterator iterator = code.instructionListIterator();
while (iterator.hasNext()) {
Instruction instruction = iterator.next();
@@ -30,23 +63,29 @@
}
InvokeMethod invokeMethod = instruction.asInvokeMethod();
DexMethod invokedMethod = invokeMethod.getInvokedMethod();
- if (appView.rewritePrefix.hasRewrittenType(invokedMethod.holder)) {
+ // Rewritting is required only on calls to library methods which are not desugared.
+ if (appView.rewritePrefix.hasRewrittenType(invokedMethod.holder)
+ || invokedMethod.holder.isArrayType()) {
continue;
}
- // In this case, the method has not been rewritten and is not on a rewritten class.
- // This invoke will (likely) not work at runtime if a desugared type is present.
- if (appView.rewritePrefix.hasRewrittenType(invokedMethod.proto.returnType)) {
- warnInvalidInvoke(invokedMethod.proto.returnType, invokedMethod, "return");
+ DexClass dexClass = appView.definitionFor(invokedMethod.holder);
+ if (dexClass == null || !dexClass.isLibraryClass()) {
+ continue;
}
- for (DexType argType : invokedMethod.proto.parameters.values) {
+ if (appView.rewritePrefix.hasRewrittenType(invokedMethod.proto.returnType)) {
+ addReturnConversion(code, invokeMethod, iterator);
+ }
+ for (int i = 0; i < invokedMethod.proto.parameters.values.length; i++) {
+ DexType argType = invokedMethod.proto.parameters.values[i];
if (appView.rewritePrefix.hasRewrittenType(argType)) {
- warnInvalidInvoke(argType, invokedMethod, "parameter");
+ addParameterConversion(code, invokeMethod, iterator, argType, i);
}
}
}
}
private void warnInvalidInvoke(DexType type, DexMethod invokedMethod, String debugString) {
+ DexType desugaredType = appView.rewritePrefix.rewrittenType(type);
appView
.options()
.reporter
@@ -59,7 +98,128 @@
+ " may not work correctly at runtime ("
+ debugString
+ " type "
- + appView.rewritePrefix.rewrittenType(type)
+ + desugaredType
+ " is a desugared type)."));
}
+
+ private DexType vivifiedTypeFor(DexType type) {
+ DexType vivifiedType =
+ factory.createType(DescriptorUtils.javaTypeToDescriptor(VIVIFIED_PREFIX + type.toString()));
+ appView.rewritePrefix.rewriteType(vivifiedType, type);
+ return vivifiedType;
+ }
+
+ private void addParameterConversion(
+ IRCode code,
+ InvokeMethod invokeMethod,
+ InstructionListIterator iterator,
+ DexType argType,
+ int parameter) {
+ if (!appView
+ .options()
+ .desugaredLibraryConfiguration
+ .getCustomConversions()
+ .containsKey(argType)) {
+ // TODO(b/134732760): Add Wrapper Conversions.
+ warnInvalidInvoke(argType, invokeMethod.getInvokedMethod(), "parameter");
+ return;
+ }
+
+ Value inValue = invokeMethod.inValues().get(parameter);
+ DexType argVivifiedType = vivifiedTypeFor(argType);
+ DexType conversionHolder =
+ appView.options().desugaredLibraryConfiguration.getCustomConversions().get(argType);
+
+ // ConversionType has static method "type convert(rewrittenType)".
+ // But everything is going to be rewritten, so we need to call "vivifiedType convert(type)".
+ DexMethod conversionMethod =
+ factory.createMethod(
+ conversionHolder,
+ factory.createProto(argVivifiedType, argType),
+ factory.convertMethodName);
+ Value convertedValue =
+ code.createValue(
+ TypeLatticeElement.fromDexType(
+ argVivifiedType, inValue.getTypeLattice().nullability(), appView));
+ InvokeStatic conversionInstruction =
+ new InvokeStatic(conversionMethod, convertedValue, Collections.singletonList(inValue));
+ conversionInstruction.setPosition(invokeMethod.getPosition());
+ iterator.previous();
+ iterator.add(conversionInstruction);
+ iterator.next();
+
+ // Rewrite invoke (signature and inValue to rewrite).
+ DexMethod newDexMethod =
+ dexMethodWithDifferentParameter(
+ invokeMethod.getInvokedMethod(), argVivifiedType, parameter);
+ Invoke newInvokeMethod =
+ Invoke.create(
+ invokeMethod.getType(),
+ newDexMethod,
+ newDexMethod.proto,
+ invokeMethod.outValue(),
+ invokeMethod.inValues());
+ newInvokeMethod.replaceValue(parameter, conversionInstruction.outValue());
+ iterator.replaceCurrentInstruction(newInvokeMethod);
+ }
+
+ private void addReturnConversion(
+ IRCode code, InvokeMethod invokeMethod, InstructionListIterator iterator) {
+ DexType returnType = invokeMethod.getReturnType();
+ if (!appView
+ .options()
+ .desugaredLibraryConfiguration
+ .getCustomConversions()
+ .containsKey(returnType)) {
+ // TODO(b/134732760): Add Wrapper Conversions.
+ warnInvalidInvoke(returnType, invokeMethod.getInvokedMethod(), "return");
+ return;
+ }
+
+ DexType returnVivifiedType = vivifiedTypeFor(returnType);
+ DexType conversionHolder =
+ appView.options().desugaredLibraryConfiguration.getCustomConversions().get(returnType);
+
+ // ConversionType has static method "rewrittenType convert(type)".
+ // But everything is going to be rewritten, so we need to call "type convert(vivifiedType)".
+ DexMethod conversionMethod =
+ factory.createMethod(
+ conversionHolder,
+ factory.createProto(returnType, returnVivifiedType),
+ factory.convertMethodName);
+ Value convertedValue =
+ code.createValue(
+ TypeLatticeElement.fromDexType(returnType, Nullability.maybeNull(), appView));
+ invokeMethod.outValue().replaceUsers(convertedValue);
+ InvokeStatic conversionInstruction =
+ new InvokeStatic(
+ conversionMethod, convertedValue, Collections.singletonList(invokeMethod.outValue()));
+ conversionInstruction.setPosition(invokeMethod.getPosition());
+
+ // Rewrite invoke (signature to rewrite).
+ DexMethod newDexMethod =
+ dexMethodWithDifferentReturn(invokeMethod.getInvokedMethod(), returnVivifiedType);
+ Invoke newInvokeMethod =
+ Invoke.create(
+ invokeMethod.getType(),
+ newDexMethod,
+ newDexMethod.proto,
+ invokeMethod.outValue(),
+ invokeMethod.inValues());
+ iterator.replaceCurrentInstruction(newInvokeMethod);
+ iterator.add(conversionInstruction);
+ }
+
+ private DexMethod dexMethodWithDifferentParameter(
+ DexMethod method, DexType newParameterType, int parameter) {
+ DexType[] newParameters = method.proto.parameters.values.clone();
+ newParameters[parameter] = newParameterType;
+ DexProto newProto = factory.createProto(method.proto.returnType, newParameters);
+ return factory.createMethod(method.holder, newProto, method.name);
+ }
+
+ private DexMethod dexMethodWithDifferentReturn(DexMethod method, DexType newReturnType) {
+ DexProto newProto = factory.createProto(newReturnType, method.proto.parameters.values);
+ return factory.createMethod(method.holder, newProto, method.name);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
index 5bf1c8e..e1f2181 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.desugar.PrefixRewritingMapper.DesugarPrefixRewritingMapper;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.Pair;
import com.google.common.collect.ImmutableList;
@@ -22,11 +23,13 @@
public class DesugaredLibraryConfiguration {
// TODO(b/134732760): should use DexString, DexType, DexMethod or so on when possible.
+ private final AndroidApiLevel requiredCompilationAPILevel;
private final boolean libraryCompilation;
private final Map<String, String> rewritePrefix;
private final Map<DexType, DexType> emulateLibraryInterface;
private final Map<DexString, Map<DexType, DexType>> retargetCoreLibMember;
private final Map<DexType, DexType> backportCoreLibraryMember;
+ private final Map<DexType, DexType> customConversions;
private final List<Pair<DexType, DexString>> dontRewriteInvocation;
public static Builder builder(DexItemFactory dexItemFactory) {
@@ -35,26 +38,32 @@
public static DesugaredLibraryConfiguration empty() {
return new DesugaredLibraryConfiguration(
+ AndroidApiLevel.B,
false,
ImmutableMap.of(),
ImmutableMap.of(),
ImmutableMap.of(),
ImmutableMap.of(),
+ ImmutableMap.of(),
ImmutableList.of());
}
public DesugaredLibraryConfiguration(
+ AndroidApiLevel requiredCompilationAPILevel,
boolean libraryCompilation,
Map<String, String> rewritePrefix,
Map<DexType, DexType> emulateLibraryInterface,
Map<DexString, Map<DexType, DexType>> retargetCoreLibMember,
Map<DexType, DexType> backportCoreLibraryMember,
+ Map<DexType, DexType> customConversions,
List<Pair<DexType, DexString>> dontRewriteInvocation) {
+ this.requiredCompilationAPILevel = requiredCompilationAPILevel;
this.libraryCompilation = libraryCompilation;
this.rewritePrefix = rewritePrefix;
this.emulateLibraryInterface = emulateLibraryInterface;
this.retargetCoreLibMember = retargetCoreLibMember;
this.backportCoreLibraryMember = backportCoreLibraryMember;
+ this.customConversions = customConversions;
this.dontRewriteInvocation = dontRewriteInvocation;
}
@@ -64,6 +73,10 @@
: new DesugarPrefixRewritingMapper(rewritePrefix, factory);
}
+ public AndroidApiLevel getRequiredCompilationApiLevel() {
+ return requiredCompilationAPILevel;
+ }
+
public boolean isLibraryCompilation() {
return libraryCompilation;
}
@@ -84,6 +97,10 @@
return backportCoreLibraryMember;
}
+ public Map<DexType, DexType> getCustomConversions() {
+ return customConversions;
+ }
+
public List<Pair<DexType, DexString>> getDontRewriteInvocation() {
return dontRewriteInvocation;
}
@@ -92,17 +109,24 @@
private final DexItemFactory factory;
+ private AndroidApiLevel requiredCompilationAPILevel;
private boolean libraryCompilation = false;
private Map<String, String> rewritePrefix = new HashMap<>();
private Map<DexType, DexType> emulateLibraryInterface = new HashMap<>();
private Map<DexString, Map<DexType, DexType>> retargetCoreLibMember = new IdentityHashMap<>();
private Map<DexType, DexType> backportCoreLibraryMember = new HashMap<>();
+ private Map<DexType, DexType> customConversions = new HashMap<>();
private List<Pair<DexType, DexString>> dontRewriteInvocation = new ArrayList<>();
public Builder(DexItemFactory dexItemFactory) {
this.factory = dexItemFactory;
}
+ public Builder setRequiredCompilationAPILevel(AndroidApiLevel requiredCompilationAPILevel) {
+ this.requiredCompilationAPILevel = requiredCompilationAPILevel;
+ return this;
+ }
+
public Builder setProgramCompilation() {
libraryCompilation = false;
return this;
@@ -126,6 +150,13 @@
return this;
}
+ public Builder putCustomConversion(String type, String conversionHolder) {
+ DexType dexType = stringClassToDexType(type);
+ DexType conversionType = stringClassToDexType(conversionHolder);
+ customConversions.put(dexType, conversionType);
+ return this;
+ }
+
public Builder putRetargetCoreLibMember(String retarget, String rewrittenRetarget) {
int index = sharpIndex(retarget, "retarget core library member");
DexString methodName = factory.createString(retarget.substring(index + 1));
@@ -169,11 +200,13 @@
public DesugaredLibraryConfiguration build() {
return new DesugaredLibraryConfiguration(
+ requiredCompilationAPILevel,
libraryCompilation,
ImmutableMap.copyOf(rewritePrefix),
ImmutableMap.copyOf(emulateLibraryInterface),
ImmutableMap.copyOf(retargetCoreLibMember),
ImmutableMap.copyOf(backportCoreLibraryMember),
+ ImmutableMap.copyOf(customConversions),
ImmutableList.copyOf(dontRewriteInvocation));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
index f5664d3..fbf3d1f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.StringResource;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
import com.google.gson.JsonArray;
@@ -57,6 +58,10 @@
"Unsupported desugared library configuration version, please upgrade the D8/R8"
+ " compiler."));
}
+ int required_compilation_api_level =
+ jsonConfig.get("required_compilation_api_level").getAsInt();
+ configurationBuilder.setRequiredCompilationAPILevel(
+ AndroidApiLevel.getAndroidApiLevel(required_compilation_api_level));
JsonArray jsonFlags =
libraryCompilation
? jsonConfig.getAsJsonArray("library_flags")
@@ -100,6 +105,13 @@
configurationBuilder.putEmulateLibraryInterface(itf.getKey(), itf.getValue().getAsString());
}
}
+ if (jsonFlagSet.has("custom_conversion")) {
+ for (Map.Entry<String, JsonElement> conversion :
+ jsonFlagSet.get("custom_conversion").getAsJsonObject().entrySet()) {
+ configurationBuilder.putCustomConversion(
+ conversion.getKey(), conversion.getValue().getAsString());
+ }
+ }
if (jsonFlagSet.has("dont_rewrite")) {
JsonArray dontRewrite = jsonFlagSet.get("dont_rewrite").getAsJsonArray();
for (JsonElement rewrite : dontRewrite) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 320935f..db8f582 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -156,12 +156,15 @@
}
private void addRewritePrefix(DexType interfaceType, String rewrittenType) {
- appView.rewritePrefix.addPrefix(
- getCompanionClassType(interfaceType).toString(),
- rewrittenType + COMPANION_CLASS_NAME_SUFFIX);
- appView.rewritePrefix.addPrefix(
- getEmulateLibraryInterfaceClassType(interfaceType, factory).toString(),
- rewrittenType + EMULATE_LIBRARY_CLASS_NAME_SUFFIX);
+ appView.rewritePrefix.rewriteType(
+ getCompanionClassType(interfaceType),
+ factory.createType(
+ DescriptorUtils.javaTypeToDescriptor(rewrittenType + COMPANION_CLASS_NAME_SUFFIX)));
+ appView.rewritePrefix.rewriteType(
+ getEmulateLibraryInterfaceClassType(interfaceType, factory),
+ factory.createType(
+ DescriptorUtils.javaTypeToDescriptor(
+ rewrittenType + EMULATE_LIBRARY_CLASS_NAME_SUFFIX)));
}
boolean isEmulatedInterface(DexType itf) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index 65eb486..454085b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -30,6 +30,7 @@
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.utils.DescriptorUtils;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableSet;
@@ -298,8 +299,11 @@
String rewrittenString = rewritten.toString();
String actualRewrittenPrefix = rewrittenString.substring(0, rewrittenString.lastIndexOf('.'));
assert javaName.startsWith(actualPrefix);
- appView.rewritePrefix.addPrefix(
- javaName, actualRewrittenPrefix + javaName.substring(actualPrefix.length()));
+ appView.rewritePrefix.rewriteType(
+ lambdaClassType,
+ factory.createType(
+ DescriptorUtils.javaTypeToDescriptor(
+ actualRewrittenPrefix + javaName.substring(actualPrefix.length()))));
}
private static <K, V> V getKnown(Map<K, V> map, K key) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java b/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
index 2b1b45b..244dfaf 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
@@ -23,7 +23,7 @@
public abstract DexType rewrittenType(DexType type);
- public abstract void addPrefix(String prefix, String rewrittenPrefix);
+ public abstract void rewriteType(DexType type, DexType rewrittenType);
public boolean hasRewrittenType(DexType type) {
return rewrittenType(type) != null;
@@ -35,9 +35,7 @@
private final Set<DexType> notRewritten = Sets.newConcurrentHashSet();
private final Map<DexType, DexType> rewritten = new ConcurrentHashMap<>();
- // Prefix is IdentityHashMap, additionalPrefixes requires however concurrent read and writes.
private final Map<DexString, DexString> initialPrefixes;
- private final Map<DexString, DexString> additionalPrefixes = new ConcurrentHashMap<>();
private final DexItemFactory factory;
public DesugarPrefixRewritingMapper(Map<String, String> prefixes, DexItemFactory factory) {
@@ -97,19 +95,25 @@
}
@Override
- public void addPrefix(String prefix, String rewrittenPrefix) {
- additionalPrefixes.put(toDescriptorPrefix(prefix), toDescriptorPrefix(rewrittenPrefix));
+ public void rewriteType(DexType type, DexType rewrittenType) {
+ assert !notRewritten.contains(type)
+ : "New rewriting rule for "
+ + type
+ + " but the compiler has already made decisions based on the fact that this type was"
+ + " not rewritten";
+ assert !rewritten.containsKey(type) || rewritten.get(type) == rewrittenType
+ : "New rewriting rule for "
+ + type
+ + " but the compiler has already made decisions based on a different rewriting rule"
+ + " for this type";
+ rewritten.put(type, rewrittenType);
}
private DexType computePrefix(DexType type) {
DexString prefixToMatch = type.descriptor.withoutArray(factory);
- DexType result1 = lookup(type, prefixToMatch, initialPrefixes);
- if (result1 != null) {
- return result1;
- }
- DexType result2 = lookup(type, prefixToMatch, additionalPrefixes);
- if (result2 != null) {
- return result2;
+ DexType result = lookup(type, prefixToMatch, initialPrefixes);
+ if (result != null) {
+ return result;
}
notRewritten.add(type);
return null;
@@ -122,7 +126,7 @@
DexString rewrittenTypeDescriptor =
type.descriptor.withNewPrefix(prefix, map.get(prefix), factory);
DexType rewrittenType = factory.createType(rewrittenTypeDescriptor);
- rewritten.put(type, rewrittenType);
+ rewriteType(type, rewrittenType);
return rewrittenType;
}
}
@@ -143,7 +147,7 @@
}
@Override
- public void addPrefix(String prefix, String rewrittenPrefix) {}
+ public void rewriteType(DexType type, DexType rewrittenType) {}
@Override
public boolean isRewriting() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
index 12d43cb..d746bf1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
@@ -126,7 +126,7 @@
// The only encoded method.
CfCode code =
BackportedMethods.CloseResourceMethod_closeResourceImpl(
- options, twrCloseResourceMethod, null);
+ options, twrCloseResourceMethod);
MethodAccessFlags flags = MethodAccessFlags.fromSharedAccessFlags(
Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC, false);
DexEncodedMethod method = new DexEncodedMethod(twrCloseResourceMethod,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
index fb025c9..aadf9e5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
@@ -47,8 +47,7 @@
public final class BackportedMethods {
- public static CfCode BooleanMethods_compare(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode BooleanMethods_compare(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -79,8 +78,7 @@
ImmutableList.of());
}
- public static CfCode BooleanMethods_hashCode(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode BooleanMethods_hashCode(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -104,7 +102,7 @@
ImmutableList.of());
}
- public static CfCode ByteMethods_compare(InternalOptions options, DexMethod method, String name) {
+ public static CfCode ByteMethods_compare(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
return new CfCode(
@@ -122,8 +120,7 @@
ImmutableList.of());
}
- public static CfCode ByteMethods_compareUnsigned(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode ByteMethods_compareUnsigned(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
return new CfCode(
@@ -145,8 +142,7 @@
ImmutableList.of());
}
- public static CfCode ByteMethods_toUnsignedInt(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode ByteMethods_toUnsignedInt(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
return new CfCode(
@@ -164,8 +160,7 @@
ImmutableList.of());
}
- public static CfCode ByteMethods_toUnsignedLong(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode ByteMethods_toUnsignedLong(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
return new CfCode(
@@ -184,8 +179,7 @@
ImmutableList.of());
}
- public static CfCode CharacterMethods_compare(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode CharacterMethods_compare(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
return new CfCode(
@@ -204,7 +198,7 @@
}
public static CfCode CharacterMethods_toStringCodepoint(
- InternalOptions options, DexMethod method, String name) {
+ InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
return new CfCode(
@@ -239,7 +233,7 @@
}
public static CfCode CloseResourceMethod_closeResourceImpl(
- InternalOptions options, DexMethod method, String name) {
+ InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -518,8 +512,400 @@
ImmutableList.of());
}
+ public static CfCode CollectionMethods_listOfArray(InternalOptions options, DexMethod method) {
+ CfLabel label0 = new CfLabel();
+ CfLabel label1 = new CfLabel();
+ CfLabel label2 = new CfLabel();
+ CfLabel label3 = new CfLabel();
+ CfLabel label4 = new CfLabel();
+ CfLabel label5 = new CfLabel();
+ CfLabel label6 = new CfLabel();
+ return new CfCode(
+ method.holder,
+ 3,
+ 6,
+ ImmutableList.of(
+ label0,
+ new CfNew(options.itemFactory.createType("Ljava/util/ArrayList;")),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfArrayLength(),
+ new CfInvoke(
+ 183,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/ArrayList;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("V"), options.itemFactory.createType("I")),
+ options.itemFactory.createString("<init>")),
+ false),
+ new CfStore(ValueType.OBJECT, 1),
+ label1,
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfStore(ValueType.OBJECT, 2),
+ new CfLoad(ValueType.OBJECT, 2),
+ new CfArrayLength(),
+ new CfStore(ValueType.INT, 3),
+ new CfConstNumber(0, ValueType.INT),
+ new CfStore(ValueType.INT, 4),
+ label2,
+ new CfLoad(ValueType.INT, 4),
+ new CfLoad(ValueType.INT, 3),
+ new CfIfCmp(If.Type.GE, ValueType.INT, label5),
+ new CfLoad(ValueType.OBJECT, 2),
+ new CfLoad(ValueType.INT, 4),
+ new CfArrayLoad(MemberType.OBJECT),
+ new CfStore(ValueType.OBJECT, 5),
+ label3,
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfLoad(ValueType.OBJECT, 5),
+ new CfInvoke(
+ 184,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Objects;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/Object;"),
+ options.itemFactory.createType("Ljava/lang/Object;")),
+ options.itemFactory.createString("requireNonNull")),
+ false),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/ArrayList;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Z"),
+ options.itemFactory.createType("Ljava/lang/Object;")),
+ options.itemFactory.createString("add")),
+ false),
+ new CfStackInstruction(CfStackInstruction.Opcode.Pop),
+ label4,
+ new CfIinc(4, 1),
+ new CfGoto(label2),
+ label5,
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfInvoke(
+ 184,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Collections;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/util/List;"),
+ options.itemFactory.createType("Ljava/util/List;")),
+ options.itemFactory.createString("unmodifiableList")),
+ false),
+ new CfReturn(ValueType.OBJECT),
+ label6),
+ ImmutableList.of(),
+ ImmutableList.of());
+ }
+
+ public static CfCode CollectionMethods_mapOfEntries(InternalOptions options, DexMethod method) {
+ CfLabel label0 = new CfLabel();
+ CfLabel label1 = new CfLabel();
+ CfLabel label2 = new CfLabel();
+ CfLabel label3 = new CfLabel();
+ CfLabel label4 = new CfLabel();
+ CfLabel label5 = new CfLabel();
+ CfLabel label6 = new CfLabel();
+ CfLabel label7 = new CfLabel();
+ CfLabel label8 = new CfLabel();
+ CfLabel label9 = new CfLabel();
+ return new CfCode(
+ method.holder,
+ 4,
+ 8,
+ ImmutableList.of(
+ label0,
+ new CfNew(options.itemFactory.createType("Ljava/util/HashMap;")),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfArrayLength(),
+ new CfInvoke(
+ 183,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/HashMap;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("V"), options.itemFactory.createType("I")),
+ options.itemFactory.createString("<init>")),
+ false),
+ new CfStore(ValueType.OBJECT, 1),
+ label1,
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfStore(ValueType.OBJECT, 2),
+ new CfLoad(ValueType.OBJECT, 2),
+ new CfArrayLength(),
+ new CfStore(ValueType.INT, 3),
+ new CfConstNumber(0, ValueType.INT),
+ new CfStore(ValueType.INT, 4),
+ label2,
+ new CfLoad(ValueType.INT, 4),
+ new CfLoad(ValueType.INT, 3),
+ new CfIfCmp(If.Type.GE, ValueType.INT, label8),
+ new CfLoad(ValueType.OBJECT, 2),
+ new CfLoad(ValueType.INT, 4),
+ new CfArrayLoad(MemberType.OBJECT),
+ new CfStore(ValueType.OBJECT, 5),
+ label3,
+ new CfLoad(ValueType.OBJECT, 5),
+ new CfInvoke(
+ 185,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Map$Entry;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/Object;")),
+ options.itemFactory.createString("getKey")),
+ true),
+ new CfInvoke(
+ 184,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Objects;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/Object;"),
+ options.itemFactory.createType("Ljava/lang/Object;")),
+ options.itemFactory.createString("requireNonNull")),
+ false),
+ new CfStore(ValueType.OBJECT, 6),
+ label4,
+ new CfLoad(ValueType.OBJECT, 5),
+ new CfInvoke(
+ 185,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Map$Entry;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/Object;")),
+ options.itemFactory.createString("getValue")),
+ true),
+ new CfInvoke(
+ 184,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Objects;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/Object;"),
+ options.itemFactory.createType("Ljava/lang/Object;")),
+ options.itemFactory.createString("requireNonNull")),
+ false),
+ new CfStore(ValueType.OBJECT, 7),
+ label5,
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfLoad(ValueType.OBJECT, 6),
+ new CfLoad(ValueType.OBJECT, 7),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/HashMap;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/Object;"),
+ options.itemFactory.createType("Ljava/lang/Object;"),
+ options.itemFactory.createType("Ljava/lang/Object;")),
+ options.itemFactory.createString("put")),
+ false),
+ new CfIf(If.Type.EQ, ValueType.OBJECT, label7),
+ label6,
+ new CfNew(options.itemFactory.createType("Ljava/lang/IllegalArgumentException;")),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfNew(options.itemFactory.createType("Ljava/lang/StringBuilder;")),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfInvoke(
+ 183,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/lang/StringBuilder;"),
+ options.itemFactory.createProto(options.itemFactory.createType("V")),
+ options.itemFactory.createString("<init>")),
+ false),
+ new CfConstString(options.itemFactory.createString("duplicate key: ")),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/lang/StringBuilder;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/StringBuilder;"),
+ options.itemFactory.createType("Ljava/lang/String;")),
+ options.itemFactory.createString("append")),
+ false),
+ new CfLoad(ValueType.OBJECT, 6),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/lang/StringBuilder;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/StringBuilder;"),
+ options.itemFactory.createType("Ljava/lang/Object;")),
+ options.itemFactory.createString("append")),
+ false),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/lang/StringBuilder;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/String;")),
+ options.itemFactory.createString("toString")),
+ false),
+ new CfInvoke(
+ 183,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/lang/IllegalArgumentException;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("V"),
+ options.itemFactory.createType("Ljava/lang/String;")),
+ options.itemFactory.createString("<init>")),
+ false),
+ new CfThrow(),
+ label7,
+ new CfIinc(4, 1),
+ new CfGoto(label2),
+ label8,
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfInvoke(
+ 184,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Collections;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/util/Map;"),
+ options.itemFactory.createType("Ljava/util/Map;")),
+ options.itemFactory.createString("unmodifiableMap")),
+ false),
+ new CfReturn(ValueType.OBJECT),
+ label9),
+ ImmutableList.of(),
+ ImmutableList.of());
+ }
+
+ public static CfCode CollectionMethods_setOfArray(InternalOptions options, DexMethod method) {
+ CfLabel label0 = new CfLabel();
+ CfLabel label1 = new CfLabel();
+ CfLabel label2 = new CfLabel();
+ CfLabel label3 = new CfLabel();
+ CfLabel label4 = new CfLabel();
+ CfLabel label5 = new CfLabel();
+ CfLabel label6 = new CfLabel();
+ CfLabel label7 = new CfLabel();
+ return new CfCode(
+ method.holder,
+ 4,
+ 6,
+ ImmutableList.of(
+ label0,
+ new CfNew(options.itemFactory.createType("Ljava/util/HashSet;")),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfArrayLength(),
+ new CfInvoke(
+ 183,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/HashSet;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("V"), options.itemFactory.createType("I")),
+ options.itemFactory.createString("<init>")),
+ false),
+ new CfStore(ValueType.OBJECT, 1),
+ label1,
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfStore(ValueType.OBJECT, 2),
+ new CfLoad(ValueType.OBJECT, 2),
+ new CfArrayLength(),
+ new CfStore(ValueType.INT, 3),
+ new CfConstNumber(0, ValueType.INT),
+ new CfStore(ValueType.INT, 4),
+ label2,
+ new CfLoad(ValueType.INT, 4),
+ new CfLoad(ValueType.INT, 3),
+ new CfIfCmp(If.Type.GE, ValueType.INT, label6),
+ new CfLoad(ValueType.OBJECT, 2),
+ new CfLoad(ValueType.INT, 4),
+ new CfArrayLoad(MemberType.OBJECT),
+ new CfStore(ValueType.OBJECT, 5),
+ label3,
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfLoad(ValueType.OBJECT, 5),
+ new CfInvoke(
+ 184,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Objects;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/Object;"),
+ options.itemFactory.createType("Ljava/lang/Object;")),
+ options.itemFactory.createString("requireNonNull")),
+ false),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/HashSet;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Z"),
+ options.itemFactory.createType("Ljava/lang/Object;")),
+ options.itemFactory.createString("add")),
+ false),
+ new CfIf(If.Type.NE, ValueType.INT, label5),
+ label4,
+ new CfNew(options.itemFactory.createType("Ljava/lang/IllegalArgumentException;")),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfNew(options.itemFactory.createType("Ljava/lang/StringBuilder;")),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfInvoke(
+ 183,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/lang/StringBuilder;"),
+ options.itemFactory.createProto(options.itemFactory.createType("V")),
+ options.itemFactory.createString("<init>")),
+ false),
+ new CfConstString(options.itemFactory.createString("duplicate element: ")),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/lang/StringBuilder;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/StringBuilder;"),
+ options.itemFactory.createType("Ljava/lang/String;")),
+ options.itemFactory.createString("append")),
+ false),
+ new CfLoad(ValueType.OBJECT, 5),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/lang/StringBuilder;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/StringBuilder;"),
+ options.itemFactory.createType("Ljava/lang/Object;")),
+ options.itemFactory.createString("append")),
+ false),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/lang/StringBuilder;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/String;")),
+ options.itemFactory.createString("toString")),
+ false),
+ new CfInvoke(
+ 183,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/lang/IllegalArgumentException;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("V"),
+ options.itemFactory.createType("Ljava/lang/String;")),
+ options.itemFactory.createString("<init>")),
+ false),
+ new CfThrow(),
+ label5,
+ new CfIinc(4, 1),
+ new CfGoto(label2),
+ label6,
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfInvoke(
+ 184,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Collections;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/util/Set;"),
+ options.itemFactory.createType("Ljava/util/Set;")),
+ options.itemFactory.createString("unmodifiableSet")),
+ false),
+ new CfReturn(ValueType.OBJECT),
+ label7),
+ ImmutableList.of(),
+ ImmutableList.of());
+ }
+
public static CfCode CollectionsMethods_emptyEnumeration(
- InternalOptions options, DexMethod method, String name) {
+ InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
return new CfCode(
method.holder,
@@ -549,8 +935,7 @@
ImmutableList.of());
}
- public static CfCode CollectionsMethods_emptyIterator(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode CollectionsMethods_emptyIterator(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
return new CfCode(
method.holder,
@@ -580,7 +965,7 @@
}
public static CfCode CollectionsMethods_emptyListIterator(
- InternalOptions options, DexMethod method, String name) {
+ InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
return new CfCode(
method.holder,
@@ -609,8 +994,7 @@
ImmutableList.of());
}
- public static CfCode DoubleMethods_hashCode(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode DoubleMethods_hashCode(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -643,8 +1027,7 @@
ImmutableList.of());
}
- public static CfCode DoubleMethods_isFinite(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode DoubleMethods_isFinite(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -686,8 +1069,7 @@
ImmutableList.of());
}
- public static CfCode FloatMethods_isFinite(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode FloatMethods_isFinite(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -729,8 +1111,7 @@
ImmutableList.of());
}
- public static CfCode IntegerMethods_compare(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode IntegerMethods_compare(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -762,8 +1143,7 @@
ImmutableList.of());
}
- public static CfCode IntegerMethods_compareUnsigned(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode IntegerMethods_compareUnsigned(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -802,8 +1182,7 @@
ImmutableList.of());
}
- public static CfCode IntegerMethods_divideUnsigned(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode IntegerMethods_divideUnsigned(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -816,13 +1195,13 @@
label0,
new CfLoad(ValueType.INT, 0),
new CfNumberConversion(NumericType.INT, NumericType.LONG),
- new CfConstNumber(4294967295l, ValueType.LONG),
+ new CfConstNumber(4294967295L, ValueType.LONG),
new CfLogicalBinop(CfLogicalBinop.Opcode.And, NumericType.LONG),
new CfStore(ValueType.LONG, 2),
label1,
new CfLoad(ValueType.INT, 1),
new CfNumberConversion(NumericType.INT, NumericType.LONG),
- new CfConstNumber(4294967295l, ValueType.LONG),
+ new CfConstNumber(4294967295L, ValueType.LONG),
new CfLogicalBinop(CfLogicalBinop.Opcode.And, NumericType.LONG),
new CfStore(ValueType.LONG, 4),
label2,
@@ -836,8 +1215,7 @@
ImmutableList.of());
}
- public static CfCode IntegerMethods_parseUnsignedInt(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode IntegerMethods_parseUnsignedInt(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
return new CfCode(
@@ -865,7 +1243,7 @@
}
public static CfCode IntegerMethods_parseUnsignedIntWithRadix(
- InternalOptions options, DexMethod method, String name) {
+ InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -930,7 +1308,7 @@
new CfStore(ValueType.LONG, 2),
label3,
new CfLoad(ValueType.LONG, 2),
- new CfConstNumber(4294967295l, ValueType.LONG),
+ new CfConstNumber(4294967295L, ValueType.LONG),
new CfLogicalBinop(CfLogicalBinop.Opcode.And, NumericType.LONG),
new CfLoad(ValueType.LONG, 2),
new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
@@ -1025,8 +1403,7 @@
ImmutableList.of());
}
- public static CfCode IntegerMethods_remainderUnsigned(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode IntegerMethods_remainderUnsigned(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -1039,13 +1416,13 @@
label0,
new CfLoad(ValueType.INT, 0),
new CfNumberConversion(NumericType.INT, NumericType.LONG),
- new CfConstNumber(4294967295l, ValueType.LONG),
+ new CfConstNumber(4294967295L, ValueType.LONG),
new CfLogicalBinop(CfLogicalBinop.Opcode.And, NumericType.LONG),
new CfStore(ValueType.LONG, 2),
label1,
new CfLoad(ValueType.INT, 1),
new CfNumberConversion(NumericType.INT, NumericType.LONG),
- new CfConstNumber(4294967295l, ValueType.LONG),
+ new CfConstNumber(4294967295L, ValueType.LONG),
new CfLogicalBinop(CfLogicalBinop.Opcode.And, NumericType.LONG),
new CfStore(ValueType.LONG, 4),
label2,
@@ -1059,8 +1436,7 @@
ImmutableList.of());
}
- public static CfCode IntegerMethods_toUnsignedLong(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode IntegerMethods_toUnsignedLong(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
return new CfCode(
@@ -1071,7 +1447,7 @@
label0,
new CfLoad(ValueType.INT, 0),
new CfNumberConversion(NumericType.INT, NumericType.LONG),
- new CfConstNumber(4294967295l, ValueType.LONG),
+ new CfConstNumber(4294967295L, ValueType.LONG),
new CfLogicalBinop(CfLogicalBinop.Opcode.And, NumericType.LONG),
new CfReturn(ValueType.LONG),
label1),
@@ -1079,8 +1455,7 @@
ImmutableList.of());
}
- public static CfCode IntegerMethods_toUnsignedString(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode IntegerMethods_toUnsignedString(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
return new CfCode(
@@ -1108,7 +1483,7 @@
}
public static CfCode IntegerMethods_toUnsignedStringWithRadix(
- InternalOptions options, DexMethod method, String name) {
+ InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -1120,7 +1495,7 @@
label0,
new CfLoad(ValueType.INT, 0),
new CfNumberConversion(NumericType.INT, NumericType.LONG),
- new CfConstNumber(4294967295l, ValueType.LONG),
+ new CfConstNumber(4294967295L, ValueType.LONG),
new CfLogicalBinop(CfLogicalBinop.Opcode.And, NumericType.LONG),
new CfStore(ValueType.LONG, 2),
label1,
@@ -1142,93 +1517,7 @@
ImmutableList.of());
}
- public static CfCode ListMethods_ofArray(InternalOptions options, DexMethod method, String name) {
- CfLabel label0 = new CfLabel();
- CfLabel label1 = new CfLabel();
- CfLabel label2 = new CfLabel();
- CfLabel label3 = new CfLabel();
- CfLabel label4 = new CfLabel();
- CfLabel label5 = new CfLabel();
- CfLabel label6 = new CfLabel();
- return new CfCode(
- method.holder,
- 3,
- 6,
- ImmutableList.of(
- label0,
- new CfNew(options.itemFactory.createType("Ljava/util/ArrayList;")),
- new CfStackInstruction(CfStackInstruction.Opcode.Dup),
- new CfLoad(ValueType.OBJECT, 0),
- new CfArrayLength(),
- new CfInvoke(
- 183,
- options.itemFactory.createMethod(
- options.itemFactory.createType("Ljava/util/ArrayList;"),
- options.itemFactory.createProto(
- options.itemFactory.createType("V"), options.itemFactory.createType("I")),
- options.itemFactory.createString("<init>")),
- false),
- new CfStore(ValueType.OBJECT, 1),
- label1,
- new CfLoad(ValueType.OBJECT, 0),
- new CfStore(ValueType.OBJECT, 2),
- new CfLoad(ValueType.OBJECT, 2),
- new CfArrayLength(),
- new CfStore(ValueType.INT, 3),
- new CfConstNumber(0, ValueType.INT),
- new CfStore(ValueType.INT, 4),
- label2,
- new CfLoad(ValueType.INT, 4),
- new CfLoad(ValueType.INT, 3),
- new CfIfCmp(If.Type.GE, ValueType.INT, label5),
- new CfLoad(ValueType.OBJECT, 2),
- new CfLoad(ValueType.INT, 4),
- new CfArrayLoad(MemberType.OBJECT),
- new CfStore(ValueType.OBJECT, 5),
- label3,
- new CfLoad(ValueType.OBJECT, 1),
- new CfLoad(ValueType.OBJECT, 5),
- new CfInvoke(
- 184,
- options.itemFactory.createMethod(
- options.itemFactory.createType("Ljava/util/Objects;"),
- options.itemFactory.createProto(
- options.itemFactory.createType("Ljava/lang/Object;"),
- options.itemFactory.createType("Ljava/lang/Object;")),
- options.itemFactory.createString("requireNonNull")),
- false),
- new CfInvoke(
- 182,
- options.itemFactory.createMethod(
- options.itemFactory.createType("Ljava/util/ArrayList;"),
- options.itemFactory.createProto(
- options.itemFactory.createType("Z"),
- options.itemFactory.createType("Ljava/lang/Object;")),
- options.itemFactory.createString("add")),
- false),
- new CfStackInstruction(CfStackInstruction.Opcode.Pop),
- label4,
- new CfIinc(4, 1),
- new CfGoto(label2),
- label5,
- new CfLoad(ValueType.OBJECT, 1),
- new CfInvoke(
- 184,
- options.itemFactory.createMethod(
- options.itemFactory.createType("Ljava/util/Collections;"),
- options.itemFactory.createProto(
- options.itemFactory.createType("Ljava/util/List;"),
- options.itemFactory.createType("Ljava/util/List;")),
- options.itemFactory.createString("unmodifiableList")),
- false),
- new CfReturn(ValueType.OBJECT),
- label6),
- ImmutableList.of(),
- ImmutableList.of());
- }
-
- public static CfCode LongMethods_compareUnsigned(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode LongMethods_compareUnsigned(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -1240,12 +1529,12 @@
ImmutableList.of(
label0,
new CfLoad(ValueType.LONG, 0),
- new CfConstNumber(-9223372036854775808l, ValueType.LONG),
+ new CfConstNumber(-9223372036854775808L, ValueType.LONG),
new CfLogicalBinop(CfLogicalBinop.Opcode.Xor, NumericType.LONG),
new CfStore(ValueType.LONG, 4),
label1,
new CfLoad(ValueType.LONG, 2),
- new CfConstNumber(-9223372036854775808l, ValueType.LONG),
+ new CfConstNumber(-9223372036854775808L, ValueType.LONG),
new CfLogicalBinop(CfLogicalBinop.Opcode.Xor, NumericType.LONG),
new CfStore(ValueType.LONG, 6),
label2,
@@ -1267,8 +1556,7 @@
ImmutableList.of());
}
- public static CfCode LongMethods_divideUnsigned(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode LongMethods_divideUnsigned(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -1297,12 +1585,12 @@
new CfIf(If.Type.GE, ValueType.INT, label6),
label1,
new CfLoad(ValueType.LONG, 0),
- new CfConstNumber(-9223372036854775808l, ValueType.LONG),
+ new CfConstNumber(-9223372036854775808L, ValueType.LONG),
new CfLogicalBinop(CfLogicalBinop.Opcode.Xor, NumericType.LONG),
new CfStore(ValueType.LONG, 4),
label2,
new CfLoad(ValueType.LONG, 2),
- new CfConstNumber(-9223372036854775808l, ValueType.LONG),
+ new CfConstNumber(-9223372036854775808L, ValueType.LONG),
new CfLogicalBinop(CfLogicalBinop.Opcode.Xor, NumericType.LONG),
new CfStore(ValueType.LONG, 6),
label3,
@@ -1344,12 +1632,12 @@
new CfStore(ValueType.LONG, 6),
label10,
new CfLoad(ValueType.LONG, 6),
- new CfConstNumber(-9223372036854775808l, ValueType.LONG),
+ new CfConstNumber(-9223372036854775808L, ValueType.LONG),
new CfLogicalBinop(CfLogicalBinop.Opcode.Xor, NumericType.LONG),
new CfStore(ValueType.LONG, 8),
label11,
new CfLoad(ValueType.LONG, 2),
- new CfConstNumber(-9223372036854775808l, ValueType.LONG),
+ new CfConstNumber(-9223372036854775808L, ValueType.LONG),
new CfLogicalBinop(CfLogicalBinop.Opcode.Xor, NumericType.LONG),
new CfStore(ValueType.LONG, 10),
label12,
@@ -1371,8 +1659,7 @@
ImmutableList.of());
}
- public static CfCode LongMethods_hashCode(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode LongMethods_hashCode(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
return new CfCode(
@@ -1393,8 +1680,7 @@
ImmutableList.of());
}
- public static CfCode LongMethods_parseUnsignedLong(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode LongMethods_parseUnsignedLong(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
return new CfCode(
@@ -1422,7 +1708,7 @@
}
public static CfCode LongMethods_parseUnsignedLongWithRadix(
- InternalOptions options, DexMethod method, String name) {
+ InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -1679,8 +1965,7 @@
ImmutableList.of());
}
- public static CfCode LongMethods_remainderUnsigned(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode LongMethods_remainderUnsigned(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -1709,12 +1994,12 @@
new CfIf(If.Type.GE, ValueType.INT, label6),
label1,
new CfLoad(ValueType.LONG, 0),
- new CfConstNumber(-9223372036854775808l, ValueType.LONG),
+ new CfConstNumber(-9223372036854775808L, ValueType.LONG),
new CfLogicalBinop(CfLogicalBinop.Opcode.Xor, NumericType.LONG),
new CfStore(ValueType.LONG, 4),
label2,
new CfLoad(ValueType.LONG, 2),
- new CfConstNumber(-9223372036854775808l, ValueType.LONG),
+ new CfConstNumber(-9223372036854775808L, ValueType.LONG),
new CfLogicalBinop(CfLogicalBinop.Opcode.Xor, NumericType.LONG),
new CfStore(ValueType.LONG, 6),
label3,
@@ -1758,12 +2043,12 @@
new CfStore(ValueType.LONG, 6),
label10,
new CfLoad(ValueType.LONG, 6),
- new CfConstNumber(-9223372036854775808l, ValueType.LONG),
+ new CfConstNumber(-9223372036854775808L, ValueType.LONG),
new CfLogicalBinop(CfLogicalBinop.Opcode.Xor, NumericType.LONG),
new CfStore(ValueType.LONG, 8),
label11,
new CfLoad(ValueType.LONG, 2),
- new CfConstNumber(-9223372036854775808l, ValueType.LONG),
+ new CfConstNumber(-9223372036854775808L, ValueType.LONG),
new CfLogicalBinop(CfLogicalBinop.Opcode.Xor, NumericType.LONG),
new CfStore(ValueType.LONG, 10),
label12,
@@ -1784,8 +2069,7 @@
ImmutableList.of());
}
- public static CfCode LongMethods_toUnsignedString(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode LongMethods_toUnsignedString(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
return new CfCode(
@@ -1813,7 +2097,7 @@
}
public static CfCode LongMethods_toUnsignedStringWithRadix(
- InternalOptions options, DexMethod method, String name) {
+ InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -2066,8 +2350,7 @@
ImmutableList.of());
}
- public static CfCode MathMethods_addExactInt(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode MathMethods_addExactInt(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -2115,8 +2398,7 @@
ImmutableList.of());
}
- public static CfCode MathMethods_addExactLong(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode MathMethods_addExactLong(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -2180,8 +2462,7 @@
ImmutableList.of());
}
- public static CfCode MathMethods_decrementExactInt(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode MathMethods_decrementExactInt(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -2216,8 +2497,7 @@
ImmutableList.of());
}
- public static CfCode MathMethods_decrementExactLong(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode MathMethods_decrementExactLong(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -2229,7 +2509,7 @@
ImmutableList.of(
label0,
new CfLoad(ValueType.LONG, 0),
- new CfConstNumber(-9223372036854775808l, ValueType.LONG),
+ new CfConstNumber(-9223372036854775808L, ValueType.LONG),
new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
new CfIf(If.Type.NE, ValueType.INT, label2),
label1,
@@ -2253,8 +2533,7 @@
ImmutableList.of());
}
- public static CfCode MathMethods_floorDivInt(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode MathMethods_floorDivInt(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -2312,8 +2591,7 @@
ImmutableList.of());
}
- public static CfCode MathMethods_floorDivLong(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode MathMethods_floorDivLong(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -2375,8 +2653,7 @@
ImmutableList.of());
}
- public static CfCode MathMethods_floorDivLongInt(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode MathMethods_floorDivLongInt(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
return new CfCode(
@@ -2404,8 +2681,7 @@
ImmutableList.of());
}
- public static CfCode MathMethods_floorModInt(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode MathMethods_floorModInt(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -2455,8 +2731,7 @@
ImmutableList.of());
}
- public static CfCode MathMethods_floorModLong(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode MathMethods_floorModLong(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -2510,8 +2785,7 @@
ImmutableList.of());
}
- public static CfCode MathMethods_floorModLongInt(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode MathMethods_floorModLongInt(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
return new CfCode(
@@ -2540,8 +2814,7 @@
ImmutableList.of());
}
- public static CfCode MathMethods_incrementExactInt(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode MathMethods_incrementExactInt(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -2576,8 +2849,7 @@
ImmutableList.of());
}
- public static CfCode MathMethods_incrementExactLong(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode MathMethods_incrementExactLong(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -2589,7 +2861,7 @@
ImmutableList.of(
label0,
new CfLoad(ValueType.LONG, 0),
- new CfConstNumber(9223372036854775807l, ValueType.LONG),
+ new CfConstNumber(9223372036854775807L, ValueType.LONG),
new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
new CfIf(If.Type.NE, ValueType.INT, label2),
label1,
@@ -2613,8 +2885,7 @@
ImmutableList.of());
}
- public static CfCode MathMethods_multiplyExactInt(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode MathMethods_multiplyExactInt(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -2662,8 +2933,7 @@
ImmutableList.of());
}
- public static CfCode MathMethods_multiplyExactLong(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode MathMethods_multiplyExactLong(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -2758,7 +3028,7 @@
new CfConstNumber(0, ValueType.INT),
label9,
new CfLoad(ValueType.LONG, 2),
- new CfConstNumber(-9223372036854775808l, ValueType.LONG),
+ new CfConstNumber(-9223372036854775808L, ValueType.LONG),
new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
new CfIf(If.Type.EQ, ValueType.INT, label10),
new CfConstNumber(1, ValueType.INT),
@@ -2803,8 +3073,7 @@
ImmutableList.of());
}
- public static CfCode MathMethods_multiplyExactLongInt(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode MathMethods_multiplyExactLongInt(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
return new CfCode(
@@ -2832,8 +3101,7 @@
ImmutableList.of());
}
- public static CfCode MathMethods_negateExactInt(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode MathMethods_negateExactInt(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -2867,8 +3135,7 @@
ImmutableList.of());
}
- public static CfCode MathMethods_negateExactLong(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode MathMethods_negateExactLong(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -2880,7 +3147,7 @@
ImmutableList.of(
label0,
new CfLoad(ValueType.LONG, 0),
- new CfConstNumber(-9223372036854775808l, ValueType.LONG),
+ new CfConstNumber(-9223372036854775808L, ValueType.LONG),
new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
new CfIf(If.Type.NE, ValueType.INT, label2),
label1,
@@ -2903,8 +3170,7 @@
ImmutableList.of());
}
- public static CfCode MathMethods_nextDownDouble(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode MathMethods_nextDownDouble(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
return new CfCode(
@@ -2930,8 +3196,7 @@
ImmutableList.of());
}
- public static CfCode MathMethods_nextDownFloat(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode MathMethods_nextDownFloat(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
return new CfCode(
@@ -2957,8 +3222,7 @@
ImmutableList.of());
}
- public static CfCode MathMethods_subtractExactInt(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode MathMethods_subtractExactInt(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -3006,8 +3270,7 @@
ImmutableList.of());
}
- public static CfCode MathMethods_subtractExactLong(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode MathMethods_subtractExactLong(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -3071,8 +3334,7 @@
ImmutableList.of());
}
- public static CfCode MathMethods_toIntExact(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode MathMethods_toIntExact(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -3113,7 +3375,7 @@
}
public static CfCode ObjectsMethods_checkFromIndexSize(
- InternalOptions options, DexMethod method, String name) {
+ InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -3253,8 +3515,7 @@
ImmutableList.of());
}
- public static CfCode ObjectsMethods_checkFromToIndex(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode ObjectsMethods_checkFromToIndex(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -3371,8 +3632,7 @@
ImmutableList.of());
}
- public static CfCode ObjectsMethods_checkIndex(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode ObjectsMethods_checkIndex(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -3466,8 +3726,7 @@
ImmutableList.of());
}
- public static CfCode ObjectsMethods_compare(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode ObjectsMethods_compare(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -3504,8 +3763,7 @@
ImmutableList.of());
}
- public static CfCode ObjectsMethods_deepEquals(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode ObjectsMethods_deepEquals(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -3841,8 +4099,7 @@
ImmutableList.of());
}
- public static CfCode ObjectsMethods_equals(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode ObjectsMethods_equals(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -3883,8 +4140,7 @@
ImmutableList.of());
}
- public static CfCode ObjectsMethods_hashCode(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode ObjectsMethods_hashCode(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -3915,8 +4171,7 @@
ImmutableList.of());
}
- public static CfCode ObjectsMethods_isNull(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode ObjectsMethods_isNull(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -3940,8 +4195,7 @@
ImmutableList.of());
}
- public static CfCode ObjectsMethods_nonNull(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode ObjectsMethods_nonNull(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -3966,7 +4220,7 @@
}
public static CfCode ObjectsMethods_requireNonNullElse(
- InternalOptions options, DexMethod method, String name) {
+ InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -4000,7 +4254,7 @@
}
public static CfCode ObjectsMethods_requireNonNullElseGet(
- InternalOptions options, DexMethod method, String name) {
+ InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -4058,7 +4312,7 @@
}
public static CfCode ObjectsMethods_requireNonNullMessage(
- InternalOptions options, DexMethod method, String name) {
+ InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -4093,8 +4347,7 @@
ImmutableList.of());
}
- public static CfCode ObjectsMethods_toString(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode ObjectsMethods_toString(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
return new CfCode(
@@ -4121,8 +4374,7 @@
ImmutableList.of());
}
- public static CfCode ObjectsMethods_toStringDefault(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode ObjectsMethods_toStringDefault(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -4154,8 +4406,7 @@
ImmutableList.of());
}
- public static CfCode OptionalMethods_ifPresentOrElse(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode OptionalMethods_ifPresentOrElse(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -4214,7 +4465,7 @@
}
public static CfCode OptionalMethods_ifPresentOrElseDouble(
- InternalOptions options, DexMethod method, String name) {
+ InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -4271,7 +4522,7 @@
}
public static CfCode OptionalMethods_ifPresentOrElseInt(
- InternalOptions options, DexMethod method, String name) {
+ InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -4328,7 +4579,7 @@
}
public static CfCode OptionalMethods_ifPresentOrElseLong(
- InternalOptions options, DexMethod method, String name) {
+ InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -4384,7 +4635,7 @@
ImmutableList.of());
}
- public static CfCode OptionalMethods_or(InternalOptions options, DexMethod method, String name) {
+ public static CfCode OptionalMethods_or(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -4451,8 +4702,7 @@
ImmutableList.of());
}
- public static CfCode OptionalMethods_stream(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode OptionalMethods_stream(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -4507,8 +4757,7 @@
ImmutableList.of());
}
- public static CfCode ShortMethods_compare(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode ShortMethods_compare(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
return new CfCode(
@@ -4526,8 +4775,7 @@
ImmutableList.of());
}
- public static CfCode ShortMethods_compareUnsigned(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode ShortMethods_compareUnsigned(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
return new CfCode(
@@ -4549,8 +4797,7 @@
ImmutableList.of());
}
- public static CfCode ShortMethods_toUnsignedInt(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode ShortMethods_toUnsignedInt(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
return new CfCode(
@@ -4568,8 +4815,7 @@
ImmutableList.of());
}
- public static CfCode ShortMethods_toUnsignedLong(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode ShortMethods_toUnsignedLong(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
return new CfCode(
@@ -4588,8 +4834,7 @@
ImmutableList.of());
}
- public static CfCode StringMethods_joinArray(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode StringMethods_joinArray(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -4707,8 +4952,7 @@
ImmutableList.of());
}
- public static CfCode StringMethods_joinIterable(
- InternalOptions options, DexMethod method, String name) {
+ public static CfCode StringMethods_joinIterable(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/CollectionMethodGenerators.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/CollectionMethodGenerators.java
new file mode 100644
index 0000000..9e78a3b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/CollectionMethodGenerators.java
@@ -0,0 +1,122 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.desugar.backports;
+
+import com.android.tools.r8.cf.code.CfArrayStore;
+import com.android.tools.r8.cf.code.CfConstNumber;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfNew;
+import com.android.tools.r8.cf.code.CfNewArray;
+import com.android.tools.r8.cf.code.CfReturn;
+import com.android.tools.r8.cf.code.CfStackInstruction;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import org.objectweb.asm.Opcodes;
+
+public final class CollectionMethodGenerators {
+
+ private CollectionMethodGenerators() {}
+
+ public static CfCode generateListOf(InternalOptions options, DexMethod method, int formalCount) {
+ return generateFixedMethods(options, method, formalCount, options.itemFactory.listType);
+ }
+
+ public static CfCode generateSetOf(InternalOptions options, DexMethod method, int formalCount) {
+ return generateFixedMethods(options, method, formalCount, options.itemFactory.setType);
+ }
+
+ private static CfCode generateFixedMethods(
+ InternalOptions options, DexMethod method, int formalCount, DexType returnType) {
+ Builder<CfInstruction> builder = ImmutableList.builder();
+ builder.add(
+ new CfConstNumber(formalCount, ValueType.INT),
+ new CfNewArray(options.itemFactory.objectArrayType));
+
+ for (int i = 0; i < formalCount; i++) {
+ builder.add(
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfConstNumber(i, ValueType.INT),
+ new CfLoad(ValueType.OBJECT, i),
+ new CfArrayStore(MemberType.OBJECT));
+ }
+
+ builder.add(
+ new CfInvoke(
+ Opcodes.INVOKESTATIC,
+ options.itemFactory.createMethod(
+ returnType,
+ options.itemFactory.createProto(returnType, options.itemFactory.objectArrayType),
+ options.itemFactory.createString("of")),
+ false),
+ new CfReturn(ValueType.OBJECT));
+
+ return new CfCode(
+ method.holder,
+ 4,
+ formalCount,
+ builder.build(),
+ ImmutableList.of(),
+ ImmutableList.of());
+ }
+
+ public static CfCode generateMapOf(
+ InternalOptions options, DexMethod method, int formalCount) {
+ DexType mapEntryArray =
+ options.itemFactory.createArrayType(1, options.itemFactory.mapEntryType);
+ DexType simpleEntry = options.itemFactory.createType("Ljava/util/AbstractMap$SimpleEntry;");
+ DexMethod simpleEntryConstructor = options.itemFactory.createMethod(
+ simpleEntry,
+ options.itemFactory.createProto(
+ options.itemFactory.voidType,
+ options.itemFactory.objectType,
+ options.itemFactory.objectType),
+ Constants.INSTANCE_INITIALIZER_NAME);
+
+ Builder<CfInstruction> builder = ImmutableList.builder();
+ builder.add(
+ new CfConstNumber(formalCount, ValueType.INT),
+ new CfNewArray(mapEntryArray));
+
+ for (int i = 0; i < formalCount; i++) {
+ builder.add(
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfConstNumber(i, ValueType.INT),
+ new CfNew(simpleEntry),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfLoad(ValueType.OBJECT, i * 2),
+ new CfLoad(ValueType.OBJECT, i * 2 + 1),
+ new CfInvoke(Opcodes.INVOKESPECIAL, simpleEntryConstructor, false),
+ new CfArrayStore(MemberType.OBJECT));
+ }
+
+ builder.add(
+ new CfInvoke(
+ Opcodes.INVOKESTATIC,
+ options.itemFactory.createMethod(
+ options.itemFactory.mapType,
+ options.itemFactory.createProto(
+ options.itemFactory.mapType,
+ mapEntryArray),
+ options.itemFactory.createString("ofEntries")),
+ false),
+ new CfReturn(ValueType.OBJECT));
+
+ return new CfCode(
+ method.holder,
+ 7,
+ formalCount * 2,
+ builder.build(),
+ ImmutableList.of(),
+ ImmutableList.of());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/CollectionMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/CollectionMethodRewrites.java
new file mode 100644
index 0000000..5f54256
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/CollectionMethodRewrites.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.backports;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeStatic;
+import java.util.Collections;
+
+public final class CollectionMethodRewrites {
+
+ private CollectionMethodRewrites() {}
+
+ public static void rewriteListOfEmpty(
+ InvokeMethod invoke, InstructionListIterator iterator, DexItemFactory factory) {
+ rewriteToCollectionMethod(invoke, iterator, factory, "emptyList");
+ }
+
+ public static void rewriteSetOfEmpty(
+ InvokeMethod invoke, InstructionListIterator iterator, DexItemFactory factory) {
+ rewriteToCollectionMethod(invoke, iterator, factory, "emptySet");
+ }
+
+ public static void rewriteMapOfEmpty(
+ InvokeMethod invoke, InstructionListIterator iterator, DexItemFactory factory) {
+ rewriteToCollectionMethod(invoke, iterator, factory, "emptyMap");
+ }
+
+ private static void rewriteToCollectionMethod(InvokeMethod invoke,
+ InstructionListIterator iterator, DexItemFactory factory, String methodName) {
+ assert invoke.inValues().isEmpty();
+
+ DexMethod collectionsEmptyList =
+ factory.createMethod(factory.collectionsType, invoke.getInvokedMethod().proto, methodName);
+ InvokeStatic newInvoke =
+ new InvokeStatic(collectionsEmptyList, invoke.outValue(), Collections.emptyList());
+ iterator.replaceCurrentInstruction(newInvoke);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/ListMethodGenerators.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/ListMethodGenerators.java
deleted file mode 100644
index 1319752..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/ListMethodGenerators.java
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.ir.desugar.backports;
-
-import com.android.tools.r8.cf.code.CfArrayStore;
-import com.android.tools.r8.cf.code.CfConstNumber;
-import com.android.tools.r8.cf.code.CfInstruction;
-import com.android.tools.r8.cf.code.CfInvoke;
-import com.android.tools.r8.cf.code.CfLoad;
-import com.android.tools.r8.cf.code.CfNewArray;
-import com.android.tools.r8.cf.code.CfReturn;
-import com.android.tools.r8.cf.code.CfStackInstruction;
-import com.android.tools.r8.graph.CfCode;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.ir.code.MemberType;
-import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableList.Builder;
-import org.objectweb.asm.Opcodes;
-
-public final class ListMethodGenerators {
-
- private ListMethodGenerators() {}
-
- public static CfCode generateListOf(InternalOptions options, DexMethod method, int formalCount) {
- Builder<CfInstruction> builder = ImmutableList.builder();
- builder.add(
- new CfConstNumber(formalCount, ValueType.INT),
- new CfNewArray(options.itemFactory.objectArrayType));
-
- for (int i = 0; i < formalCount; i++) {
- builder.add(
- new CfStackInstruction(CfStackInstruction.Opcode.Dup),
- new CfConstNumber(i, ValueType.INT),
- new CfLoad(ValueType.OBJECT, i),
- new CfArrayStore(MemberType.OBJECT));
- }
-
- builder.add(
- new CfInvoke(
- Opcodes.INVOKESTATIC,
- options.itemFactory.createMethod(
- options.itemFactory.listType,
- options.itemFactory.createProto(
- options.itemFactory.listType, options.itemFactory.objectArrayType),
- options.itemFactory.createString("of")),
- false),
- new CfReturn(ValueType.OBJECT));
-
- return new CfCode(
- method.holder,
- formalCount + 2,
- formalCount,
- builder.build(),
- ImmutableList.of(),
- ImmutableList.of());
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/ListMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/ListMethodRewrites.java
deleted file mode 100644
index c3245d8..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/ListMethodRewrites.java
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.desugar.backports;
-
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.ir.code.InstructionListIterator;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeStatic;
-import java.util.Collections;
-
-public final class ListMethodRewrites {
-
- private ListMethodRewrites() {}
-
- public static void rewriteEmptyOf(
- InvokeMethod invoke, InstructionListIterator iterator, DexItemFactory factory) {
- assert invoke.inValues().isEmpty();
-
- DexMethod collectionsEmptyList =
- factory.createMethod(factory.collectionsType, invoke.getInvokedMethod().proto, "emptyList");
- InvokeStatic newInvoke =
- new InvokeStatic(collectionsEmptyList, invoke.outValue(), Collections.emptyList());
- iterator.replaceCurrentInstruction(newInvoke);
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
index 72009f4..6c2110b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
@@ -29,7 +29,6 @@
import com.android.tools.r8.graph.DexValue.DexValueNull;
import com.android.tools.r8.graph.DexValue.DexValueShort;
import com.android.tools.r8.graph.DexValue.DexValueString;
-import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.code.ArrayPut;
import com.android.tools.r8.ir.code.BasicBlock;
@@ -45,7 +44,6 @@
import com.android.tools.r8.ir.code.StaticPut;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.naming.dexitembasedstring.ClassNameComputationInfo;
import com.android.tools.r8.naming.dexitembasedstring.ClassNameComputationInfo.ClassNameMapping;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -104,7 +102,7 @@
this.dexItemFactory = appView.dexItemFactory();
}
- public void optimize(DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) {
+ public void optimize(DexEncodedMethod method, IRCode code) {
if (!method.isClassInitializer()) {
return;
}
@@ -165,14 +163,6 @@
throw new Unreachable("Unexpected field type " + fieldType + ".");
}
}
- } else if (appView.options().enableFieldTypePropagation && appView.appInfo().hasLiveness()) {
- AppInfoWithLiveness appInfoWithLiveness = appView.withLiveness().appInfo();
- if (appInfoWithLiveness.isStaticFieldWrittenOnlyInEnclosingStaticInitializer(field)) {
- TypeLatticeElement valueType = value.getTypeLattice();
- assert valueType.strictlyLessThan(
- TypeLatticeElement.fromDexType(fieldType, Nullability.maybeNull(), appView), appView);
- feedback.markFieldHasDynamicType(field, valueType);
- }
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index fba5ae7..1f36d25 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -218,7 +218,7 @@
// Therefore, Assume elimination may result in a trivial phi:
// z <- phi(x, x)
if (needToCheckTrivialPhis) {
- code.removeAllTrivialPhis();
+ code.removeAllTrivialPhis(valuesThatRequireWidening);
}
if (!valuesThatRequireWidening.isEmpty()) {
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 6644b26..787cf33 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
@@ -5,12 +5,9 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DebugLocalInfo;
-import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexDefinition;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer;
-import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer.TrivialClassInitializer;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexReference;
@@ -19,7 +16,6 @@
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
-import com.android.tools.r8.ir.code.Assume;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
import com.android.tools.r8.ir.code.ConstInstruction;
@@ -31,7 +27,6 @@
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
@@ -44,7 +39,6 @@
import com.google.common.collect.Sets;
import java.util.ListIterator;
import java.util.Set;
-import java.util.function.Predicate;
public class MemberValuePropagation {
@@ -335,18 +329,11 @@
iterator.add(replacement);
}
target.getMutableOptimizationInfo().markAsPropagated();
- return;
- }
- if (target.getOptimizationInfo().neverReturnsNull()
- && current.outValue().getTypeLattice().isReference()
- && current.outValue().canBeNull()) {
- insertAssumeNotNull(code, affectedValues, blocks, iterator, current);
}
}
private void rewriteStaticGetWithConstantValues(
IRCode code,
- Predicate<DexEncodedMethod> isProcessedConcurrently,
Set<Value> affectedValues,
ListIterator<BasicBlock> blocks,
InstructionListIterator iterator,
@@ -390,49 +377,6 @@
code.method.getMutableOptimizationInfo().markUseIdentifierNameString();
}
feedback.markFieldAsPropagated(target);
- return;
- }
-
- if (current.hasOutValue()) {
- Value outValue = current.outValue();
- TypeLatticeElement outType = outValue.getTypeLattice();
- if (outType.isReference() && outType.isNullable()) {
- TypeLatticeElement dynamicType = target.getOptimizationInfo().getDynamicType();
- if (dynamicType != null && dynamicType.isDefinitelyNotNull()) {
- insertAssumeNotNull(code, affectedValues, blocks, iterator, current);
- return;
- }
-
- // In case the class holder of this static field satisfying following criteria:
- // -- cannot trigger other static initializer except for its own
- // -- is final
- // -- has a class initializer which is classified as trivial
- // (see CodeRewriter::computeClassInitializerInfo) and
- // initializes the field being accessed
- //
- // ... and the field itself is not pinned by keep rules (in which case it might
- // be updated outside the class constructor, e.g. via reflections), it is safe
- // to assume that the static-get instruction reads the value it initialized value
- // in class initializer and is never null.
- // TODO(b/141143236): This should be subsumed entirely by the non-null propagation for
- // fields, and thus should be removed.
- DexClass holderDefinition = appView.definitionFor(field.holder);
- if (holderDefinition != null
- && holderDefinition.accessFlags.isFinal()
- && !field.holder.initializationOfParentTypesMayHaveSideEffects(appView)) {
- DexEncodedMethod classInitializer = holderDefinition.getClassInitializer();
- if (classInitializer != null && !isProcessedConcurrently.test(classInitializer)) {
- TrivialInitializer info =
- classInitializer.getOptimizationInfo().getTrivialInitializerInfo();
- if (info != null
- && ((TrivialClassInitializer) info).field == field
- && outValue.getTypeLattice().isReference()
- && outValue.canBeNull()) {
- insertAssumeNotNull(code, affectedValues, blocks, iterator, current);
- }
- }
- }
- }
}
}
@@ -465,39 +409,12 @@
}
}
- private void insertAssumeNotNull(
- IRCode code,
- Set<Value> affectedValues,
- ListIterator<BasicBlock> blocks,
- InstructionListIterator iterator,
- Instruction current) {
- Value knownToBeNonNullValue = current.outValue();
- Set<Value> affectedUsers = knownToBeNonNullValue.affectedValues();
- TypeLatticeElement typeLattice = knownToBeNonNullValue.getTypeLattice();
- Value nonNullValue =
- code.createValue(
- typeLattice.asReferenceTypeLatticeElement().asNotNull(),
- knownToBeNonNullValue.getLocalInfo());
- knownToBeNonNullValue.replaceUsers(nonNullValue);
- Assume nonNull =
- Assume.createAssumeNonNullInstruction(
- nonNullValue, knownToBeNonNullValue, current, appView);
- nonNull.setPosition(appView.options().debug ? current.getPosition() : Position.none());
- if (current.getBlock().hasCatchHandlers()) {
- iterator.split(code, blocks).listIterator(code).add(nonNull);
- } else {
- iterator.add(nonNull);
- }
- affectedValues.addAll(affectedUsers);
- }
-
/**
* Replace invoke targets and field accesses with constant values where possible.
*
* <p>Also assigns value ranges to values where possible.
*/
- public void rewriteWithConstantValues(
- IRCode code, DexType callingContext, Predicate<DexEncodedMethod> isProcessedConcurrently) {
+ public void rewriteWithConstantValues(IRCode code, DexType callingContext) {
IRMetadata metadata = code.metadata();
if (!metadata.mayHaveFieldGet() && !metadata.mayHaveInvokeMethod()) {
return;
@@ -516,7 +433,6 @@
} else if (current.isStaticGet()) {
rewriteStaticGetWithConstantValues(
code,
- isProcessedConcurrently,
affectedValues,
blocks,
iterator,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
index 3b06027..02cfe3a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
@@ -6,8 +6,10 @@
import static com.android.tools.r8.ir.code.DominatorTree.Assumption.MAY_HAVE_UNREACHABLE_BLOCKS;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
@@ -30,6 +32,7 @@
import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
@@ -139,6 +142,9 @@
&& optimizationInfo.getDynamicType().isDefinitelyNotNull()) {
knownToBeNonNullValues.add(outValue);
}
+
+ assert verifyCompanionClassInstanceIsKnownToBeNonNull(
+ fieldInstruction, encodedField, knownToBeNonNullValues);
}
}
}
@@ -244,6 +250,35 @@
}
}
+ private boolean verifyCompanionClassInstanceIsKnownToBeNonNull(
+ FieldInstruction instruction,
+ DexEncodedField encodedField,
+ Set<Value> knownToBeNonNullValues) {
+ if (!appView.appInfo().hasLiveness()) {
+ return true;
+ }
+ if (instruction.isStaticGet()) {
+ AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+ DexField field = encodedField.field;
+ DexClass clazz = appViewWithLiveness.definitionFor(field.holder);
+ assert clazz != null;
+ if (clazz.accessFlags.isFinal()
+ && !clazz.initializationOfParentTypesMayHaveSideEffects(appViewWithLiveness)) {
+ DexEncodedMethod classInitializer = clazz.getClassInitializer();
+ if (classInitializer != null) {
+ TrivialInitializer info =
+ classInitializer.getOptimizationInfo().getTrivialInitializerInfo();
+ boolean expectedToBeNonNull =
+ info != null
+ && info.asTrivialClassInitializer().field == field
+ && !appViewWithLiveness.appInfo().isPinned(field);
+ assert !expectedToBeNonNull || knownToBeNonNullValues.contains(instruction.outValue());
+ }
+ }
+ }
+ return true;
+ }
+
private void addNonNullForValues(
IRCode code,
ListIterator<BasicBlock> blockIterator,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 4837545..1845186 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -556,8 +556,10 @@
templateInstructions.add(OutlineInstruction.fromInstruction(current));
} else if (current.isConstInstruction()) {
// Don't include const instructions in the template.
+ } else if (current.isAssume()) {
+ // Don't include assume instructions in the template.
} else {
- assert false : "Unexpected type of instruction in outlining template.";
+ assert false : "Unexpected type of instruction in outlining template:" + current;
}
}
}
@@ -788,6 +790,12 @@
include = true;
instructionIncrement = 0;
}
+ } else if (instruction.isAssume()) {
+ // Technically, assume instructions will be removed, and thus it should not be included.
+ // However, we should keep searching, so here we pretend to include it with 0 increment.
+ // When adding instruction into the outline candidate, we filter out assume instructions.
+ include = true;
+ instructionIncrement = 0;
} else {
include = canIncludeInstruction(instruction);
}
@@ -986,12 +994,16 @@
// Add the current instruction to the outline.
private void includeInstruction(Instruction instruction) {
+ if (instruction.isAssume()) {
+ return;
+ }
+
List<Value> inValues = orderedInValues(instruction, returnValue);
Value prevReturnValue = returnValue;
if (returnValue != null) {
for (Value value : inValues) {
- if (value == returnValue) {
+ if (value.getAliasedValue() == returnValue) {
assert returnValueUsersLeft > 0;
returnValueUsersLeft--;
}
@@ -1013,7 +1025,7 @@
|| instruction.isArithmeticBinop();
if (inValues.size() > 0) {
for (int i = 0; i < inValues.size(); i++) {
- Value value = inValues.get(i);
+ Value value = inValues.get(i).getAliasedValue();
if (value == prevReturnValue) {
argumentsMap.add(OutlineInstruction.OUTLINE_TEMP);
continue;
@@ -1067,7 +1079,6 @@
}
}
-
protected abstract void handle(int start, int end, Outline outline);
private void candidate(int start, int index) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index b656678..7df3542 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -8,9 +8,11 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionOrPhi;
+import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.desugar.LambdaRewriter;
import com.android.tools.r8.ir.optimize.CodeRewriter;
import com.android.tools.r8.ir.optimize.Inliner;
@@ -18,9 +20,11 @@
import com.android.tools.r8.ir.optimize.string.StringOptimizer;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import java.util.Iterator;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
import java.util.function.Supplier;
@@ -34,8 +38,14 @@
NON_CLASS_TYPE,
UNKNOWN_TYPE,
+ // Used by isClassEligible
+ NON_PROGRAM_CLASS,
+ ABSTRACT_OR_INTERFACE,
+ NEVER_CLASS_INLINE,
+ HAS_FINALIZER,
+ TRIGGER_CLINIT,
+
// Used by InlineCandidateProcessor#isClassAndUsageEligible
- INELIGIBLE_CLASS,
HAS_CLINIT,
HAS_INSTANCE_FIELDS,
NON_FINAL_TYPE,
@@ -46,7 +56,8 @@
}
private final LambdaRewriter lambdaRewriter;
- private final ConcurrentHashMap<DexClass, Boolean> knownClasses = new ConcurrentHashMap<>();
+ private final ConcurrentHashMap<DexClass, EligibilityStatus> knownClasses =
+ new ConcurrentHashMap<>();
public ClassInliner(LambdaRewriter lambdaRewriter) {
this.lambdaRewriter = lambdaRewriter;
@@ -56,7 +67,15 @@
DexEncodedMethod context, Instruction root, EligibilityStatus status) {
if (Log.ENABLED && Log.isLoggingEnabledFor(ClassInliner.class)) {
Log.info(getClass(), "At %s,", context.toSourceString());
- Log.info(getClass(), "ClassInlining eligibility of %s: %s,", root, status);
+ Log.info(getClass(), "ClassInlining eligibility of `%s`: %s.", root, status);
+ }
+ }
+
+ private void logIneligibleUser(
+ DexEncodedMethod context, Instruction root, InstructionOrPhi ineligibleUser) {
+ if (Log.ENABLED && Log.isLoggingEnabledFor(ClassInliner.class)) {
+ Log.info(getClass(), "At %s,", context.toSourceString());
+ Log.info(getClass(), "Ineligible user of `%s`: `%s`.", root, ineligibleUser);
}
}
@@ -133,7 +152,7 @@
// return 1;
// }
// static int method3() {
- // return "F::getX";
+ // return 123;
// }
// }
//
@@ -195,6 +214,7 @@
InstructionOrPhi ineligibleUser = processor.areInstanceUsersEligible(defaultOracle);
if (ineligibleUser != null) {
// This root may succeed if users change in future.
+ logIneligibleUser(code.method, root, ineligibleUser);
continue;
}
@@ -208,7 +228,11 @@
anyInlinedMethods |= processor.processInlining(code, defaultOracle);
// Restore normality.
- code.removeAllTrivialPhis();
+ Set<Value> affectedValues = Sets.newIdentityHashSet();
+ code.removeAllTrivialPhis(affectedValues);
+ if (!affectedValues.isEmpty()) {
+ new TypeAnalysis(appView).narrowing(affectedValues);
+ }
assert code.isConsistentSSA();
rootsIterator.remove();
repeat = true;
@@ -233,11 +257,11 @@
}
}
- private boolean isClassEligible(AppView<AppInfoWithLiveness> appView, DexClass clazz) {
- Boolean eligible = knownClasses.get(clazz);
+ private EligibilityStatus isClassEligible(AppView<AppInfoWithLiveness> appView, DexClass clazz) {
+ EligibilityStatus eligible = knownClasses.get(clazz);
if (eligible == null) {
- boolean computed = computeClassEligible(appView, clazz);
- Boolean existing = knownClasses.putIfAbsent(clazz, computed);
+ EligibilityStatus computed = computeClassEligible(appView, clazz);
+ EligibilityStatus existing = knownClasses.putIfAbsent(clazz, computed);
assert existing == null || existing == computed;
eligible = existing == null ? computed : existing;
}
@@ -248,13 +272,19 @@
// - is not an abstract class or interface
// - does not declare finalizer
// - does not trigger any static initializers except for its own
- private boolean computeClassEligible(AppView<AppInfoWithLiveness> appView, DexClass clazz) {
- if (clazz == null
- || clazz.isNotProgramClass()
- || clazz.accessFlags.isAbstract()
- || clazz.accessFlags.isInterface()
- || appView.appInfo().neverClassInline.contains(clazz.type)) {
- return false;
+ private EligibilityStatus computeClassEligible(
+ AppView<AppInfoWithLiveness> appView, DexClass clazz) {
+ if (clazz == null) {
+ return EligibilityStatus.UNKNOWN_TYPE;
+ }
+ if (clazz.isNotProgramClass()) {
+ return EligibilityStatus.NON_PROGRAM_CLASS;
+ }
+ if (clazz.isAbstract() || clazz.isInterface()) {
+ return EligibilityStatus.ABSTRACT_OR_INTERFACE;
+ }
+ if (appView.appInfo().neverClassInline.contains(clazz.type)) {
+ return EligibilityStatus.NEVER_CLASS_INLINE;
}
// Class must not define finalizer.
@@ -262,11 +292,14 @@
for (DexEncodedMethod method : clazz.virtualMethods()) {
if (method.method.name == dexItemFactory.finalizeMethodName
&& method.method.proto == dexItemFactory.objectMethods.finalize.proto) {
- return false;
+ return EligibilityStatus.HAS_FINALIZER;
}
}
// Check for static initializers in this class or any of interfaces it implements.
- return !clazz.initializationOfParentTypesMayHaveSideEffects(appView);
+ if (clazz.initializationOfParentTypesMayHaveSideEffects(appView)) {
+ return EligibilityStatus.TRIGGER_CLINIT;
+ }
+ return EligibilityStatus.ELIGIBLE;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/FieldValueHelper.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/FieldValueHelper.java
index a16e9a3..d06b3f9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/FieldValueHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/FieldValueHelper.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.optimize.classinliner;
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
import com.android.tools.r8.graph.AppView;
@@ -14,6 +15,7 @@
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Phi.RegisterReadType;
import com.android.tools.r8.ir.code.Value;
@@ -39,6 +41,9 @@
this.code = code;
this.root = root;
this.appView = appView;
+ // Verify that `root` is not aliased.
+ assert root.hasOutValue();
+ assert root.outValue() == root.outValue().getAliasedValue();
}
void replaceValue(Value oldValue, Value newValue) {
@@ -122,10 +127,10 @@
Instruction instruction = iterator.previous();
assert instruction != null;
- if (instruction == root ||
- (instruction.isInstancePut() &&
- instruction.asInstancePut().getField() == field &&
- instruction.asInstancePut().object() == root.outValue())) {
+ if (instruction == root
+ || (instruction.isInstancePut()
+ && instruction.asInstancePut().getField() == field
+ && instruction.asInstancePut().object().getAliasedValue() == root.outValue())) {
valueProducingInsn = instruction;
break;
}
@@ -140,12 +145,17 @@
assert root == valueProducingInsn;
if (defaultValue == null) {
+ InstructionListIterator it = block.listIterator(code, root);
// If we met newInstance it means that default value is supposed to be used.
- defaultValue =
- code.createValue(TypeLatticeElement.fromDexType(field.type, maybeNull(), appView));
- ConstNumber defaultValueInsn = new ConstNumber(defaultValue, 0);
- defaultValueInsn.setPosition(root.getPosition());
- block.listIterator(code, root).add(defaultValueInsn);
+ if (field.type.isPrimitiveType()) {
+ defaultValue = code.createValue(
+ TypeLatticeElement.fromDexType(field.type, definitelyNotNull(), appView));
+ ConstNumber defaultValueInsn = new ConstNumber(defaultValue, 0);
+ defaultValueInsn.setPosition(root.getPosition());
+ it.add(defaultValueInsn);
+ } else {
+ defaultValue = it.insertConstNullInstruction(code, appView.options());
+ }
}
return defaultValue;
}
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 a2e9358..1318135 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
@@ -17,6 +17,7 @@
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
+import com.android.tools.r8.ir.code.Assume;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.IRCode;
@@ -40,16 +41,18 @@
import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
import com.android.tools.r8.kotlin.KotlinInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.Pair;
+import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.util.ArrayList;
-import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
@@ -60,7 +63,7 @@
private final AppView<AppInfoWithLiveness> appView;
private final LambdaRewriter lambdaRewriter;
private final Inliner inliner;
- private final Predicate<DexClass> isClassEligible;
+ private final Function<DexClass, EligibilityStatus> isClassEligible;
private final Predicate<DexEncodedMethod> isProcessedConcurrently;
private final DexEncodedMethod method;
private final Instruction root;
@@ -83,7 +86,7 @@
AppView<AppInfoWithLiveness> appView,
LambdaRewriter lambdaRewriter,
Inliner inliner,
- Predicate<DexClass> isClassEligible,
+ Function<DexClass, EligibilityStatus> isClassEligible,
Predicate<DexEncodedMethod> isProcessedConcurrently,
DexEncodedMethod method,
Instruction root) {
@@ -140,8 +143,9 @@
// * class has class initializer marked as TrivialClassInitializer, and
// class initializer initializes the field we are reading here.
EligibilityStatus isClassAndUsageEligible() {
- if (!isClassEligible.test(eligibleClassDefinition)) {
- return EligibilityStatus.INELIGIBLE_CLASS;
+ EligibilityStatus status = isClassEligible.apply(eligibleClassDefinition);
+ if (status != EligibilityStatus.ELIGIBLE) {
+ return status;
}
if (root.isNewInstance()) {
@@ -251,7 +255,7 @@
*
* @return null if all users are eligible, or the first ineligible user.
*/
- protected InstructionOrPhi areInstanceUsersEligible(Supplier<InliningOracle> defaultOracle) {
+ InstructionOrPhi areInstanceUsersEligible(Supplier<InliningOracle> defaultOracle) {
// No Phi users.
if (eligibleInstance.numberOfPhiUsers() > 0) {
return eligibleInstance.firstPhiUser(); // Not eligible.
@@ -259,11 +263,19 @@
Set<Instruction> currentUsers = eligibleInstance.uniqueUsers();
while (!currentUsers.isEmpty()) {
- Set<Instruction> indirectUsers = new HashSet<>();
+ Set<Instruction> indirectUsers = Sets.newIdentityHashSet();
for (Instruction user : currentUsers) {
+ if (user.isAssume()) {
+ if (user.outValue().numberOfPhiUsers() > 0) {
+ return user.outValue().firstPhiUser(); // Not eligible.
+ }
+ indirectUsers.addAll(user.outValue().uniqueUsers());
+ continue;
+ }
// Field read/write.
if (user.isInstanceGet()
- || (user.isInstancePut() && user.asInstancePut().value() != eligibleInstance)) {
+ || (user.isInstancePut()
+ && user.asInstancePut().value().getAliasedValue() != eligibleInstance)) {
DexField field = user.asFieldInstruction().getField();
if (field.holder == eligibleClass
&& eligibleClassDefinition.lookupInstanceField(field) != null) {
@@ -288,7 +300,7 @@
boolean isCorrespondingConstructorCall =
root.isNewInstance()
&& !invoke.inValues().isEmpty()
- && root.outValue() == invoke.inValues().get(0);
+ && root.outValue() == invoke.getReceiver();
if (isCorrespondingConstructorCall) {
InliningInfo inliningInfo =
isEligibleConstructorCall(user.asInvokeDirect(), singleTarget, defaultOracle);
@@ -343,6 +355,7 @@
// Process inlining, includes the following steps:
//
+ // * remove linked assume instructions if any so that users of the eligible field are up-to-date.
// * replace unused instance usages as arguments which are never used
// * inline extra methods if any, collect new direct method calls
// * inline direct methods if any
@@ -352,6 +365,9 @@
//
// Returns `true` if at least one method was inlined.
boolean processInlining(IRCode code, Supplier<InliningOracle> defaultOracle) {
+ // Verify that `eligibleInstance` is not aliased.
+ assert eligibleInstance == eligibleInstance.getAliasedValue();
+
replaceUsagesAsUnusedArgument(code);
boolean anyInlinedMethods = forceInlineExtraMethodInvocations(code);
@@ -374,11 +390,14 @@
// methods that need to be inlined anyway.
return true;
}
- assert extraMethodCalls.isEmpty();
- assert unusedArguments.isEmpty();
+ assert extraMethodCalls.isEmpty()
+ : "Remaining extra method calls: " + StringUtils.join(extraMethodCalls.entrySet(), ", ");
+ assert unusedArguments.isEmpty()
+ : "Remaining unused arg: " + StringUtils.join(unusedArguments, ", ");
}
anyInlinedMethods |= forceInlineDirectMethodInvocations(code);
+ removeAssumeInstructionsLinkedToEligibleInstance();
removeMiscUsages(code);
removeFieldReads(code);
removeFieldWrites();
@@ -419,6 +438,22 @@
return true;
}
+ private void removeAssumeInstructionsLinkedToEligibleInstance() {
+ for (Instruction user : eligibleInstance.aliasedUsers()) {
+ if (!user.isAssume()) {
+ continue;
+ }
+ Assume<?> assumeInstruction = user.asAssume();
+ Value src = assumeInstruction.src();
+ Value dest = assumeInstruction.outValue();
+ assert dest.numberOfPhiUsers() == 0;
+ dest.replaceUsers(src);
+ removeInstruction(user);
+ }
+ // Verify that no more assume instructions are left as users.
+ assert eligibleInstance.aliasedUsers().stream().noneMatch(Instruction::isAssume);
+ }
+
// Remove miscellaneous users before handling field reads.
private void removeMiscUsages(IRCode code) {
boolean needToRemoveUnreachableBlocks = false;
@@ -495,8 +530,8 @@
}
}
- private void replaceFieldRead(IRCode code,
- InstanceGet fieldRead, Map<DexField, FieldValueHelper> fieldHelpers) {
+ private void replaceFieldRead(
+ IRCode code, InstanceGet fieldRead, Map<DexField, FieldValueHelper> fieldHelpers) {
Value value = fieldRead.outValue();
if (value != null) {
FieldValueHelper helper =
@@ -508,7 +543,10 @@
fieldValueHelper.replaceValue(value, newValue);
}
assert value.numberOfAllUsers() == 0;
- new TypeAnalysis(appView).narrowing(newValue.affectedValues());
+ // `newValue` could be a phi introduced by FieldValueHelper. Its initial type is set as the
+ // type of read field, but it could be more precise than that due to (multiple) inlining.
+ // Instead of values affected by `newValue`, it's necessary to begin with `newValue` itself.
+ new TypeAnalysis(appView).narrowing(ImmutableSet.of(newValue));
}
removeInstruction(fieldRead);
}
@@ -539,7 +577,8 @@
assert isEligibleSingleTarget(singleTarget);
// Must be a constructor called on the receiver.
- if (invoke.inValues().lastIndexOf(eligibleInstance) != 0) {
+ if (ListUtils.lastIndexMatching(
+ invoke.inValues(), v -> v.getAliasedValue() == eligibleInstance) != 0) {
return null;
}
@@ -594,7 +633,7 @@
: null;
}
- // An invoke is eligible for inlinining in the following cases:
+ // An invoke is eligible for inlining in the following cases:
//
// - if it does not return the receiver
// - if there are no uses of the out value
@@ -645,7 +684,8 @@
DexEncodedMethod singleTarget,
Set<Instruction> indirectUsers) {
assert isEligibleSingleTarget(singleTarget);
- if (invoke.inValues().lastIndexOf(eligibleInstance) > 0) {
+ if (ListUtils.lastIndexMatching(
+ invoke.inValues(), v -> v.getAliasedValue() == eligibleInstance) > 0) {
return null; // Instance passed as an argument.
}
return isEligibleVirtualMethodCall(
@@ -714,7 +754,8 @@
return false;
}
if (invoke.isInvokeMethodWithReceiver()
- && invoke.asInvokeMethodWithReceiver().getReceiver() == eligibleInstance) {
+ && invoke.asInvokeMethodWithReceiver().getReceiver().getAliasedValue()
+ == eligibleInstance) {
return false;
}
if (invoke.isInvokeSuper()) {
@@ -755,7 +796,7 @@
// If we got here with invocation on receiver the user is ineligible.
if (invoke.isInvokeMethodWithReceiver()) {
- if (arguments.get(0) == eligibleInstance) {
+ if (arguments.get(0).getAliasedValue() == eligibleInstance) {
return false;
}
@@ -775,7 +816,7 @@
}
for (int argIndex = 0; argIndex < arguments.size(); argIndex++) {
- Value argument = arguments.get(argIndex);
+ Value argument = arguments.get(argIndex).getAliasedValue();
if (argument == eligibleInstance && optimizationInfo.getParameterUsages(argIndex).notUsed()) {
// Reference can be removed since it's not used.
unusedArguments.add(new Pair<>(invoke, argIndex));
@@ -796,7 +837,7 @@
Supplier<InliningOracle> defaultOracle) {
// Go through all arguments, see if all usages of eligibleInstance are good.
for (int argIndex = 0; argIndex < arguments.size(); argIndex++) {
- Value argument = arguments.get(argIndex);
+ Value argument = arguments.get(argIndex).getAliasedValue();
if (argument != eligibleInstance) {
continue; // Nothing to worry about.
}
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 e77f932..1eb64de 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -284,10 +284,9 @@
*/
private final Set<DexEncodedMethod> pendingReflectiveUses = Sets.newLinkedHashSet();
- /**
- * A cache for DexMethod that have been marked reachable.
- */
- private final Set<DexMethod> virtualTargetsMarkedAsReachable = Sets.newIdentityHashSet();
+ /** A cache for DexMethod that have been marked reachable. */
+ private final Map<DexMethod, DexEncodedMethod> virtualTargetsMarkedAsReachable =
+ Maps.newIdentityHashMap();
/**
* A set of references we have reported missing to dedupe warnings.
@@ -596,9 +595,14 @@
private final DexEncodedMethod currentMethod;
- private UseRegistry(DexItemFactory factory, DexEncodedMethod currentMethod) {
+ private UseRegistry(DexItemFactory factory, DexProgramClass holder, DexEncodedMethod method) {
super(factory);
- this.currentMethod = currentMethod;
+ assert holder.type == method.method.holder;
+ this.currentMethod = method;
+ }
+
+ private KeepReasonWitness reportClassReferenced(DexProgramClass referencedClass) {
+ return graphReporter.reportClassReferencedFrom(referencedClass, currentMethod);
}
@Override
@@ -744,10 +748,8 @@
// the field as live, if the holder is an interface.
if (appView.options().enableUnusedInterfaceRemoval) {
if (encodedField.field != field) {
- markTypeAsLive(clazz, graphReporter.reportClassReferencedFrom(clazz, currentMethod));
- markTypeAsLive(
- encodedField.field.type,
- type -> graphReporter.reportClassReferencedFrom(type, currentMethod));
+ markTypeAsLive(clazz, reportClassReferenced(clazz));
+ markTypeAsLive(encodedField.field.type, this::reportClassReferenced);
}
}
@@ -784,10 +786,8 @@
// the field as live, if the holder is an interface.
if (appView.options().enableUnusedInterfaceRemoval) {
if (encodedField.field != field) {
- markTypeAsLive(clazz, graphReporter.reportClassReferencedFrom(clazz, currentMethod));
- markTypeAsLive(
- encodedField.field.type,
- type -> graphReporter.reportClassReferencedFrom(type, currentMethod));
+ markTypeAsLive(clazz, reportClassReferenced(clazz));
+ markTypeAsLive(encodedField.field.type, this::reportClassReferenced);
}
}
@@ -923,7 +923,7 @@
@Override
public boolean registerTypeReference(DexType type) {
- markTypeAsLive(type, clazz -> graphReporter.reportClassReferencedFrom(clazz, currentMethod));
+ markTypeAsLive(type, this::reportClassReferenced);
return true;
}
@@ -1056,8 +1056,7 @@
markClassAsInstantiatedWithCompatRule(baseClass);
} else {
// This also handles reporting of missing classes.
- markTypeAsLive(
- baseType, clazz -> graphReporter.reportClassReferencedFrom(clazz, currentMethod));
+ markTypeAsLive(baseType, this::reportClassReferenced);
}
return true;
}
@@ -1847,9 +1846,6 @@
boolean interfaceInvoke,
KeepReason reason,
BiPredicate<DexProgramClass, DexEncodedMethod> possibleTargetsFilter) {
- if (!virtualTargetsMarkedAsReachable.add(method)) {
- return;
- }
if (Log.ENABLED) {
Log.verbose(getClass(), "Marking virtual method `%s` as reachable.", method);
}
@@ -1867,8 +1863,13 @@
return;
}
- DexEncodedMethod resolutionTarget =
- findAndMarkResolutionTarget(method, interfaceInvoke, reason);
+ DexEncodedMethod resolutionTarget = virtualTargetsMarkedAsReachable.get(method);
+ if (resolutionTarget != null) {
+ registerMethod(resolutionTarget, reason);
+ return;
+ }
+ resolutionTarget = findAndMarkResolutionTarget(method, interfaceInvoke, reason);
+ virtualTargetsMarkedAsReachable.put(method, resolutionTarget);
if (resolutionTarget == null || !resolutionTarget.isValidVirtualTarget(options)) {
// There is no valid resolution, so any call will lead to a runtime exception.
return;
@@ -2514,7 +2515,7 @@
method.parameterAnnotationsList.forEachAnnotation(
annotation -> processAnnotation(method, annotation));
}
- method.registerCodeReferences(new UseRegistry(options.itemFactory, method));
+ method.registerCodeReferences(new UseRegistry(options.itemFactory, clazz, method));
// Add all dependent members to the workqueue.
enqueueRootItems(rootSet.getDependentItems(method));
diff --git a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
index 7f16e07..487afa2 100644
--- a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
@@ -256,6 +256,11 @@
if (appView.appInfo().neverMerge.contains(clazz.type)) {
return MergeGroup.DONT_MERGE;
}
+ if (appView.options().featureSplitConfiguration != null &&
+ appView.options().featureSplitConfiguration.isInFeature(clazz)) {
+ // TODO(b/141452765): Allow class merging between classes in features.
+ return MergeGroup.DONT_MERGE;
+ }
if (clazz.staticFields().size() + clazz.directMethods().size() + clazz.virtualMethods().size()
== 0) {
return MergeGroup.DONT_MERGE;
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index cd77544..b8b8482 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -374,6 +374,12 @@
|| appInfo.neverMerge.contains(clazz.type)) {
return false;
}
+ if (appView.options().featureSplitConfiguration != null &&
+ appView.options().featureSplitConfiguration.isInFeature(clazz)) {
+ // TODO(b/141452765): Allow class merging between classes in features.
+ return false;
+ }
+
// Note that the property "singleSubtype == null" cannot change during merging, since we visit
// classes in a top-down order.
DexType singleSubtype = appInfo.getSingleSubtype(clazz.type);
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 2e45d5f..9c135c8 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -239,7 +239,6 @@
public boolean enableInitializedClassesInInstanceMethodsAnalysis = true;
public boolean enableRedundantFieldLoadElimination = true;
public boolean enableValuePropagation = true;
- public boolean enableFieldTypePropagation = true;
public boolean enableUninstantiatedTypeOptimization = true;
// TODO(b/138917494): Disable until we have numbers on potential performance penalties.
public boolean enableRedundantConstNumberOptimization = false;
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
index a9c3f98..370d5e2 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -8,9 +8,19 @@
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
+import java.util.function.Predicate;
public class ListUtils {
+ public static <T> int lastIndexMatching(List<T> list, Predicate<T> tester) {
+ for (int i = list.size() - 1; i >= 0; i--) {
+ if (tester.test(list.get(i))) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
public static <S, T> List<T> map(Collection<S> list, Function<S, T> fn) {
List<T> result = new ArrayList<>(list.size());
for (S element : list) {
diff --git a/src/test/desugaredLibraryConversions/conversions/TimeConversions.java b/src/test/desugaredLibraryConversions/conversions/TimeConversions.java
new file mode 100644
index 0000000..b5adf3c
--- /dev/null
+++ b/src/test/desugaredLibraryConversions/conversions/TimeConversions.java
@@ -0,0 +1,114 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package java.time;
+
+public class TimeConversions {
+
+ public static j$.time.ZonedDateTime convert(java.time.ZonedDateTime dateTime) {
+ if (dateTime == null) {
+ return null;
+ }
+ return j$.time.ZonedDateTime.of(
+ dateTime.getYear(),
+ dateTime.getMonthValue(),
+ dateTime.getDayOfMonth(),
+ dateTime.getHour(),
+ dateTime.getMinute(),
+ dateTime.getSecond(),
+ dateTime.getNano(),
+ convert(dateTime.getZone()));
+ }
+
+ public static java.time.ZonedDateTime convert(j$.time.ZonedDateTime dateTime) {
+ if (dateTime == null) {
+ return null;
+ }
+ return java.time.ZonedDateTime.of(
+ dateTime.getYear(),
+ dateTime.getMonthValue(),
+ dateTime.getDayOfMonth(),
+ dateTime.getHour(),
+ dateTime.getMinute(),
+ dateTime.getSecond(),
+ dateTime.getNano(),
+ convert(dateTime.getZone()));
+ }
+
+ // ZoneId conversion works because in practice only two final classes are used.
+ // ZoneId is responsible for using one of the other final classes.
+ // This does not support custom implementations of ZoneId.
+
+ public static j$.time.ZoneId convert(java.time.ZoneId zoneId) {
+ if (zoneId == null) {
+ return null;
+ }
+ return j$.time.ZoneId.of(zoneId.getId());
+ }
+
+ public static java.time.ZoneId convert(j$.time.ZoneId zoneId) {
+ if (zoneId == null) {
+ return null;
+ }
+ return java.time.ZoneId.of(zoneId.getId());
+ }
+
+ public static j$.time.MonthDay convert(java.time.MonthDay monthDay) {
+ if (monthDay == null) {
+ return null;
+ }
+ return j$.time.MonthDay.of(monthDay.getMonthValue(), monthDay.getDayOfMonth());
+ }
+
+ public static java.time.MonthDay convert(j$.time.MonthDay monthDay) {
+ if (monthDay == null) {
+ return null;
+ }
+ return java.time.MonthDay.of(monthDay.getMonthValue(), monthDay.getDayOfMonth());
+ }
+
+ public static j$.time.Instant convert(java.time.Instant instant) {
+ if (instant == null) {
+ return null;
+ }
+ return j$.time.Instant.ofEpochSecond(instant.getEpochSecond(), (long) instant.getNano());
+ }
+
+ public static java.time.Instant convert(j$.time.Instant instant) {
+ if (instant == null) {
+ return null;
+ }
+ return java.time.Instant.ofEpochSecond(instant.getEpochSecond(), (long) instant.getNano());
+ }
+
+ // Following conversions are hidden (Used by tests APIs only).
+
+ public static j$.time.LocalDate convert(java.time.LocalDate date) {
+ if (date == null) {
+ return null;
+ }
+ return j$.time.LocalDate.of(date.getYear(), date.getMonthValue(), date.getDayOfMonth());
+ }
+
+ public static java.time.LocalDate convert(j$.time.LocalDate date) {
+ if (date == null) {
+ return null;
+ }
+ return java.time.LocalDate.of(date.getYear(), date.getMonthValue(), date.getDayOfMonth());
+ }
+
+ public static j$.time.Duration convert(java.time.Duration duration) {
+ if (duration == null) {
+ return null;
+ }
+ return j$.time.Duration.ofSeconds(duration.getSeconds(), duration.getNano());
+ }
+
+ public static java.time.Duration convert(j$.time.Duration duration) {
+ if (duration == null) {
+ return null;
+ }
+ return java.time.Duration.ofSeconds(duration.getSeconds(), duration.getNano());
+ }
+}
diff --git a/src/test/desugaredLibraryConversions/stubs/Clock.java b/src/test/desugaredLibraryConversions/stubs/Clock.java
new file mode 100644
index 0000000..51f6e52
--- /dev/null
+++ b/src/test/desugaredLibraryConversions/stubs/Clock.java
@@ -0,0 +1,7 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package j$.time;
+
+public class Clock {}
diff --git a/src/test/desugaredLibraryConversions/stubs/Duration.java b/src/test/desugaredLibraryConversions/stubs/Duration.java
new file mode 100644
index 0000000..e559a8f
--- /dev/null
+++ b/src/test/desugaredLibraryConversions/stubs/Duration.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package j$.time;
+
+public class Duration {
+ public static Duration ofSeconds(long seconds, long nanoAdjustment) {
+ return null;
+ }
+
+ public int getSeconds() {
+ return 0;
+ }
+
+ public int getNano() {
+ return 0;
+ }
+}
diff --git a/src/test/desugaredLibraryConversions/stubs/Instant.java b/src/test/desugaredLibraryConversions/stubs/Instant.java
new file mode 100644
index 0000000..bc399ac
--- /dev/null
+++ b/src/test/desugaredLibraryConversions/stubs/Instant.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package j$.time;
+
+public class Instant {
+
+ public static Instant ofEpochSecond(long seconds, long nanos) {
+ return null;
+ }
+
+ public long getEpochSecond() {
+ return 0L;
+ }
+
+ public int getNano() {
+ return 0;
+ }
+}
diff --git a/src/test/desugaredLibraryConversions/stubs/LocalDate.java b/src/test/desugaredLibraryConversions/stubs/LocalDate.java
new file mode 100644
index 0000000..ddbff3b
--- /dev/null
+++ b/src/test/desugaredLibraryConversions/stubs/LocalDate.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package j$.time;
+
+public class LocalDate {
+ public static LocalDate of(int year, int month, int dayOfMonth) {
+ return null;
+ }
+
+ public int getYear() {
+ return 0;
+ }
+
+ public int getMonthValue() {
+ return 0;
+ }
+
+ public int getDayOfMonth() {
+ return 0;
+ }
+}
diff --git a/src/test/desugaredLibraryConversions/stubs/MonthDay.java b/src/test/desugaredLibraryConversions/stubs/MonthDay.java
new file mode 100644
index 0000000..25a0392
--- /dev/null
+++ b/src/test/desugaredLibraryConversions/stubs/MonthDay.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package j$.time;
+
+public final class MonthDay {
+ public static MonthDay of(int monthValue, int dayValue) {
+ return null;
+ }
+
+ public int getMonthValue() {
+ return 0;
+ }
+
+ public int getDayOfMonth() {
+ return 0;
+ }
+}
diff --git a/src/test/desugaredLibraryConversions/stubs/ZoneId.java b/src/test/desugaredLibraryConversions/stubs/ZoneId.java
new file mode 100644
index 0000000..27f7230
--- /dev/null
+++ b/src/test/desugaredLibraryConversions/stubs/ZoneId.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package j$.time;
+
+public final class ZoneId {
+
+ public static ZoneId of(String id) {
+ return null;
+ }
+
+ public String getId() {
+ return null;
+ }
+}
diff --git a/src/test/desugaredLibraryConversions/stubs/ZonedDateTime.java b/src/test/desugaredLibraryConversions/stubs/ZonedDateTime.java
new file mode 100644
index 0000000..ff02570
--- /dev/null
+++ b/src/test/desugaredLibraryConversions/stubs/ZonedDateTime.java
@@ -0,0 +1,52 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package j$.time;
+
+public final class ZonedDateTime {
+
+ public static ZonedDateTime of(
+ int year,
+ int month,
+ int dayOfMonth,
+ int hour,
+ int minute,
+ int second,
+ int nanoOfSecond,
+ ZoneId zone) {
+ return null;
+ }
+
+ public int getYear() {
+ return 0;
+ }
+
+ public int getMonthValue() {
+ return 0;
+ }
+
+ public int getDayOfMonth() {
+ return 0;
+ }
+
+ public int getHour() {
+ return 0;
+ }
+
+ public int getMinute() {
+ return 0;
+ }
+
+ public int getSecond() {
+ return 0;
+ }
+
+ public int getNano() {
+ return 0;
+ }
+
+ public j$.time.ZoneId getZone() {
+ return null;
+ }
+}
diff --git a/src/test/examplesJava9/backport/MapBackportJava9Main.java b/src/test/examplesJava9/backport/MapBackportJava9Main.java
new file mode 100644
index 0000000..2aa5cfd
--- /dev/null
+++ b/src/test/examplesJava9/backport/MapBackportJava9Main.java
@@ -0,0 +1,247 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package backport;
+
+import java.util.AbstractMap;
+import java.util.Map;
+
+public class MapBackportJava9Main {
+
+ public static void main(String[] args) {
+ testOf0();
+ testOf1();
+ testOf2();
+ testOf10();
+ testOfEntries();
+ }
+
+ private static void testOf0() {
+ Map<Object, Object> ofObject = Map.of();
+ assertEquals(0, ofObject.size());
+ assertEquals(null, ofObject.get(new Object()));
+ assertMutationNotAllowed(ofObject);
+
+ Map<Integer, Integer> ofInteger = Map.of();
+ assertEquals(0, ofInteger.size());
+ assertEquals(null, ofInteger.get(0));
+ }
+
+ private static void testOf1() {
+ Object objectKey0 = new Object();
+ Object objectValue0 = new Object();
+ Map<Object, Object> ofObject = Map.of(objectKey0, objectValue0);
+ assertEquals(1, ofObject.size());
+ assertSame(objectValue0, ofObject.get(objectKey0));
+ assertEquals(null, ofObject.get(new Object()));
+ assertMutationNotAllowed(ofObject);
+
+ Map<Integer, Integer> ofInteger = Map.of(0, 0);
+ assertEquals(1, ofInteger.size());
+ assertEquals(0, ofInteger.get(0));
+ assertEquals(null, ofInteger.get(1));
+
+ try {
+ Map.of((Object) null, 1);
+ throw new AssertionError();
+ } catch (NullPointerException expected) {
+ }
+ try {
+ Map.of(1, (Object) null);
+ throw new AssertionError();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ private static void testOf2() {
+ Object objectKey0 = new Object();
+ Object objectValue0 = new Object();
+ Object objectKey1 = new Object();
+ Object objectValue1 = new Object();
+ Map<Object, Object> ofObject = Map.of(objectKey0, objectValue0, objectKey1, objectValue1);
+ assertEquals(2, ofObject.size());
+ assertSame(objectValue0, ofObject.get(objectKey0));
+ assertSame(objectValue1, ofObject.get(objectKey1));
+ assertEquals(null, ofObject.get(new Object()));
+ assertMutationNotAllowed(ofObject);
+
+ Map<Integer, Integer> ofInteger = Map.of(0, 0, 1, 1);
+ assertEquals(2, ofInteger.size());
+ assertEquals(0, ofInteger.get(0));
+ assertEquals(1, ofInteger.get(1));
+ assertEquals(null, ofInteger.get(3));
+
+ Map<Object, Object> ofMixed = Map.of(objectKey0, 0, objectKey1, 1);
+ assertEquals(2, ofMixed.size());
+ assertEquals(0, ofMixed.get(objectKey0));
+ assertEquals(1, ofMixed.get(objectKey1));
+ assertEquals(null, ofMixed.get(new Object()));
+
+ try {
+ Map.of(1, 1, null, 2);
+ throw new AssertionError();
+ } catch (NullPointerException expected) {
+ }
+ try {
+ Map.of(1, 1, 2, null);
+ throw new AssertionError();
+ } catch (NullPointerException expected) {
+ }
+
+ try {
+ Map.of(1, 1, 1, 2);
+ throw new AssertionError();
+ } catch (IllegalArgumentException expected) {
+ assertEquals("duplicate key: 1", expected.getMessage());
+ }
+ }
+
+ private static void testOf10() {
+ Object objectKey0 = new Object();
+ Object objectValue0 = new Object();
+ Object objectKey6 = new Object();
+ Object objectValue6 = new Object();
+ Object objectKey9 = new Object();
+ Object objectValue9 = new Object();
+ Map<Object, Object> ofObject =
+ Map.of(objectKey0, objectValue0, new Object(), new Object(), new Object(), new Object(),
+ new Object(), new Object(), new Object(), new Object(), new Object(), new Object(),
+ objectKey6, objectValue6, new Object(), new Object(), new Object(), new Object(),
+ objectKey9, objectValue9);
+ assertEquals(10, ofObject.size());
+ assertSame(objectValue0, ofObject.get(objectKey0));
+ assertSame(objectValue6, ofObject.get(objectKey6));
+ assertSame(objectValue9, ofObject.get(objectKey9));
+ assertEquals(null, ofObject.get(new Object()));
+ assertMutationNotAllowed(ofObject);
+
+ Map<Integer, Integer> ofInteger =
+ Map.of(0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9);
+ assertEquals(10, ofInteger.size());
+ assertEquals(0, ofInteger.get(0));
+ assertEquals(6, ofInteger.get(6));
+ assertEquals(9, ofInteger.get(9));
+ assertEquals(null, ofInteger.get(10));
+
+ Map<Object, Object> ofMixed =
+ Map.of(0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, objectKey9, objectValue9);
+ assertEquals(10, ofMixed.size());
+ assertEquals(0, ofMixed.get(0));
+ assertEquals(6, ofMixed.get(6));
+ assertSame(objectValue9, ofMixed.get(objectKey9));
+ assertEquals(null, ofMixed.get(9));
+
+ try {
+ Map.of(0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, null, objectValue9);
+ throw new AssertionError();
+ } catch (NullPointerException expected) {
+ }
+ try {
+ Map.of(0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, objectKey9, null);
+ throw new AssertionError();
+ } catch (NullPointerException expected) {
+ }
+
+ try {
+ Map.of(0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 0, 9);
+ throw new AssertionError();
+ } catch (IllegalArgumentException expected) {
+ assertEquals("duplicate key: 0", expected.getMessage());
+ }
+ }
+
+ private static void testOfEntries() {
+ Object objectKey0 = new Object();
+ Object objectValue0 = new Object();
+ Object objectKey1 = new Object();
+ Object objectValue1 = new Object();
+ Map<Object, Object> ofObject = Map.ofEntries(
+ new AbstractMap.SimpleEntry<>(objectKey0, objectValue0),
+ new AbstractMap.SimpleEntry<>(objectKey1, objectValue1));
+ assertEquals(2, ofObject.size());
+ assertSame(objectValue0, ofObject.get(objectKey0));
+ assertSame(objectValue1, ofObject.get(objectKey1));
+ assertEquals(null, ofObject.get(new Object()));
+ assertMutationNotAllowed(ofObject);
+
+ Map<Integer, Integer> ofInteger = Map.ofEntries(
+ new AbstractMap.SimpleEntry<>(0, 0),
+ new AbstractMap.SimpleEntry<>(1, 1));
+ assertEquals(2, ofInteger.size());
+ assertEquals(0, ofInteger.get(0));
+ assertEquals(1, ofInteger.get(1));
+ assertEquals(null, ofInteger.get(2));
+
+ Map<Object, Object> ofMixed = Map.ofEntries(
+ new AbstractMap.SimpleEntry<>(0, objectValue0),
+ new AbstractMap.SimpleEntry<>(objectKey1, 1));
+ assertEquals(2, ofMixed.size());
+ assertSame(objectValue0, ofMixed.get(0));
+ assertEquals(1, ofMixed.get(objectKey1));
+ assertEquals(null, ofMixed.get(1));
+
+ // Ensure the supplied entry objects are not used directly since they are mutable.
+ Map.Entry<Object, Object> mutableEntry =
+ new AbstractMap.SimpleEntry<>(objectKey0, objectValue0);
+ Map<Object, Object> ofMutableEntry = Map.ofEntries(mutableEntry);
+ mutableEntry.setValue(objectValue1);
+ assertSame(objectValue0, ofMutableEntry.get(objectKey0));
+
+ // Ensure the supplied mutable array is not used directly since it is mutable.
+ @SuppressWarnings("unchecked")
+ Map.Entry<Object, Object>[] mutableArray =
+ new Map.Entry[] { new AbstractMap.SimpleEntry<>(objectKey0, objectValue0) };
+ Map<Object, Object> ofArray = Map.ofEntries(mutableArray);
+ mutableArray[0] = new AbstractMap.SimpleEntry<>(objectKey1, objectValue1);
+ assertSame(objectValue0, ofArray.get(objectKey0));
+ assertEquals(null, ofArray.get(objectKey1));
+
+ try {
+ Map.ofEntries(new AbstractMap.SimpleEntry<Object, Integer>(null, 1));
+ throw new AssertionError();
+ } catch (NullPointerException expected) {
+ }
+ try {
+ Map.ofEntries(new AbstractMap.SimpleEntry<Object, Integer>(1, null));
+ throw new AssertionError();
+ } catch (NullPointerException expected) {
+ }
+
+ try {
+ Map.ofEntries(
+ new AbstractMap.SimpleEntry<>(0, objectValue0),
+ new AbstractMap.SimpleEntry<>(0, objectValue1));
+ throw new AssertionError();
+ } catch (IllegalArgumentException expected) {
+ assertEquals("duplicate key: 0", expected.getMessage());
+ }
+ }
+
+ private static void assertMutationNotAllowed(Map<Object, Object> ofObject) {
+ try {
+ ofObject.put(new Object(), new Object());
+ throw new AssertionError();
+ } catch (UnsupportedOperationException expected) {
+ }
+ for (Map.Entry<Object, Object> entry : ofObject.entrySet()) {
+ try {
+ entry.setValue(new Object());
+ throw new AssertionError();
+ } catch (UnsupportedOperationException expected) {
+ }
+ }
+ }
+
+ private static void assertSame(Object expected, Object actual) {
+ if (expected != actual) {
+ throw new AssertionError("Expected <" + expected + "> but was <" + actual + ">");
+ }
+ }
+
+ private static void assertEquals(Object expected, Object actual) {
+ if (expected != actual && !expected.equals(actual)) {
+ throw new AssertionError("Expected <" + expected + "> but was <" + actual + ">");
+ }
+ }
+}
diff --git a/src/test/examplesJava9/backport/SetBackportJava9Main.java b/src/test/examplesJava9/backport/SetBackportJava9Main.java
new file mode 100644
index 0000000..d824bd0
--- /dev/null
+++ b/src/test/examplesJava9/backport/SetBackportJava9Main.java
@@ -0,0 +1,207 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package backport;
+
+import java.util.Set;
+
+public class SetBackportJava9Main {
+
+ public static void main(String[] args) {
+ testOf0();
+ testOf1();
+ testOf2();
+ testOf10();
+ testOfVarargs();
+ }
+
+ private static void testOf0() {
+ Set<Object> ofObject = Set.of();
+ assertEquals(0, ofObject.size());
+ assertFalse(ofObject.contains(new Object()));
+ assertMutationNotAllowed(ofObject);
+
+ Set<Integer> ofInteger = Set.of();
+ assertEquals(0, ofInteger.size());
+ assertFalse(ofInteger.contains(0));
+ }
+
+ private static void testOf1() {
+ Object anObject = new Object();
+ Set<Object> ofObject = Set.of(anObject);
+ assertEquals(1, ofObject.size());
+ assertTrue(ofObject.contains(anObject));
+ assertFalse(ofObject.contains(new Object()));
+ assertMutationNotAllowed(ofObject);
+
+ Set<Integer> ofInteger = Set.of(1);
+ assertEquals(1, ofInteger.size());
+ assertTrue(ofInteger.contains(1));
+ assertFalse(ofInteger.contains(2));
+
+ try {
+ Set.of((Object) null);
+ throw new AssertionError();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ private static void testOf2() {
+ Object anObject0 = new Object();
+ Object anObject1 = new Object();
+ Set<Object> ofObject = Set.of(anObject0, anObject1);
+ assertEquals(2, ofObject.size());
+ assertTrue(ofObject.contains(anObject0));
+ assertTrue(ofObject.contains(anObject1));
+ assertFalse(ofObject.contains(new Object()));
+ assertMutationNotAllowed(ofObject);
+
+ Set<Integer> ofInteger = Set.of(1, 2);
+ assertEquals(2, ofInteger.size());
+ assertTrue(ofInteger.contains(1));
+ assertTrue(ofInteger.contains(2));
+ assertFalse(ofInteger.contains(3));
+
+ Set<Object> ofMixed = Set.of(anObject0, 1);
+ assertEquals(2, ofMixed.size());
+ assertTrue(ofMixed.contains(anObject0));
+ assertTrue(ofMixed.contains(1));
+ assertFalse(ofMixed.contains(2));
+ assertFalse(ofMixed.contains(anObject1));
+ assertMutationNotAllowed(ofMixed);
+
+ try {
+ Set.of(1, null);
+ throw new AssertionError();
+ } catch (NullPointerException expected) {
+ }
+
+ try {
+ Set.of(1, 1);
+ throw new AssertionError();
+ } catch (IllegalArgumentException expected) {
+ assertEquals("duplicate element: 1", expected.getMessage());
+ }
+ }
+
+ private static void testOf10() {
+ Object anObject0 = new Object();
+ Object anObject6 = new Object();
+ Object anObject9 = new Object();
+ Set<Object> ofObject =
+ Set.of(anObject0, new Object(), new Object(), new Object(), new Object(), new Object(),
+ anObject6, new Object(), new Object(), anObject9);
+ assertEquals(10, ofObject.size());
+ assertTrue(ofObject.contains(anObject0));
+ assertTrue(ofObject.contains(anObject6));
+ assertTrue(ofObject.contains(anObject9));
+ assertFalse(ofObject.contains(new Object()));
+ assertMutationNotAllowed(ofObject);
+
+ Set<Integer> ofInteger = Set.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+ assertEquals(10, ofInteger.size());
+ assertTrue(ofInteger.contains(0));
+ assertTrue(ofInteger.contains(6));
+ assertTrue(ofInteger.contains(9));
+ assertFalse(ofInteger.contains(10));
+
+ Set<Object> ofMixed = Set.of(0, 1, 2, 3, 4, 5, 6, 7, 8, anObject9);
+ assertEquals(10, ofMixed.size());
+ assertTrue(ofMixed.contains(0));
+ assertTrue(ofMixed.contains(6));
+ assertTrue(ofMixed.contains(anObject9));
+ assertFalse(ofMixed.contains(anObject0));
+ assertMutationNotAllowed(ofMixed);
+
+ try {
+ Set.of(0, 1, 2, 3, 4, 5, 6, 7, 8, null);
+ throw new AssertionError();
+ } catch (NullPointerException expected) {
+ }
+
+ try {
+ Set.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 0);
+ throw new AssertionError();
+ } catch (IllegalArgumentException expected) {
+ assertEquals("duplicate element: 0", expected.getMessage());
+ }
+ }
+
+ private static void testOfVarargs() {
+ Object anObject0 = new Object();
+ Object anObject6 = new Object();
+ Object anObject10 = new Object();
+ Set<Object> ofObject =
+ Set.of(anObject0, new Object(), new Object(), new Object(), new Object(), new Object(),
+ anObject6, new Object(), new Object(), new Object(), anObject10);
+ assertEquals(11, ofObject.size());
+ assertTrue(ofObject.contains(anObject0));
+ assertTrue(ofObject.contains(anObject6));
+ assertTrue(ofObject.contains(anObject10));
+ assertFalse(ofObject.contains(new Object()));
+ assertMutationNotAllowed(ofObject);
+
+ Set<Integer> ofInteger = Set.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+ assertEquals(11, ofInteger.size());
+ assertTrue(ofInteger.contains(0));
+ assertTrue(ofInteger.contains(6));
+ assertTrue(ofInteger.contains(10));
+ assertFalse(ofInteger.contains(11));
+
+ Set<Object> ofMixed = Set.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, anObject10);
+ assertEquals(11, ofMixed.size());
+ assertTrue(ofMixed.contains(0));
+ assertTrue(ofMixed.contains(6));
+ assertTrue(ofMixed.contains(anObject10));
+ assertFalse(ofMixed.contains(10));
+ assertFalse(ofMixed.contains(anObject0));
+ assertMutationNotAllowed(ofMixed);
+
+ // Ensure the supplied mutable array is not used directly since it is mutable.
+ Object[] mutableArray = { anObject0 };
+ Set<Object> ofMutableArray = Set.of(mutableArray);
+ mutableArray[0] = anObject10;
+ assertTrue(ofMutableArray.contains(anObject0));
+ assertFalse(ofMutableArray.contains(anObject10));
+
+ try {
+ Set.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, null);
+ throw new AssertionError();
+ } catch (NullPointerException expected) {
+ }
+
+ try {
+ Set.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ throw new AssertionError();
+ } catch (IllegalArgumentException expected) {
+ assertEquals("duplicate element: 0", expected.getMessage());
+ }
+ }
+
+ private static void assertMutationNotAllowed(Set<Object> ofObject) {
+ try {
+ ofObject.add(new Object());
+ throw new AssertionError();
+ } catch (UnsupportedOperationException expected) {
+ }
+ }
+
+ private static void assertTrue(boolean value) {
+ if (!value) {
+ throw new AssertionError("Expected <true> but was <false>");
+ }
+ }
+
+ private static void assertFalse(boolean value) {
+ if (value) {
+ throw new AssertionError("Expected <false> but was <true>");
+ }
+ }
+
+ private static void assertEquals(Object expected, Object actual) {
+ if (expected != actual && !expected.equals(actual)) {
+ throw new AssertionError("Expected <" + expected + "> but was <" + actual + ">");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
index 6cd2bdf..fee1235 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
@@ -93,7 +93,7 @@
.assertSuccess();
} else {
testForD8()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.apply(this::configureProgram)
.compile()
.run(parameters.getRuntime(), testClassName)
@@ -117,7 +117,7 @@
.filter(is -> !ignoredInvokes.contains(is.getMethod().name.toString()))
.collect(toList());
- AndroidApiLevel apiLevel = parameters.getRuntime().asDex().getMinApiLevel();
+ AndroidApiLevel apiLevel = parameters.getApiLevel();
long expectedTargetInvokes = invokeStaticCounts.ceilingEntry(apiLevel).getValue();
long actualTargetInvokes = javaInvokeStatics.size();
assertEquals("Expected "
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/BooleanBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/BooleanBackportTest.java
index edc9e53..3e4b973 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/BooleanBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/BooleanBackportTest.java
@@ -14,7 +14,9 @@
public final class BooleanBackportTest extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters()
+ .withAllRuntimesAndApiLevels()
+ .build();
}
public BooleanBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportJava9Test.java
index fd03159..28eefe3 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportJava9Test.java
@@ -22,6 +22,7 @@
return getTestParameters()
.withCfRuntimesStartingFromIncluding(CfVm.JDK9)
.withDexRuntimes()
+ .withAllApiLevels()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportTest.java
index 82d143c..13a4564 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportTest.java
@@ -14,7 +14,9 @@
public final class ByteBackportTest extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters()
+ .withAllRuntimesAndApiLevels()
+ .build();
}
public ByteBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportJava11Test.java b/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportJava11Test.java
index 1374fcc..f1d51fc 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportJava11Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportJava11Test.java
@@ -21,6 +21,7 @@
public static Iterable<?> data() {
return getTestParameters()
.withDexRuntimes()
+ .withAllApiLevels()
.withCfRuntimesStartingFromIncluding(CfVm.JDK11)
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportTest.java
index 4e1f069..e4eef82 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportTest.java
@@ -14,7 +14,9 @@
public final class CharacterBackportTest extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters()
+ .withAllRuntimesAndApiLevels()
+ .build();
}
public CharacterBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/CollectionsBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/CollectionsBackportTest.java
index 25c3721..66af3d0 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/CollectionsBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/CollectionsBackportTest.java
@@ -19,7 +19,9 @@
public final class CollectionsBackportTest extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters()
+ .withAllRuntimesAndApiLevels()
+ .build();
}
public CollectionsBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/DoubleBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/DoubleBackportTest.java
index 2f6da00..16cdeda 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/DoubleBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/DoubleBackportTest.java
@@ -14,7 +14,9 @@
public final class DoubleBackportTest extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters()
+ .withAllRuntimesAndApiLevels()
+ .build();
}
public DoubleBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/FloatBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/FloatBackportTest.java
index d8c1f8d..92db3a6 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/FloatBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/FloatBackportTest.java
@@ -14,7 +14,9 @@
public final class FloatBackportTest extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters()
+ .withAllRuntimesAndApiLevels()
+ .build();
}
public FloatBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportTest.java
index 706cf36..ed697a7 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportTest.java
@@ -15,7 +15,9 @@
public final class IntegerBackportTest extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters()
+ .withAllRuntimesAndApiLevels()
+ .build();
}
public IntegerBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava9Test.java
index b512634..a861d1f 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava9Test.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.ToolHelper;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.List;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@@ -22,6 +23,7 @@
return getTestParameters()
.withCfRuntimesStartingFromIncluding(CfVm.JDK9)
.withDexRuntimes()
+ .withAllApiLevels()
.build();
}
@@ -29,7 +31,13 @@
Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR).resolve("backport" + JAR_EXTENSION);
public ListBackportJava9Test(TestParameters parameters) {
- super(parameters, Byte.class, TEST_JAR, "backport.ListBackportJava9Main");
- // TODO Once shipped in an actual API level, migrate to ByteBackportTest
+ super(parameters, List.class, TEST_JAR, "backport.ListBackportJava9Main");
+ // TODO Once shipped in an actual API level, migrate to ListBackportTest
+
+ // Available since API 1 and used to test created lists.
+ ignoreInvokes("add");
+ ignoreInvokes("get");
+ ignoreInvokes("set");
+ ignoreInvokes("size");
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/LongBackportSingleMethodTest.java b/src/test/java/com/android/tools/r8/desugar/backports/LongBackportSingleMethodTest.java
index 4ddc18c..7344bd8 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/LongBackportSingleMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/LongBackportSingleMethodTest.java
@@ -20,7 +20,9 @@
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters().withDexRuntimes().build();
+ return getTestParameters()
+ .withAllRuntimesAndApiLevels()
+ .build();
}
public LongBackportSingleMethodTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/LongBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/LongBackportTest.java
index ba8a638..f8a6ad8 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/LongBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/LongBackportTest.java
@@ -15,7 +15,9 @@
public final class LongBackportTest extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters()
+ .withAllRuntimesAndApiLevels()
+ .build();
}
public LongBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava9Test.java
new file mode 100644
index 0000000..06e41f8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava9Test.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.backports;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Map;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
+@RunWith(Parameterized.class)
+public class MapBackportJava9Test extends AbstractBackportTest {
+ @Parameters(name = "{0}")
+ public static Iterable<?> data() {
+ return getTestParameters()
+ .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+ .withDexRuntimes()
+ .withAllApiLevels()
+ .build();
+ }
+
+ private static final Path TEST_JAR =
+ Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR).resolve("backport" + JAR_EXTENSION);
+
+ public MapBackportJava9Test(TestParameters parameters) {
+ super(parameters, Map.class, TEST_JAR, "backport.MapBackportJava9Main");
+ // TODO Once shipped in an actual API level, migrate to MapBackportTest
+
+ // Available since API 1 and used to test created maps.
+ ignoreInvokes("entrySet");
+ ignoreInvokes("get");
+ ignoreInvokes("put");
+ ignoreInvokes("size");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava9Test.java
index d822d5e..439ef38 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava9Test.java
@@ -21,6 +21,7 @@
public static Iterable<?> data() {
return getTestParameters()
.withDexRuntimes()
+ .withAllApiLevels()
.withCfRuntimesStartingFromIncluding(CfVm.JDK9)
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MathBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/MathBackportTest.java
index cbde5a7..acae4c9 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/MathBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MathBackportTest.java
@@ -14,7 +14,9 @@
public final class MathBackportTest extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters()
+ .withAllRuntimesAndApiLevels()
+ .build();
}
public MathBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava9Test.java
index da50daf..7544e87 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava9Test.java
@@ -21,6 +21,7 @@
public static Iterable<?> data() {
return getTestParameters()
.withDexRuntimes()
+ .withAllApiLevels()
.withCfRuntimesStartingFromIncluding(CfVm.JDK9)
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportTest.java
index 74aec20..3120a95 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportTest.java
@@ -15,9 +15,13 @@
import static java.util.Collections.reverseOrder;
-@RunWith(Parameterized.class) public final class ObjectsBackportTest extends AbstractBackportTest {
- @Parameters(name = "{0}") public static Iterable<?> data() {
- return getTestParameters().withAllRuntimes().build();
+@RunWith(Parameterized.class)
+public final class ObjectsBackportTest extends AbstractBackportTest {
+ @Parameters(name = "{0}")
+ public static Iterable<?> data() {
+ return getTestParameters()
+ .withAllRuntimesAndApiLevels()
+ .build();
}
public ObjectsBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java
index e6105d3..d77052a 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.junit.runner.RunWith;
@@ -22,6 +23,7 @@
public static Iterable<?> data() {
return getTestParameters()
.withDexRuntimesStartingFromIncluding(Version.V7_0_0)
+ .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
.withCfRuntimesStartingFromIncluding(CfVm.JDK9)
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava9Test.java
new file mode 100644
index 0000000..b248e0b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava9Test.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.backports;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Set;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
+@RunWith(Parameterized.class)
+public class SetBackportJava9Test extends AbstractBackportTest {
+ @Parameters(name = "{0}")
+ public static Iterable<?> data() {
+ return getTestParameters()
+ .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+ .withDexRuntimes()
+ .withAllApiLevels()
+ .build();
+ }
+
+ private static final Path TEST_JAR =
+ Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR).resolve("backport" + JAR_EXTENSION);
+
+ public SetBackportJava9Test(TestParameters parameters) {
+ super(parameters, Set.class, TEST_JAR, "backport.SetBackportJava9Main");
+ // TODO Once shipped in an actual API level, migrate to SetBackportTest
+
+ // Available since API 1 and used to test created sets.
+ ignoreInvokes("add");
+ ignoreInvokes("contains");
+ ignoreInvokes("size");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportJava9Test.java
index eb2a078..d56ef5b 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportJava9Test.java
@@ -22,6 +22,7 @@
return getTestParameters()
.withCfRuntimesStartingFromIncluding(CfVm.JDK9)
.withDexRuntimes()
+ .withAllApiLevels()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportTest.java
index dd6e702..2d1a61a 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportTest.java
@@ -14,7 +14,9 @@
public final class ShortBackportTest extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters()
+ .withAllRuntimesAndApiLevels()
+ .build();
}
public ShortBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava9Test.java
index e7486b9..43cbf48 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava9Test.java
@@ -21,6 +21,7 @@
public static Iterable<?> data() {
return getTestParameters()
.withDexRuntimes()
+ .withAllApiLevels()
.withCfRuntimesStartingFromIncluding(CfVm.JDK9)
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportTest.java
index 915a6fb..1926975 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportTest.java
@@ -14,7 +14,9 @@
public final class StrictMathBackportTest extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters()
+ .withAllRuntimesAndApiLevels()
+ .build();
}
public StrictMathBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/StringBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/StringBackportTest.java
index 52610db..6ac9189 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/StringBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/StringBackportTest.java
@@ -18,7 +18,9 @@
public final class StringBackportTest extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters()
+ .withAllRuntimesAndApiLevels()
+ .build();
}
public StringBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/CoreLibDesugarTestBase.java b/src/test/java/com/android/tools/r8/desugar/corelib/CoreLibDesugarTestBase.java
index 7543fc5..8741c08 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/CoreLibDesugarTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/CoreLibDesugarTestBase.java
@@ -20,6 +20,8 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
@@ -103,6 +105,13 @@
}
}
+ protected static Path[] getAllFilesWithSuffixInDirectory(Path directory, String suffix)
+ throws IOException {
+ return Files.walk(directory)
+ .filter(path -> path.toString().endsWith(suffix))
+ .toArray(Path[]::new);
+ }
+
protected KeepRuleConsumer createKeepRuleConsumer(TestParameters parameters) {
if (requiresAnyCoreLibDesugaring(parameters)) {
return new PresentKeepRuleConsumer();
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/InconsistentPrefixTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/InconsistentPrefixTest.java
index 167374a..843551e 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/InconsistentPrefixTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/InconsistentPrefixTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
import com.android.tools.r8.jasmin.JasminBuilder;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.nio.file.Path;
@@ -39,11 +40,13 @@
options ->
options.desugaredLibraryConfiguration =
new DesugaredLibraryConfiguration(
+ AndroidApiLevel.B,
false,
x,
ImmutableMap.of(),
ImmutableMap.of(),
ImmutableMap.of(),
+ ImmutableMap.of(),
ImmutableList.of()))
.compile();
fail("Should have raised the compilation error.");
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/KeepRuleShrinkTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/KeepRuleShrinkTest.java
index 30b62a0..843e154 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/KeepRuleShrinkTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/KeepRuleShrinkTest.java
@@ -57,6 +57,7 @@
static class Executor {
+ @SuppressWarnings("unchecked")
public static void main(String[] args) {
Map<String, String>[] maps =
new Map[] {
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/LintFilesTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/LintFilesTest.java
new file mode 100644
index 0000000..87a6140
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/LintFilesTest.java
@@ -0,0 +1,94 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.corelib;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.GenerateLintFiles;
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Reporter;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.Assume;
+import org.junit.Test;
+
+public class LintFilesTest extends TestBase {
+
+ private void checkFileContent(AndroidApiLevel minApiLevel, Path lintFile) throws Exception {
+ // Just do some light probing in the generated lint files.
+ List<String> methods = FileUtils.readAllLines(lintFile);
+ assertTrue(methods.contains("java/util/List/spliterator()Ljava/util/Spliterator;"));
+ assertTrue(methods.contains("java/util/Optional/empty()Ljava/util/Optional;"));
+ assertTrue(methods.contains("java/util/OptionalInt/empty()Ljava/util/OptionalInt;"));
+ assertEquals(
+ minApiLevel == AndroidApiLevel.L,
+ methods.contains("java/util/Collection/parallelStream()Ljava/util/stream/Stream;"));
+ assertEquals(
+ minApiLevel == AndroidApiLevel.L,
+ methods.contains(
+ "java/util/stream/DoubleStream/parallel()Ljava/util/stream/DoubleStream;"));
+ assertEquals(
+ minApiLevel == AndroidApiLevel.L,
+ methods.contains("java/util/stream/IntStream/parallel()Ljava/util/stream/IntStream;"));
+ }
+
+ @Test
+ public void testFileContent() throws Exception {
+ // Test require r8.jar not r8lib.jar, as the class com.android.tools.r8.GenerateLintFiles in
+ // not kept.
+ Assume.assumeTrue(!ToolHelper.isTestingR8Lib());
+
+ Path directory = temp.newFolder().toPath();
+ GenerateLintFiles.main(
+ new String[] {ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString(), directory.toString()});
+ InternalOptions options = new InternalOptions(new DexItemFactory(), new Reporter());
+ DesugaredLibraryConfiguration desugaredLibraryConfiguration =
+ new DesugaredLibraryConfigurationParser(
+ options.itemFactory, options.reporter, false, AndroidApiLevel.B.getLevel())
+ .parse(StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING));
+
+ for (AndroidApiLevel apiLevel : AndroidApiLevel.values()) {
+ if (apiLevel == AndroidApiLevel.R) {
+ // Skip API level 30 for now.
+ continue;
+ }
+
+ if (apiLevel.getLevel()
+ >= desugaredLibraryConfiguration.getRequiredCompilationApiLevel().getLevel()) {
+ Path compileApiLevelDirectory =
+ directory.resolve("compile_api_level_" + apiLevel.getLevel());
+ assertTrue(Files.exists(compileApiLevelDirectory));
+ for (AndroidApiLevel minApiLevel : AndroidApiLevel.values()) {
+ String desugaredApisBaseName =
+ "desugared_apis_" + apiLevel.getLevel() + "_" + minApiLevel.getLevel();
+ if (minApiLevel == AndroidApiLevel.L || minApiLevel == AndroidApiLevel.B) {
+ assertTrue(
+ Files.exists(compileApiLevelDirectory.resolve(desugaredApisBaseName + ".txt")));
+ assertTrue(
+ Files.exists(compileApiLevelDirectory.resolve(desugaredApisBaseName + ".jar")));
+ checkFileContent(
+ minApiLevel, compileApiLevelDirectory.resolve(desugaredApisBaseName + ".txt"));
+ } else {
+ assertFalse(
+ Files.exists(compileApiLevelDirectory.resolve(desugaredApisBaseName + ".txt")));
+ assertFalse(
+ Files.exists(compileApiLevelDirectory.resolve(desugaredApisBaseName + ".jar")));
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/APIConversionTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionTest.java
similarity index 95%
rename from src/test/java/com/android/tools/r8/desugar/corelib/APIConversionTest.java
rename to src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionTest.java
index 167ecf7..7a68a55 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/APIConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionTest.java
@@ -2,7 +2,7 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.desugar.corelib;
+package com.android.tools.r8.desugar.corelib.conversionTests;
import static org.hamcrest.CoreMatchers.endsWith;
import static org.hamcrest.core.StringContains.containsString;
@@ -10,6 +10,7 @@
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.desugar.corelib.CoreLibDesugarTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import java.util.Arrays;
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionTestBase.java b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionTestBase.java
new file mode 100644
index 0000000..eca2a47
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionTestBase.java
@@ -0,0 +1,59 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.corelib.conversionTests;
+
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.desugar.corelib.CoreLibDesugarTestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+
+public class APIConversionTestBase extends CoreLibDesugarTestBase {
+
+ private static final Path CONVERSION_FOLDER = Paths.get("src/test/desugaredLibraryConversions");
+
+ public Path[] getTimeConversionClasses() throws IOException {
+ File conversionFolder = temp.newFolder("conversions");
+ File stubsFolder = temp.newFolder("stubs");
+
+ // Compile the stubs to be able to compile the conversions.
+ ToolHelper.runJavac(
+ CfVm.JDK8,
+ null,
+ stubsFolder.toPath(),
+ getAllFilesWithSuffixInDirectory(CONVERSION_FOLDER.resolve("stubs/"), "java"));
+
+ // Compile the conversions using the stubs.
+ ArrayList<Path> classPath = new ArrayList<>();
+ classPath.add(stubsFolder.toPath());
+ ToolHelper.runJavac(
+ CfVm.JDK8,
+ classPath,
+ conversionFolder.toPath(),
+ getAllFilesWithSuffixInDirectory(CONVERSION_FOLDER.resolve("conversions"), "java"));
+
+ Path[] classes = getAllFilesWithSuffixInDirectory(conversionFolder.toPath(), "class");
+ assert classes.length > 0
+ : "Something went wrong during compilation, check the runJavac return value for debugging.";
+ return classes;
+ }
+
+ protected Path buildDesugaredLibraryWithConversionExtension(AndroidApiLevel apiLevel) {
+ Path[] timeConversionClasses = null;
+ try {
+ timeConversionClasses = getTimeConversionClasses();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ ArrayList<Path> paths = new ArrayList<>();
+ Collections.addAll(paths, timeConversionClasses);
+ return buildDesugaredLibrary(apiLevel, "", false, paths);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/TimeConversionCompilationTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/TimeConversionCompilationTest.java
new file mode 100644
index 0000000..2cdf6e7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/TimeConversionCompilationTest.java
@@ -0,0 +1,135 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.corelib.conversionTests;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.L8Command;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
+import com.android.tools.r8.TestRuntime.DexRuntime;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.nio.file.Path;
+import java.time.ZoneId;
+import java.util.TimeZone;
+import org.junit.Test;
+
+public class TimeConversionCompilationTest extends APIConversionTestBase {
+
+ @Test
+ public void testTimeGeneratedDex() throws Exception {
+ TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
+ Path desugaredLib = temp.newFolder().toPath().resolve("conversion_dex.zip");
+ L8Command.Builder l8Builder =
+ L8Command.builder(diagnosticsHandler)
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .addProgramFiles(getTimeConversionClasses())
+ .addDesugaredLibraryConfiguration(
+ StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
+ .setMinApiLevel(AndroidApiLevel.B.getLevel())
+ .setOutput(desugaredLib, OutputMode.DexIndexed);
+ ToolHelper.runL8(l8Builder.build(), x -> {});
+ this.checkTimeConversionGeneratedDex(new CodeInspector(desugaredLib));
+ }
+
+ private void checkTimeConversionGeneratedDex(CodeInspector inspector) {
+ ClassSubject clazz = inspector.clazz("j$.time.TimeConversions");
+ assertThat(clazz, isPresent());
+ assertEquals(13, clazz.allMethods().size());
+ }
+
+ @Test
+ public void testRewrittenAPICalls() throws Exception {
+ testForD8()
+ .setMinApi(AndroidApiLevel.B)
+ .addInnerClasses(TimeConversionCompilationTest.class)
+ .enableCoreLibraryDesugaring(AndroidApiLevel.B)
+ .compile()
+ .inspect(this::checkAPIRewritten)
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+ .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class);
+ }
+
+ private void checkAPIRewritten(CodeInspector inspector) {
+ MethodSubject mainMethod = inspector.clazz(Executor.class).uniqueMethodWithName("main");
+ // Check the API calls are not using j$ types.
+ assertTrue(
+ mainMethod
+ .streamInstructions()
+ .anyMatch(
+ instr ->
+ instr.isInvokeStatic()
+ && instr.getMethod().name.toString().equals("getTimeZone")
+ && instr
+ .getMethod()
+ .proto
+ .parameters
+ .values[0]
+ .toString()
+ .equals("java.time.ZoneId")));
+ assertTrue(
+ mainMethod
+ .streamInstructions()
+ .anyMatch(
+ instr ->
+ instr.isInvokeVirtual()
+ && instr.getMethod().name.toString().equals("toZoneId")
+ && instr
+ .getMethod()
+ .proto
+ .returnType
+ .toString()
+ .equals("java.time.ZoneId")));
+ // Check the conversion instructions are present.
+ assertTrue(
+ mainMethod
+ .streamInstructions()
+ .anyMatch(
+ instr ->
+ instr.isInvokeStatic()
+ && instr.getMethod().name.toString().equals("convert")
+ && instr
+ .getMethod()
+ .proto
+ .parameters
+ .values[0]
+ .toString()
+ .equals("j$.time.ZoneId")));
+ assertTrue(
+ mainMethod
+ .streamInstructions()
+ .anyMatch(
+ instr ->
+ instr.isInvokeStatic()
+ && instr.getMethod().name.toString().equals("convert")
+ && instr
+ .getMethod()
+ .proto
+ .parameters
+ .values[0]
+ .toString()
+ .equals("java.time.ZoneId")));
+ }
+
+ static class Executor {
+ public static void main(String[] args) {
+ ZoneId zoneId = ZoneId.systemDefault();
+ // Following is a call where java.time.ZoneId is a parameter type (getTimeZone()).
+ TimeZone timeZone = TimeZone.getTimeZone(zoneId);
+ // Following is a call where java.time.ZoneId is a return type (toZoneId()).
+ System.out.println(timeZone.toZoneId().getId());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11CoreLibTestBase.java b/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11CoreLibTestBase.java
index c106bec..4be9077 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11CoreLibTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11CoreLibTestBase.java
@@ -53,13 +53,6 @@
.toArray(Path[]::new);
}
- protected static Path[] getAllFilesWithSuffixInDirectory(Path directory, String suffix)
- throws IOException {
- return Files.walk(directory)
- .filter(path -> path.toString().endsWith(suffix))
- .toArray(Path[]::new);
- }
-
protected static Path getSafeVarArgsFile() {
return ANDROID_SAFE_VAR_ARGS_LOCATION;
}
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs.json b/src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs.json
index 6b8dc10..baa351d 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs.json
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs.json
@@ -1,11 +1,12 @@
{
"configuration_format_version": 1,
- "version": "0.2.0",
+ "version": "0.3.0",
"required_compilation_api_level": 26,
"library_flags": [
{
"api_level_below_or_equal": 25,
"rewrite_prefix": {
+ "j$.time.": "java.time.",
"java.time.": "j$.time.",
"java.util.Desugar": "j$.util.Desugar"
},
@@ -71,6 +72,14 @@
"java.util.Date#toInstant": "java.util.DesugarDate",
"java.util.GregorianCalendar#from": "java.util.DesugarGregorianCalendar",
"java.util.GregorianCalendar#toZonedDateTime": "java.util.DesugarGregorianCalendar"
+ },
+ "custom_conversion": {
+ "java.time.ZonedDateTime": "j$.time.TimeConversions",
+ "java.time.LocalDate": "j$.time.TimeConversions",
+ "java.time.Duration": "j$.time.TimeConversions",
+ "java.time.ZoneId": "j$.time.TimeConversions",
+ "java.time.MonthDay": "j$.time.TimeConversions",
+ "java.time.Instant": "j$.time.TimeConversions"
}
},
{
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
index 9ea52d0..25cbe9a 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
@@ -4,9 +4,13 @@
package com.android.tools.r8.dexsplitter;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.FeatureSplit;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NeverMerge;
import com.android.tools.r8.R8FullTestBuilder;
@@ -77,6 +81,26 @@
assertTrue(processResult.stderr.contains("NoClassDefFoundError"));
}
+ @Test
+ public void testOnR8Splitter() throws IOException, CompilationFailedException,
+ ExecutionException {
+ assumeTrue(parameters.isDexRuntime());
+ Consumer<R8FullTestBuilder> configurator =
+ r8FullTestBuilder -> r8FullTestBuilder.enableMergeAnnotations().noMinification();
+ ProcessResult processResult =
+ testR8Splitter(
+ parameters,
+ ImmutableSet.of(BaseClass.class, BaseWithStatic.class),
+ ImmutableSet.of(FeatureClass.class, AFeatureWithStatic.class),
+ FeatureClass.class,
+ EXPECTED,
+ a -> true,
+ configurator);
+
+ assertEquals(processResult.exitCode, 0);
+ assertTrue(processResult.stdout.equals(StringUtils.lines("42", "foobar")));
+ }
+
@NeverMerge
public static class BaseClass implements RunInterface {
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/R8FeatureSplitTest.java b/src/test/java/com/android/tools/r8/dexsplitter/R8FeatureSplitTest.java
index 04d3b14..5622177 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/R8FeatureSplitTest.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/R8FeatureSplitTest.java
@@ -8,15 +8,19 @@
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.ExtractMarker;
import com.android.tools.r8.FeatureSplit;
+import com.android.tools.r8.ResourceException;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.file.Path;
+import java.util.Collection;
import java.util.concurrent.ExecutionException;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -60,21 +64,10 @@
@Test
public void testTwoFeatures() throws CompilationFailedException, IOException, ExecutionException {
- Path basePath = temp.newFile("base.zip").toPath();
- Path feature1Path = temp.newFile("feature1.zip").toPath();
- Path feature2Path = temp.newFile("feature2.zip").toPath();
-
- testForR8(parameters.getBackend())
- .addProgramClasses(BaseClass.class, RunInterface.class, SplitRunner.class)
- .setMinApi(parameters.getRuntime())
- .addFeatureSplit(
- builder -> simpleSplitProvider(builder, feature1Path, temp, FeatureClass.class))
- .addFeatureSplit(
- builder -> simpleSplitProvider(builder, feature2Path, temp, FeatureClass2.class))
- .addKeepAllClassesRule()
- .compile()
- .writeToZip(basePath);
-
+ CompiledWithFeature compiledWithFeature = new CompiledWithFeature().invoke();
+ Path basePath = compiledWithFeature.getBasePath();
+ Path feature1Path = compiledWithFeature.getFeature1Path();
+ Path feature2Path = compiledWithFeature.getFeature2Path();
CodeInspector baseInspector = new CodeInspector(basePath);
assertTrue(baseInspector.clazz(BaseClass.class).isPresent());
@@ -100,6 +93,24 @@
assertEquals(result.stdout, StringUtils.lines("Testing second"));
}
+ @Test
+ public void testMarkerInFeatures()
+ throws IOException, CompilationFailedException, ExecutionException, ResourceException {
+ CompiledWithFeature compiledWithFeature = new CompiledWithFeature().invoke();
+ Path basePath = compiledWithFeature.getBasePath();
+ Path feature1Path = compiledWithFeature.getFeature1Path();
+ Path feature2Path = compiledWithFeature.getFeature2Path();
+ Collection<Marker> markers = ExtractMarker.extractMarkerFromDexFile(basePath);
+ Collection<Marker> feature1Markers = ExtractMarker.extractMarkerFromDexFile(feature1Path);
+ Collection<Marker> feature2Markers = ExtractMarker.extractMarkerFromDexFile(feature2Path);
+
+ assertEquals(markers.size(), 1);
+ assertEquals(feature1Markers.size(), 1);
+ assertEquals(feature2Markers.size(), 1);
+ assertEquals(markers.iterator().next(), feature1Markers.iterator().next());
+ assertEquals(markers.iterator().next(), feature2Markers.iterator().next());
+ }
+
public static class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello world");
@@ -139,4 +150,41 @@
test();
}
}
+
+ private class CompiledWithFeature {
+
+ private Path basePath;
+ private Path feature1Path;
+ private Path feature2Path;
+
+ public Path getBasePath() {
+ return basePath;
+ }
+
+ public Path getFeature1Path() {
+ return feature1Path;
+ }
+
+ public Path getFeature2Path() {
+ return feature2Path;
+ }
+
+ public CompiledWithFeature invoke() throws IOException, CompilationFailedException {
+ basePath = temp.newFile("base.zip").toPath();
+ feature1Path = temp.newFile("feature1.zip").toPath();
+ feature2Path = temp.newFile("feature2.zip").toPath();
+
+ testForR8(parameters.getBackend())
+ .addProgramClasses(BaseClass.class, RunInterface.class, SplitRunner.class)
+ .setMinApi(parameters.getRuntime())
+ .addFeatureSplit(
+ builder -> simpleSplitProvider(builder, feature1Path, temp, FeatureClass.class))
+ .addFeatureSplit(
+ builder -> simpleSplitProvider(builder, feature2Path, temp, FeatureClass2.class))
+ .addKeepAllClassesRule()
+ .compile()
+ .writeToZip(basePath);
+ return this;
+ }
+ }
}
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java b/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
index 9d8c12b..b8154cc 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
@@ -1,6 +1,5 @@
package com.android.tools.r8.dexsplitter;
-import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assume.assumeTrue;
@@ -66,7 +65,6 @@
ByteDataView data,
Set<String> descriptors,
DiagnosticsHandler handler) {
- assertEquals(classNames.size(), descriptors.size());
for (String descriptor : descriptors) {
assertTrue(classNames.contains(DescriptorUtils.descriptorToJavaType(descriptor)));
}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/CollectionMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/CollectionMethods.java
new file mode 100644
index 0000000..0dd1a53
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/CollectionMethods.java
@@ -0,0 +1,47 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.backports;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+public class CollectionMethods {
+
+ public static <E> List<E> listOfArray(E[] elements) {
+ ArrayList<E> list = new ArrayList<>(elements.length);
+ for (E element : elements) {
+ list.add(Objects.requireNonNull(element));
+ }
+ return Collections.unmodifiableList(list);
+ }
+
+ public static <E> Set<E> setOfArray(E[] elements) {
+ HashSet<E> set = new HashSet<>(elements.length);
+ for (E element : elements) {
+ if (!set.add(Objects.requireNonNull(element))) {
+ throw new IllegalArgumentException("duplicate element: " + element);
+ }
+ }
+ return Collections.unmodifiableSet(set);
+ }
+
+ public static <K, V> Map<K, V> mapOfEntries(Map.Entry<K, V>[] elements) {
+ HashMap<K, V> map = new HashMap<>(elements.length);
+ for (Map.Entry<K, V> element : elements) {
+ K key = Objects.requireNonNull(element.getKey());
+ V value = Objects.requireNonNull(element.getValue());
+ if (map.put(key, value) != null) {
+ throw new IllegalArgumentException("duplicate key: " + key);
+ }
+ }
+ return Collections.unmodifiableMap(map);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
index 2bb7dc3..e210196 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
@@ -29,6 +29,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -73,11 +74,11 @@
ByteMethods.class,
CharacterMethods.class,
CloseResourceMethod.class,
+ CollectionMethods.class,
CollectionsMethods.class,
DoubleMethods.class,
FloatMethods.class,
IntegerMethods.class,
- ListMethods.class,
LongMethods.class,
MathMethods.class,
ObjectsMethods.class,
@@ -99,7 +100,7 @@
@Test
public void test() throws Exception {
ArrayList<Class<?>> sorted = new ArrayList<>(methodTemplateClasses);
- sorted.sort((a, b) -> a.getTypeName().compareTo(b.getTypeName()));
+ sorted.sort(Comparator.comparing(Class::getTypeName));
assertEquals("Classes should be listed in sorted order", sorted, methodTemplateClasses);
assertEquals(
FileUtils.readTextFile(backportMethodsFile, StandardCharsets.UTF_8),
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/ListMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/ListMethods.java
deleted file mode 100644
index 7d74919..0000000
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/ListMethods.java
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.desugar.backports;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-public class ListMethods {
-
- public static <E> List<E> ofArray(E[] elements) {
- // TODO(140709356): The other overloads should call into this method to ensure consistent
- // behavior, but we cannot link against List.of(E[]) because it's only available in Java 9.
- ArrayList<E> list = new ArrayList<>(elements.length);
- for (E element : elements) {
- list.add(Objects.requireNonNull(element));
- }
- return Collections.unmodifiableList(list);
- }
-}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlinesWithNonNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlinesWithNonNullTest.java
new file mode 100644
index 0000000..eabd3cc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlinesWithNonNullTest.java
@@ -0,0 +1,186 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.outliner;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.CodeMatchers;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class OutlinesWithNonNullTest extends TestBase {
+ private static final String JVM_OUTPUT = StringUtils.lines(
+ "42",
+ "arg",
+ "42",
+ "arg"
+ );
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().build();
+ }
+
+ public OutlinesWithNonNullTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testNonNullOnOneSide() throws Exception {
+ testForR8(parameters.getBackend())
+ .enableClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .addProgramClasses(TestArg.class, TestClassWithNonNullOnOneSide.class)
+ .addKeepMainRule(TestClassWithNonNullOnOneSide.class)
+ .setMinApi(parameters.getRuntime())
+ .allowAccessModification()
+ .noMinification()
+ .addOptionsModification(
+ options -> {
+ options.outline.threshold = 2;
+ options.outline.minSize = 2;
+ })
+ .compile()
+ .inspect(inspector -> validateOutlining(inspector, TestClassWithNonNullOnOneSide.class))
+ .run(parameters.getRuntime(), TestClassWithNonNullOnOneSide.class)
+ .assertSuccessWithOutput(JVM_OUTPUT);
+ }
+
+ @Test
+ public void testNonNullOnBothSides() throws Exception {
+ testForR8(parameters.getBackend())
+ .enableClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .addProgramClasses(TestArg.class, TestClassWithNonNullOnBothSides.class)
+ .addKeepMainRule(TestClassWithNonNullOnBothSides.class)
+ .setMinApi(parameters.getRuntime())
+ .allowAccessModification()
+ .noMinification()
+ .addOptionsModification(
+ options -> {
+ options.outline.threshold = 2;
+ options.outline.minSize = 2;
+ })
+ .compile()
+ .inspect(inspector -> validateOutlining(inspector, TestClassWithNonNullOnBothSides.class))
+ .run(parameters.getRuntime(), TestClassWithNonNullOnBothSides.class)
+ .assertSuccessWithOutput(JVM_OUTPUT);
+ }
+
+ private void validateOutlining(CodeInspector inspector, Class<?> main) {
+ ClassSubject outlineClass = inspector.clazz(OutlineOptions.CLASS_NAME);
+ assertThat(outlineClass, isPresent());
+ MethodSubject outlineMethod = outlineClass.uniqueMethodWithName("outline0");
+ assertThat(outlineMethod, isPresent());
+
+ ClassSubject argClass = inspector.clazz(TestArg.class);
+ assertThat(argClass, isPresent());
+ MethodSubject printHash = argClass.uniqueMethodWithName("printHash");
+ assertThat(printHash, isPresent());
+ MethodSubject printArg= argClass.uniqueMethodWithName("printArg");
+ assertThat(printArg, isPresent());
+
+ ClassSubject classSubject = inspector.clazz(main);
+ assertThat(classSubject, isPresent());
+ MethodSubject method1 = classSubject.uniqueMethodWithName("method1");
+ assertThat(method1, isPresent());
+ assertThat(method1, CodeMatchers.invokesMethod(outlineMethod));
+ assertThat(method1, not(CodeMatchers.invokesMethod(printHash)));
+ assertThat(method1, not(CodeMatchers.invokesMethod(printArg)));
+ MethodSubject method2 = classSubject.uniqueMethodWithName("method2");
+ assertThat(method2, isPresent());
+ assertThat(method2, CodeMatchers.invokesMethod(outlineMethod));
+ assertThat(method2, not(CodeMatchers.invokesMethod(printHash)));
+ assertThat(method2, not(CodeMatchers.invokesMethod(printArg)));
+ }
+
+ @NeverClassInline
+ public static class TestArg {
+ @Override
+ public int hashCode() {
+ return 42;
+ }
+
+ @Override
+ public String toString() {
+ return "arg";
+ }
+
+ @NeverInline
+ static void printHash(Object arg) {
+ if (arg == null) {
+ throw new NullPointerException();
+ }
+ System.out.println(arg.hashCode());
+ // This method guarantees that, at the normal exit, argument is not null.
+ }
+
+ @NeverInline
+ static void printArg(Object arg) {
+ System.out.println(arg);
+ }
+ }
+
+ static class TestClassWithNonNullOnOneSide {
+ @NeverInline
+ static void method1(Object arg) {
+ TestArg.printHash(arg);
+ // We will have non-null aliasing here.
+ TestArg.printArg(arg);
+ }
+
+ @NeverInline
+ static void method2(Object arg) {
+ if (arg != null) {
+ // We will have non-null aliasing here.
+ TestArg.printHash(arg);
+ TestArg.printArg(arg);
+ }
+ }
+
+ public static void main(String... args) {
+ TestArg arg = new TestArg();
+ method1(arg);
+ method2(arg);
+ }
+ }
+
+ static class TestClassWithNonNullOnBothSides {
+ @NeverInline
+ static void method1(Object arg) {
+ TestArg.printHash(arg);
+ // We will have non-null aliasing here.
+ TestArg.printArg(arg);
+ }
+
+ @NeverInline
+ static void method2(Object arg) {
+ TestArg.printHash(arg);
+ // We will have non-null aliasing here.
+ TestArg.printArg(arg);
+ }
+
+ public static void main(String... args) {
+ TestArg arg = new TestArg();
+ method1(arg);
+ method2(arg);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java b/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java
index 6c87416..3353fd2 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.memberrebinding.b135627418.library.Drawable;
import com.android.tools.r8.memberrebinding.b135627418.library.DrawableWrapper;
import com.android.tools.r8.memberrebinding.b135627418.library.InsetDrawable;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -79,11 +80,13 @@
options ->
options.desugaredLibraryConfiguration =
new DesugaredLibraryConfiguration(
+ AndroidApiLevel.B,
false,
ImmutableMap.of(packageName + ".runtime", packageName + ".library"),
ImmutableMap.of(),
ImmutableMap.of(),
ImmutableMap.of(),
+ ImmutableMap.of(),
ImmutableList.of()))
.compile();
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoMethods.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoMethods.java
new file mode 100644
index 0000000..8a67d39
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoMethods.java
@@ -0,0 +1,95 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking.keptgraph;
+
+import static com.android.tools.r8.references.Reference.methodFromMethod;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.graphinspector.GraphInspector;
+import com.android.tools.r8.utils.graphinspector.GraphInspector.QueryNode;
+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 KeptByTwoMethods extends TestBase {
+
+ @NeverMerge
+ public static class A {
+
+ @NeverInline
+ void foo() {
+ System.out.println("A.foo!");
+ }
+ }
+
+ @NeverMerge
+ public static class B extends A {
+ // Intermediate to A.
+ }
+
+ public static class TestClass {
+
+ @NeverInline
+ static void bar(B b) {
+ b.foo();
+ }
+
+ @NeverInline
+ static void baz(B b) {
+ b.foo();
+ }
+
+ public static void main(String[] args) {
+ bar(new B());
+ baz(new B());
+ }
+ }
+
+ private static final String EXPECTED = StringUtils.lines("A.foo!", "A.foo!");
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withCfRuntimes().build();
+ }
+
+ private final TestParameters parameters;
+
+ public KeptByTwoMethods(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ GraphInspector inspector =
+ testForR8(parameters.getBackend())
+ .enableGraphInspector()
+ .enableMergeAnnotations()
+ .enableInliningAnnotations()
+ .addKeepMainRule(TestClass.class)
+ .addProgramClasses(TestClass.class, A.class, B.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED)
+ .graphInspector();
+
+ MethodReference barRef = methodFromMethod(TestClass.class.getDeclaredMethod("bar", B.class));
+ inspector.method(barRef).assertPresent();
+
+ MethodReference bazRef = methodFromMethod(TestClass.class.getDeclaredMethod("baz", B.class));
+ inspector.method(bazRef).assertPresent();
+
+ QueryNode foo =
+ inspector.method(methodFromMethod(A.class.getDeclaredMethod("foo"))).assertPresent();
+
+ foo.assertInvokedFrom(barRef);
+ foo.assertInvokedFrom(bazRef);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/ListUtilsTest.java b/src/test/java/com/android/tools/r8/utils/ListUtilsTest.java
new file mode 100644
index 0000000..3a63555
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/ListUtilsTest.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+import org.junit.Test;
+
+public class ListUtilsTest {
+
+ private List<Integer> createInputData(int size) {
+ List<Integer> input = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ input.add(i);
+ }
+ return input;
+ }
+
+ @Test
+ public void lastIndexOf_outOfRange() {
+ List<Integer> input = createInputData(3);
+ Predicate<Integer> tester = x -> x * x == -1;
+ assertEquals(-1, ListUtils.lastIndexMatching(input, tester));
+ }
+
+ @Test
+ public void lastIndexOf_first() {
+ List<Integer> input = createInputData(3);
+ Predicate<Integer> tester = x -> x * x == 0;
+ assertEquals(0, ListUtils.lastIndexMatching(input, tester));
+ }
+
+ @Test
+ public void lastIndexOf_middle() {
+ List<Integer> input = createInputData(4);
+ Predicate<Integer> tester = x -> x * x == 4;
+ assertEquals(2, ListUtils.lastIndexMatching(input, tester));
+ }
+
+ @Test
+ public void lastIndexOf_last() {
+ List<Integer> input = createInputData(2);
+ Predicate<Integer> tester = x -> x * x == 1;
+ assertEquals(1, ListUtils.lastIndexMatching(input, tester));
+ }
+}
diff --git a/tools/archive.py b/tools/archive.py
index 1041b40..62e0e27 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -126,9 +126,6 @@
create_maven_release.generate_r8_maven_zip(utils.MAVEN_ZIP)
create_maven_release.generate_r8_maven_zip(
utils.MAVEN_ZIP_LIB, is_r8lib=True)
- # Create maven release of the desuage_jdk_libs configuration.
- create_maven_release.generate_desugar_configuration_maven_zip(
- utils.DESUGAR_CONFIGURATION_MAVEN_ZIP)
# Generate and copy a full build without dependencies.
gradle.RunGradleExcludeDeps([utils.R8, utils.R8_SRC])
@@ -149,6 +146,12 @@
utils.COMPATPROGUARDLIB,
'-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)
+
version = GetVersion()
is_master = IsMaster(version)
if is_master:
diff --git a/tools/create_maven_release.py b/tools/create_maven_release.py
index b4343f3..30c14c8 100755
--- a/tools/create_maven_release.py
+++ b/tools/create_maven_release.py
@@ -6,6 +6,7 @@
import argparse
import gradle
import hashlib
+import jdk
import json
from os import makedirs
from os.path import join
@@ -338,6 +339,19 @@
configuration_dir = join(tmp_dir, 'META-INF', 'desugar', 'd8')
makedirs(configuration_dir)
copyfile(configuration, join(configuration_dir, 'desugar.json'))
+
+ lint_dir = join(configuration_dir, 'lint')
+ makedirs(lint_dir)
+ cmd = [
+ jdk.GetJavaExecutable(),
+ '-cp',
+ utils.R8_JAR,
+ 'com.android.tools.r8.GenerateLintFiles',
+ configuration,
+ lint_dir]
+ utils.PrintCmd(cmd)
+ subprocess.check_call(cmd)
+
make_archive(destination, 'zip', tmp_dir)
move(destination + '.zip', destination)
diff --git a/tools/run_on_as_app.py b/tools/run_on_as_app.py
index f647f4b..89d0da2 100755
--- a/tools/run_on_as_app.py
+++ b/tools/run_on_as_app.py
@@ -64,6 +64,8 @@
# For running on Golem all third-party repositories are bundled as an x20-
# dependency and then copied to WORKING_DIR. To update the app-bundle use
# 'run_on_as_app_x20_packager.py'.
+# For showing benchmark data, also include the app in appSegmentBenchmarks in
+# the file <golem_repo>/config/r8/benchmarks.dart.
APP_REPOSITORIES = [
# ...
# Repo({
@@ -314,7 +316,8 @@
'module': 'sample/android',
'archives_base_name': 'android',
'min_sdk': 14,
- 'compile_sdk': 28
+ 'compile_sdk': 28,
+ 'skip': True
})
]
}),