Merge commit '75b469b340688d1e870d6b996c41354f38301f58' into dev-release
diff --git a/src/main/java/com/android/tools/r8/ClassFileConsumer.java b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
index e124571..807fd6e 100644
--- a/src/main/java/com/android/tools/r8/ClassFileConsumer.java
+++ b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
@@ -160,10 +160,12 @@
try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(archive, options))) {
for (ProgramResource resource : resources) {
assert resource.getClassDescriptors().size() == 1;
- String className = resource.getClassDescriptors().iterator().next();
- String entryName = getClassFileName(className);
- byte[] bytes = ByteStreams.toByteArray(closer.register(resource.getByteStream()));
- ZipUtils.writeToZipStream(out, entryName, bytes, ZipEntry.DEFLATED);
+ if (resource.getClassDescriptors() != null) {
+ String className = resource.getClassDescriptors().iterator().next();
+ String entryName = getClassFileName(className);
+ byte[] bytes = ByteStreams.toByteArray(closer.register(resource.getByteStream()));
+ ZipUtils.writeToZipStream(out, entryName, bytes, ZipEntry.DEFLATED);
+ }
}
for (DataEntryResource dataResource : dataResources) {
String entryName = dataResource.getName();
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index ed74e0b..36adabe 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -217,7 +217,6 @@
null,
options,
marker == null ? null : ImmutableList.copyOf(markers),
- app.getChecksums(),
GraphLense.getIdentityLense(),
PrefixRewritingNamingLens.createPrefixRewritingNamingLens(options, rewritePrefix),
null)
diff --git a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
index e728f6d..1ed8cac 100644
--- a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
+++ b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
@@ -100,7 +100,6 @@
null,
options,
markers,
- null,
GraphLense.getIdentityLense(),
NamingLens.getIdentityLens(),
null);
diff --git a/src/main/java/com/android/tools/r8/DexSplitterHelper.java b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
index 595e1e3..2dee6ef 100644
--- a/src/main/java/com/android/tools/r8/DexSplitterHelper.java
+++ b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
@@ -101,7 +101,6 @@
null,
options,
markers,
- null,
GraphLense.getIdentityLense(),
NamingLens.getIdentityLens(),
null,
diff --git a/src/main/java/com/android/tools/r8/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
index 3bd76a6..8ce6eed 100644
--- a/src/main/java/com/android/tools/r8/GenerateLintFiles.java
+++ b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
@@ -21,6 +21,7 @@
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.DexProgramClass.ChecksumSupplier;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.GraphLense;
@@ -155,7 +156,9 @@
DexEncodedMethod[] virtualMethodsArray = new DexEncodedMethod[virtualMethods.size()];
directMethods.toArray(directMethodsArray);
virtualMethods.toArray(virtualMethodsArray);
- builder.addProgramClass(
+ assert !options.encodeChecksums;
+ ChecksumSupplier checksumSupplier = DexProgramClass::invalidChecksumRequest;
+ DexProgramClass programClass =
new DexProgramClass(
clazz.type,
null,
@@ -173,7 +176,9 @@
DexEncodedField.EMPTY_ARRAY,
directMethodsArray,
virtualMethodsArray,
- false));
+ false,
+ checksumSupplier);
+ builder.addProgramClass(programClass);
}
public static class SupportedMethods {
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 656fee1..1837cd3 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -163,8 +163,10 @@
}
public boolean isShrinking() {
+ // TODO(b/143431384): Re-enable shrinking.
+ return false;
// Answers true if keep rules, even empty, are provided.
- return !proguardConfigStrings.isEmpty() || !proguardConfigFiles.isEmpty();
+ // return !proguardConfigStrings.isEmpty() || !proguardConfigFiles.isEmpty();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index c56ea8a..56f69f7 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -202,7 +202,6 @@
appView,
options,
Collections.singletonList(marker),
- null,
graphLense,
namingLens,
proguardMapSupplier)
diff --git a/src/main/java/com/android/tools/r8/bisect/Bisect.java b/src/main/java/com/android/tools/r8/bisect/Bisect.java
index 6b386c5..1d09319 100644
--- a/src/main/java/com/android/tools/r8/bisect/Bisect.java
+++ b/src/main/java/com/android/tools/r8/bisect/Bisect.java
@@ -186,8 +186,7 @@
StringConsumer proguardMapConsumer = options.proguardMapConsumer;
AndroidAppConsumers compatSink = new AndroidAppConsumers(options);
ApplicationWriter writer =
- new ApplicationWriter(
- app, null, options, null, null, null, NamingLens.getIdentityLens(), null);
+ new ApplicationWriter(app, null, options, null, null, NamingLens.getIdentityLens(), null);
writer.write(executor);
options.signalFinishedToConsumers();
compatSink.build().writeToDirectory(output, OutputMode.DexIndexed);
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index cfe4d9f..5629efe 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.ClassFileResourceProvider;
import com.android.tools.r8.DataResourceProvider;
+import com.android.tools.r8.OutputMode;
import com.android.tools.r8.ProgramResource;
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.ProgramResourceProvider;
@@ -42,6 +43,7 @@
import com.android.tools.r8.utils.Timing;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -59,7 +61,6 @@
private final DexItemFactory itemFactory;
private final Timing timing;
private final AndroidApp inputApp;
- private final ClassesChecksum checksums = new ClassesChecksum();
public interface ProgramClassConflictResolver {
DexProgramClass resolveClassConflict(DexProgramClass a, DexProgramClass b);
@@ -107,6 +108,10 @@
ProgramClassConflictResolver resolver)
throws IOException, ExecutionException {
assert verifyMainDexOptionsCompatible(inputApp, options);
+ if (options.dumpInputToFile != null) {
+ inputApp.writeToZip(Paths.get(options.dumpInputToFile), OutputMode.ClassFile);
+ throw options.reporter.fatalError("Dumped compilation inputs to: " + options.dumpInputToFile);
+ }
timing.begin("DexApplication.read");
final LazyLoadedDexApplication.Builder builder =
DexApplication.builder(options, timing, resolver);
@@ -124,10 +129,6 @@
ClassReader classReader = new ClassReader(executorService, futures);
JarClassFileReader jcf = classReader.readSources();
ThreadUtils.awaitFutures(futures);
- // Merge all the checksum gathered from the class file's CRC as well as the marker
- // implanted into the dex file.
- builder.mergeChecksums(jcf.getChecksums());
- builder.mergeChecksums(classReader.application.options.itemFactory.extractChecksum());
classReader.initializeLazyClassCollection(builder);
for (ProgramResourceProvider provider : inputApp.getProgramResourceProviders()) {
DataResourceProvider dataResourceProvider = provider.getDataResourceProvider();
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index ad763d8..24a33b9 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -37,7 +37,6 @@
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.ObjectToOffsetMapping;
import com.android.tools.r8.graph.ParameterAnnotationsList;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryWrapperSynthesizer;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.naming.ProguardMapSupplier;
@@ -49,12 +48,10 @@
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ThreadUtils;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
import com.google.common.collect.ObjectArrays;
-import com.google.common.primitives.Longs;
+import it.unimi.dsi.fastutil.objects.Object2LongMap;
+import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
import java.io.IOException;
-import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
@@ -66,7 +63,6 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
-import java.util.zip.CRC32;
public class ApplicationWriter {
@@ -77,7 +73,6 @@
public final InternalOptions options;
public List<Marker> markers;
public List<DexString> markerStrings;
- private final ClassesChecksum checksums;
public DexIndexedConsumer programConsumer;
public final ProguardMapSupplier proguardMapSupplier;
@@ -144,7 +139,6 @@
AppView<?> appView,
InternalOptions options,
List<Marker> markers,
- ClassesChecksum checksums,
GraphLense graphLense,
NamingLens namingLens,
ProguardMapSupplier proguardMapSupplier) {
@@ -153,7 +147,6 @@
appView,
options,
markers,
- checksums,
graphLense,
namingLens,
proguardMapSupplier,
@@ -165,7 +158,6 @@
AppView<?> appView,
InternalOptions options,
List<Marker> markers,
- ClassesChecksum checksums,
GraphLense graphLense,
NamingLens namingLens,
ProguardMapSupplier proguardMapSupplier,
@@ -176,7 +168,6 @@
assert options != null;
this.options = options;
this.markers = markers;
- this.checksums = checksums;
this.graphLense = graphLense;
this.namingLens = namingLens;
this.proguardMapSupplier = proguardMapSupplier;
@@ -210,60 +201,17 @@
* This needs to be done after distribute but before dex string sorting.
*/
private void encodeChecksums(Iterable<VirtualFile> files) {
- ImmutableMap<String, Long> inputChecksums = checksums.getChecksums();
- Map<String, Long> synthesizedChecksums = Maps.newHashMap();
- for (DexProgramClass clazz : application.classes()) {
- Collection<DexProgramClass> synthesizedFrom = clazz.getSynthesizedFrom();
-
- if (synthesizedFrom.isEmpty()) {
- if (inputChecksums.containsKey(clazz.getType().descriptor.toASCIIString())) {
- continue;
- } else {
- String name = clazz.toSourceString();
- if (name.contains(DesugaredLibraryWrapperSynthesizer.TYPE_WRAPPER_SUFFIX)
- || name.contains(DesugaredLibraryWrapperSynthesizer.VIVIFIED_TYPE_WRAPPER_SUFFIX)) {
- synthesizedChecksums.put(
- clazz.getType().descriptor.toASCIIString(), (long) name.hashCode());
- } else {
- throw new CompilationError(
- clazz
- + " from "
- + clazz.origin
- + " has no checksum information while checksum encoding is requested");
- }
- }
- }
-
- // Checksum of synthesized classes are compute based off the depending input. This might
- // create false positives (ie: unchanged lambda class detected as changed even thought only
- // an unrelated part from a synthesizedFrom class is changed).
-
- // Ideally, we should use some hashcode of the dex program class that is deterministic across
- // compiles.
- ByteBuffer buffer = ByteBuffer.allocate(synthesizedFrom.size() * Longs.BYTES);
- for (DexProgramClass from : synthesizedFrom) {
- buffer.putLong(inputChecksums.get(from.getType().descriptor.toASCIIString()));
- }
- CRC32 crc = new CRC32();
- byte[] array = buffer.array();
- crc.update(array, 0, array.length);
- synthesizedChecksums.put(clazz.getType().descriptor.toASCIIString(), crc.getValue());
+ List<DexProgramClass> classes = application.classes();
+ Object2LongMap<String> inputChecksums = new Object2LongOpenHashMap<>(classes.size());
+ for (DexProgramClass clazz : classes) {
+ inputChecksums.put(clazz.getType().descriptor.toASCIIString(), clazz.getChecksum());
}
-
- for (VirtualFile f : files) {
+ for (VirtualFile file : files) {
ClassesChecksum toWrite = new ClassesChecksum();
- for (String desc : f.getClassDescriptors()) {
- Long checksum = inputChecksums.get(desc);
- if (checksum == null) {
- checksum = synthesizedChecksums.get(desc);
- }
-
- // All classes should have a checksum from the inputChecksum (previous marker) or it was
- // computed eariler in the function. Otherwise, we would have throw an compilation error.
- assert checksum != null;
- toWrite.addChecksum(desc, checksum);
+ for (String desc : file.getClassDescriptors()) {
+ toWrite.addChecksum(desc, inputChecksums.getLong(desc));
}
- f.injectString(application.dexItemFactory.createString(toWrite.toString()));
+ file.injectString(application.dexItemFactory.createString(toWrite.toJsonString()));
}
}
@@ -295,7 +243,6 @@
if (options.encodeChecksums) {
encodeChecksums(virtualFiles);
}
-
application.dexItemFactory.sort(namingLens);
assert markers == null
|| markers.isEmpty()
diff --git a/src/main/java/com/android/tools/r8/dex/ClassesChecksum.java b/src/main/java/com/android/tools/r8/dex/ClassesChecksum.java
index d5c7a64..07d2176 100644
--- a/src/main/java/com/android/tools/r8/dex/ClassesChecksum.java
+++ b/src/main/java/com/android/tools/r8/dex/ClassesChecksum.java
@@ -1,12 +1,12 @@
package com.android.tools.r8.dex;
import com.android.tools.r8.graph.DexString;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
+import it.unimi.dsi.fastutil.objects.Object2LongMap;
+import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
import java.util.Comparator;
import java.util.Map;
@@ -16,53 +16,47 @@
private static final char PREFIX_CHAR1 = '~';
private static final char PREFIX_CHAR2 = '~';
+ private Object2LongMap<String> dictionary = null;
- // private final JsonObject dictionary;
- Map<String, Long> dictionary = Maps.newHashMap();
-
- /**
- * Checksum to be inserted.
- */
public ClassesChecksum() {
}
- private ClassesChecksum(JsonObject json) {
- json.entrySet().forEach(entry ->
- dictionary.put(entry.getKey(), Long.parseLong(entry.getValue().getAsString(), 16)));
- }
-
- public synchronized ClassesChecksum addChecksum(String classDescriptor, Long crc) {
- dictionary.put(classDescriptor, crc);
- return this;
- }
-
- public synchronized ImmutableMap<String, Long> getChecksums() {
- return ImmutableMap.copyOf(dictionary);
- }
-
- public synchronized ClassesChecksum merge(ClassesChecksum other) {
- if (other != null) {
- other.dictionary.entrySet().stream().forEach(entry -> this.dictionary.put(
- entry.getKey(), entry.getValue()));
+ private void ensureMap() {
+ if (dictionary == null) {
+ dictionary = new Object2LongOpenHashMap<>();
}
- return this;
}
- @Override
- public synchronized String toString() {
+ private void append(JsonObject json) {
+ ensureMap();
+ json.entrySet()
+ .forEach(
+ entry ->
+ dictionary.put(entry.getKey(), Long.parseLong(entry.getValue().getAsString(), 16)));
+ }
+
+ public void addChecksum(String classDescriptor, long crc) {
+ ensureMap();
+ dictionary.put(classDescriptor, crc);
+ }
+
+ public Object2LongMap<String> getChecksums() {
+ return dictionary;
+ }
+
+ public String toJsonString() {
// In order to make printing of markers deterministic we sort the entries by key.
final JsonObject sortedJson = new JsonObject();
- dictionary.entrySet()
- .stream()
+ dictionary.object2LongEntrySet().stream()
.sorted(Comparator.comparing(Map.Entry::getKey))
.forEach(
- entry -> sortedJson.addProperty(entry.getKey(), Long.toHexString(entry.getValue())));
+ entry ->
+ sortedJson.addProperty(entry.getKey(), Long.toString(entry.getLongValue(), 16)));
return "" + PREFIX_CHAR0 + PREFIX_CHAR1 + PREFIX_CHAR2 + sortedJson;
}
- // Try to parse str as a marker.
- // Returns null if parsing fails.
- public static ClassesChecksum parse(DexString dexString) {
+ // Try to parse the string as a marker and append its content if successful.
+ public void tryParseAndAppend(DexString dexString) {
if (dexString.size > 2
&& dexString.content[0] == PREFIX_CHAR0
&& dexString.content[1] == PREFIX_CHAR1
@@ -71,11 +65,10 @@
try {
JsonElement result = new JsonParser().parse(str);
if (result.isJsonObject()) {
- return new ClassesChecksum(result.getAsJsonObject());
+ append(result.getAsJsonObject());
}
} catch (JsonSyntaxException ignored) {}
}
- return null;
}
public static boolean preceedChecksumMarker(DexString string) {
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java
index 28b60a3..e722669 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -40,6 +40,8 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
@@ -60,11 +62,11 @@
import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Pair;
-import com.google.common.collect.ImmutableMap;
import com.google.common.io.ByteStreams;
import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Object2LongMap;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -87,7 +89,7 @@
private int[] stringIDs;
private final ClassKind classKind;
private final InternalOptions options;
- private ImmutableMap<String, Long> checksums;
+ private Object2LongMap<String> checksums;
public static DexSection[] parseMapFrom(Path file) throws IOException {
return parseMapFrom(Files.newInputStream(file), new PathOrigin(file));
@@ -712,9 +714,10 @@
DexEncodedMethod[] virtualMethods = DexEncodedMethod.EMPTY_ARRAY;
AnnotationsDirectory annotationsDirectory = annotationsDirectoryAt(annotationsOffsets[i]);
+ Long checksum = null;
if (checksums != null && classDataOffsets[i] != 0) {
String desc = type.descriptor.toASCIIString();
- Long checksum = checksums.get(desc);
+ checksum = checksums.getOrDefault(desc, null);
if (!options.dexClassChecksumFilter.test(desc, checksum)) {
continue;
}
@@ -752,6 +755,10 @@
AttributesAndAnnotations attrs =
new AttributesAndAnnotations(type, annotationsDirectory.clazz, options.itemFactory);
+ Long finalChecksum = checksum;
+ ChecksumSupplier checksumSupplier =
+ finalChecksum == null ? DexProgramClass::invalidChecksumRequest : c -> finalChecksum;
+
DexClass clazz =
classKind.create(
type,
@@ -770,7 +777,8 @@
instanceFields,
directMethods,
virtualMethods,
- dexItemFactory.getSkipNameValidationForTesting());
+ dexItemFactory.getSkipNameValidationForTesting(),
+ checksumSupplier);
classCollection.accept(clazz); // Update the application object.
}
}
@@ -939,23 +947,15 @@
}
private void populateChecksums() {
- ClassesChecksum checksums = null;
+ ClassesChecksum parsedChecksums = new ClassesChecksum();
for (int i = stringIDs.length - 1; i >= 0; i--) {
DexString value = indexedItems.getString(i);
- ClassesChecksum checksum = ClassesChecksum.parse(value);
- if (checksum != null) {
- if (checksums == null) {
- checksums = checksum;
- } else {
- checksums.merge(checksum);
- }
- } else if (ClassesChecksum.preceedChecksumMarker(value)) {
+ if (ClassesChecksum.preceedChecksumMarker(value)) {
break;
}
+ parsedChecksums.tryParseAndAppend(value);
}
- if (checksums != null) {
- this.checksums = checksums.getChecksums();
- }
+ this.checksums = parsedChecksums.getChecksums();
}
/**
diff --git a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
index e1c8b62..33c8dde 100644
--- a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
+++ b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
@@ -87,6 +87,7 @@
? naming.originalSignatureOf(field.field)
: FieldSignature.fromDexField(field.field);
writeAnnotations(field.annotations, ps);
+ ps.print(field.accessFlags + " ");
ps.println(fieldSignature);
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/ClassKind.java b/src/main/java/com/android/tools/r8/graph/ClassKind.java
index 6443795..14fa13e 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassKind.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassKind.java
@@ -4,7 +4,8 @@
package com.android.tools.r8.graph;
-import com.android.tools.r8.ProgramResource;
+import com.android.tools.r8.ProgramResource.Kind;
+import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
import com.android.tools.r8.origin.Origin;
import java.util.List;
import java.util.function.Consumer;
@@ -13,13 +14,89 @@
/** Kind of the application class. Can be program, classpath or library. */
public enum ClassKind {
PROGRAM(DexProgramClass::new, DexClass::isProgramClass),
- CLASSPATH(DexClasspathClass::new, DexClass::isClasspathClass),
- LIBRARY(DexLibraryClass::new, DexClass::isLibraryClass);
+ CLASSPATH(
+ (type,
+ kind,
+ origin,
+ accessFlags,
+ superType,
+ interfaces,
+ sourceFile,
+ nestHost,
+ nestMembers,
+ enclosingMember,
+ innerClasses,
+ annotations,
+ staticFields,
+ instanceFields,
+ directMethods,
+ virtualMethods,
+ skipNameValidationForTesting,
+ checksumSupplier) -> {
+ return new DexClasspathClass(
+ type,
+ kind,
+ origin,
+ accessFlags,
+ superType,
+ interfaces,
+ sourceFile,
+ nestHost,
+ nestMembers,
+ enclosingMember,
+ innerClasses,
+ annotations,
+ staticFields,
+ instanceFields,
+ directMethods,
+ virtualMethods,
+ skipNameValidationForTesting);
+ },
+ DexClass::isClasspathClass),
+ LIBRARY(
+ (type,
+ kind,
+ origin,
+ accessFlags,
+ superType,
+ interfaces,
+ sourceFile,
+ nestHost,
+ nestMembers,
+ enclosingMember,
+ innerClasses,
+ annotations,
+ staticFields,
+ instanceFields,
+ directMethods,
+ virtualMethods,
+ skipNameValidationForTesting,
+ checksumSupplier) -> {
+ return new DexLibraryClass(
+ type,
+ kind,
+ origin,
+ accessFlags,
+ superType,
+ interfaces,
+ sourceFile,
+ nestHost,
+ nestMembers,
+ enclosingMember,
+ innerClasses,
+ annotations,
+ staticFields,
+ instanceFields,
+ directMethods,
+ virtualMethods,
+ skipNameValidationForTesting);
+ },
+ DexClass::isLibraryClass);
private interface Factory {
DexClass create(
DexType type,
- ProgramResource.Kind kind,
+ Kind kind,
Origin origin,
ClassAccessFlags accessFlags,
DexType superType,
@@ -34,7 +111,8 @@
DexEncodedField[] instanceFields,
DexEncodedMethod[] directMethods,
DexEncodedMethod[] virtualMethods,
- boolean skipNameValidationForTesting);
+ boolean skipNameValidationForTesting,
+ ChecksumSupplier checksumSupplier);
}
private final Factory factory;
@@ -47,7 +125,7 @@
public DexClass create(
DexType type,
- ProgramResource.Kind kind,
+ Kind kind,
Origin origin,
ClassAccessFlags accessFlags,
DexType superType,
@@ -62,7 +140,8 @@
DexEncodedField[] instanceFields,
DexEncodedMethod[] directMethods,
DexEncodedMethod[] virtualMethods,
- boolean skipNameValidationForTesting) {
+ boolean skipNameValidationForTesting,
+ ChecksumSupplier checksumSupplier) {
return factory.create(
type,
kind,
@@ -80,7 +159,8 @@
instanceFields,
directMethods,
virtualMethods,
- skipNameValidationForTesting);
+ skipNameValidationForTesting,
+ checksumSupplier);
}
public boolean isOfKind(DexClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplication.java b/src/main/java/com/android/tools/r8/graph/DexApplication.java
index c2375c5..29af63e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.DataResourceProvider;
import com.android.tools.r8.dex.ApplicationReader.ProgramClassConflictResolver;
-import com.android.tools.r8.dex.ClassesChecksum;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.utils.InternalOptions;
@@ -35,7 +34,6 @@
public final InternalOptions options;
public final DexItemFactory dexItemFactory;
- public final ClassesChecksum checksums;
// Information on the lexicographically largest string referenced from code.
public final DexString highestSortingString;
@@ -45,14 +43,12 @@
ClassNameMapper proguardMap,
ImmutableList<DataResourceProvider> dataResourceProviders,
ImmutableSet<DexType> mainDexList,
- ClassesChecksum checksums,
InternalOptions options,
DexString highestSortingString,
Timing timing) {
this.proguardMap = proguardMap;
this.dataResourceProviders = dataResourceProviders;
this.mainDexList = mainDexList;
- this.checksums = checksums;
this.options = options;
this.dexItemFactory = options.itemFactory;
this.highestSortingString = highestSortingString;
@@ -108,10 +104,6 @@
return classes;
}
- public ClassesChecksum getChecksums() {
- return checksums;
- }
-
public abstract DexClass definitionFor(DexType type);
public abstract DexProgramClass programDefinitionFor(DexType type);
@@ -137,7 +129,6 @@
public final InternalOptions options;
public final DexItemFactory dexItemFactory;
- protected ClassesChecksum checksums;
ClassNameMapper proguardMap;
final Timing timing;
@@ -150,7 +141,6 @@
this.dexItemFactory = options.itemFactory;
this.timing = timing;
this.synthesizedClasses = new ArrayList<>();
- this.checksums = null;
}
abstract T self();
@@ -165,7 +155,6 @@
dexItemFactory = application.dexItemFactory;
mainDexList.addAll(application.mainDexList);
synthesizedClasses = new ArrayList<>();
- checksums = application.checksums;
}
public synchronized T setProguardMap(ClassNameMapper proguardMap) {
@@ -224,15 +213,6 @@
return this;
}
- public Builder<T> mergeChecksums(ClassesChecksum other) {
- if (checksums == null) {
- checksums = other;
- } else {
- checksums.merge(other);
- }
- return this;
- }
-
public abstract DexApplication build();
}
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 ee50ef0..7ba3f89 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -6,7 +6,6 @@
import static com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement.computeLeastUpperBoundOfInterfaces;
import static com.google.common.base.Predicates.alwaysTrue;
-import com.android.tools.r8.dex.ClassesChecksum;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.graph.DexDebugEvent.AdvanceLine;
@@ -49,7 +48,6 @@
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
@@ -1220,11 +1218,6 @@
return markers;
}
- public synchronized ClassesChecksum extractChecksum() {
- return strings.keySet().stream().map(s -> ClassesChecksum.parse(s)).filter(Objects::nonNull)
- .reduce(null, (s1, s2) -> s1 == null ? s2 : s1.merge(s2));
- }
-
synchronized public DexType createType(DexString descriptor) {
assert !sorted;
assert descriptor != null;
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 7bfe2a6..92a4963 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.kotlin.KotlinInfo;
import com.android.tools.r8.origin.Origin;
import java.util.ArrayList;
@@ -20,6 +21,12 @@
import java.util.function.Supplier;
public class DexProgramClass extends DexClass implements Supplier<DexProgramClass> {
+
+ @FunctionalInterface
+ public interface ChecksumSupplier {
+ long getChecksum(DexProgramClass programClass);
+ }
+
public static final DexProgramClass[] EMPTY_ARRAY = {};
private static final DexEncodedArray SENTINEL_NOT_YET_COMPUTED =
@@ -31,9 +38,11 @@
private int initialClassFileVersion = -1;
private KotlinInfo kotlinInfo = null;
+ private final ChecksumSupplier checksumSupplier;
+
public DexProgramClass(
DexType type,
- ProgramResource.Kind originKind,
+ Kind originKind,
Origin origin,
ClassAccessFlags accessFlags,
DexType superType,
@@ -48,7 +57,8 @@
DexEncodedField[] instanceFields,
DexEncodedMethod[] directMethods,
DexEncodedMethod[] virtualMethods,
- boolean skipNameValidationForTesting) {
+ boolean skipNameValidationForTesting,
+ ChecksumSupplier checksumSupplier) {
this(
type,
originKind,
@@ -67,12 +77,13 @@
directMethods,
virtualMethods,
skipNameValidationForTesting,
+ checksumSupplier,
Collections.emptyList());
}
public DexProgramClass(
DexType type,
- ProgramResource.Kind originKind,
+ Kind originKind,
Origin origin,
ClassAccessFlags accessFlags,
DexType superType,
@@ -88,6 +99,7 @@
DexEncodedMethod[] directMethods,
DexEncodedMethod[] virtualMethods,
boolean skipNameValidationForTesting,
+ ChecksumSupplier checksumSupplier,
Collection<DexProgramClass> synthesizedDirectlyFrom) {
super(
sourceFile,
@@ -106,8 +118,10 @@
classAnnotations,
origin,
skipNameValidationForTesting);
+ assert checksumSupplier != null;
assert classAnnotations != null;
this.originKind = originKind;
+ this.checksumSupplier = checksumSupplier;
this.synthesizedFrom = new HashSet<>();
synthesizedDirectlyFrom.forEach(this::addSynthesizedFrom);
}
@@ -471,4 +485,17 @@
}
};
}
+
+ public static long invalidChecksumRequest(DexProgramClass clazz) {
+ throw new CompilationError(
+ clazz + " has no checksum information while checksum encoding is requested", clazz.origin);
+ }
+
+ public static long checksumFromType(DexProgramClass clazz) {
+ return clazz.type.hashCode();
+ }
+
+ public long getChecksum() {
+ return checksumSupplier.getChecksum(this);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
index efe63f6..6308a16 100644
--- a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
@@ -7,7 +7,6 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.DataResourceProvider;
-import com.android.tools.r8.dex.ClassesChecksum;
import com.android.tools.r8.graph.LazyLoadedDexApplication.AllClasses;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.utils.InternalOptions;
@@ -41,7 +40,6 @@
ImmutableList<DexLibraryClass> libraryClasses,
ImmutableList<DataResourceProvider> dataResourceProviders,
ImmutableSet<DexType> mainDexList,
- ClassesChecksum checksums,
InternalOptions options,
DexString highestSortingString,
Timing timing) {
@@ -49,7 +47,6 @@
proguardMap,
dataResourceProviders,
mainDexList,
- checksums,
options,
highestSortingString,
timing);
@@ -203,7 +200,6 @@
libraryClasses,
ImmutableList.copyOf(dataResourceProviders),
ImmutableSet.copyOf(mainDexList),
- checksums,
options,
highestSortingString,
timing);
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 0fcb5b6..da8d8d7 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -12,10 +12,10 @@
import static org.objectweb.asm.Opcodes.V9;
import com.android.tools.r8.ProgramResource.Kind;
-import com.android.tools.r8.dex.ClassesChecksum;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
import com.android.tools.r8.graph.DexValue.DexValueAnnotation;
import com.android.tools.r8.graph.DexValue.DexValueArray;
import com.android.tools.r8.graph.DexValue.DexValueBoolean;
@@ -75,13 +75,11 @@
private final JarApplicationReader application;
private final Consumer<DexClass> classConsumer;
- private final ClassesChecksum checksums;
public JarClassFileReader(
JarApplicationReader application, Consumer<DexClass> classConsumer) {
this.application = application;
this.classConsumer = classConsumer;
- this.checksums = new ClassesChecksum();
}
public void read(Origin origin, ClassKind classKind, InputStream input) throws IOException {
@@ -116,8 +114,9 @@
parsingOptions |= SKIP_DEBUG;
}
}
- reader.accept(new CreateDexClassVisitor(
- origin, classKind, reader.b, application, classConsumer, checksums), parsingOptions);
+ reader.accept(
+ new CreateDexClassVisitor(origin, classKind, reader.b, application, classConsumer),
+ parsingOptions);
// Read marker.
if (reader.getItemCount() > CfApplicationWriter.MARKER_STRING_CONSTANT_POOL_INDEX
@@ -136,10 +135,6 @@
}
}
- public ClassesChecksum getChecksums() {
- return checksums;
- }
-
private static int cleanAccessFlags(int access) {
// Clear the "synthetic attribute" and "deprecated" attribute-flags if present.
return access & ~ACC_SYNTHETIC_ATTRIBUTE & ~ACC_DEPRECATED;
@@ -218,22 +213,19 @@
private final List<DexEncodedMethod> virtualMethods = new ArrayList<>();
private final Set<Wrapper<DexMethod>> methodSignatures = new HashSet<>();
private boolean hasReachabilitySensitiveMethod = false;
- private ClassesChecksum checksums;
public CreateDexClassVisitor(
Origin origin,
ClassKind classKind,
byte[] classCache,
JarApplicationReader application,
- Consumer<DexClass> classConsumer,
- ClassesChecksum checksums) {
+ Consumer<DexClass> classConsumer) {
super(ASM_VERSION);
this.origin = origin;
this.classKind = classKind;
this.classConsumer = classConsumer;
this.context.classCache = classCache;
this.application = application;
- this.checksums = checksums;
}
@Override
@@ -419,7 +411,8 @@
instanceFields.toArray(DexEncodedField.EMPTY_ARRAY),
directMethods.toArray(DexEncodedMethod.EMPTY_ARRAY),
virtualMethods.toArray(DexEncodedMethod.EMPTY_ARRAY),
- application.getFactory().getSkipNameValidationForTesting());
+ application.getFactory().getSkipNameValidationForTesting(),
+ getChecksumSupplier(classKind));
InnerClassAttribute innerClassAttribute = clazz.getInnerClassAttributeForThisClass();
// A member class should not be a local or anonymous class.
if (innerClassAttribute != null && innerClassAttribute.getOuter() != null) {
@@ -453,16 +446,22 @@
context.owner = clazz;
}
if (clazz.isProgramClass()) {
- clazz.asProgramClass().setInitialClassFileVersion(version);
- if (application.options.encodeChecksums) {
- CRC32 crc = new CRC32();
- crc.update(this.context.classCache, 0, this.context.classCache.length);
- checksums.addChecksum(type.descriptor.toASCIIString(), crc.getValue());
- }
+ DexProgramClass programClass = clazz.asProgramClass();
+ programClass.setInitialClassFileVersion(version);
}
classConsumer.accept(clazz);
}
+ private ChecksumSupplier getChecksumSupplier(ClassKind classKind) {
+ if (application.options.encodeChecksums && classKind == ClassKind.PROGRAM) {
+ CRC32 crc = new CRC32();
+ crc.update(this.context.classCache, 0, this.context.classCache.length);
+ final long value = crc.getValue();
+ return clazz -> value;
+ }
+ return DexProgramClass::invalidChecksumRequest;
+ }
+
private void checkName(String name) {
if (!application.getFactory().getSkipNameValidationForTesting()
&& !DexString.isValidSimpleName(application.options.minApiLevel, name)) {
diff --git a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
index 0458321..24ec3c3 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.DataResourceProvider;
import com.android.tools.r8.dex.ApplicationReader.ProgramClassConflictResolver;
-import com.android.tools.r8.dex.ClassesChecksum;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.utils.ClasspathClassCollection;
import com.android.tools.r8.utils.InternalOptions;
@@ -36,7 +35,6 @@
ClasspathClassCollection classpathClasses,
LibraryClassCollection libraryClasses,
ImmutableSet<DexType> mainDexList,
- ClassesChecksum checksum,
InternalOptions options,
DexString highestSortingString,
Timing timing) {
@@ -44,7 +42,6 @@
proguardMap,
dataResourceProviders,
mainDexList,
- checksum,
options,
highestSortingString,
timing);
@@ -236,7 +233,6 @@
classpathClasses,
libraryClasses,
ImmutableSet.copyOf(mainDexList),
- checksums,
options,
highestSortingString,
timing);
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 d068677..0bdfaf1 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
@@ -864,7 +864,7 @@
// unused out-values.
codeRewriter.rewriteMoveResult(code);
deadCodeRemover.run(code);
- codeRewriter.removeAssumeInstructions(code);
+ CodeRewriter.removeAssumeInstructions(appView, code);
consumer.accept(code, method);
return null;
}));
@@ -1158,13 +1158,6 @@
serviceLoaderRewriter.rewrite(code);
}
- if (classStaticizer != null) {
- classStaticizer.fixupMethodCode(method, code);
- assert code.isConsistentSSA();
- }
-
- previous = printMethod(code, "IR after class staticizer (SSA)", previous);
-
if (identifierNameStringMarker != null) {
identifierNameStringMarker.decoupleIdentifierNameStringsInMethod(method, code);
assert code.isConsistentSSA();
@@ -1424,17 +1417,11 @@
appView.callSiteOptimizationInfoPropagator().collectCallSiteOptimizationInfo(code);
}
- // Compute optimization info summary for the current method unless it is pinned
- // (in that case we should not be making any assumptions about the behavior of the method).
- if (!appView.appInfo().withLiveness().isPinned(method.method)) {
- methodOptimizationInfoCollector
- .collectMethodOptimizationInfo(method, code, feedback, dynamicTypeOptimization);
- FieldValueAnalysis.run(appView, code, feedback, method);
- }
+ collectOptimizationInfo(code, feedback);
}
if (aliasIntroducer != null || nonNullTracker != null || dynamicTypeOptimization != null) {
- codeRewriter.removeAssumeInstructions(code);
+ CodeRewriter.removeAssumeInstructions(appView, code);
assert code.isConsistentSSA();
}
@@ -1470,7 +1457,18 @@
finalizeIR(method, code, feedback);
}
- private void finalizeIR(DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) {
+ // Compute optimization info summary for the current method unless it is pinned
+ // (in that case we should not be making any assumptions about the behavior of the method).
+ public void collectOptimizationInfo(IRCode code, OptimizationFeedback feedback) {
+ if (appView.appInfo().withLiveness().isPinned(code.method.method)) {
+ return;
+ }
+ methodOptimizationInfoCollector
+ .collectMethodOptimizationInfo(code.method, code, feedback, dynamicTypeOptimization);
+ FieldValueAnalysis.run(appView, code, feedback, code.method);
+ }
+
+ public void finalizeIR(DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) {
code.traceBlocks();
if (options.isGeneratingClassFiles()) {
finalizeToCf(method, code, feedback);
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 bf82f85..5edd98e 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
@@ -17,6 +17,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
@@ -48,6 +49,7 @@
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
@@ -232,9 +234,10 @@
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
- new DexEncodedMethod[]{dexEncodedMethod},
+ new DexEncodedMethod[] {dexEncodedMethod},
DexEncodedMethod.EMPTY_ARRAY,
factory.getSkipNameValidationForTesting(),
+ getChecksumSupplier(dexEncodedMethod),
referencingClasses);
boolean addToMainDexList =
referencingClasses.stream()
@@ -246,6 +249,14 @@
}
}
+ private ChecksumSupplier getChecksumSupplier(DexEncodedMethod method) {
+ if (!appView.options().encodeChecksums) {
+ return DexProgramClass::invalidChecksumRequest;
+ }
+ long hash = Objects.hash(method, method.getCode());
+ return c -> hash;
+ }
+
private MethodProvider getMethodProviderOrNull(DexMethod method) {
DexMethod original = appView.graphLense().getOriginalMethodSignature(method);
assert original != null;
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 cbdef95..b7e8d31 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
@@ -169,8 +169,12 @@
}
DexEncodedMethod dexEncodedMethod = dexClass.lookupVirtualMethod(method);
if (dexEncodedMethod != null) {
- if (appView.options().desugaredLibraryConfiguration.getEmulateLibraryInterface()
- .containsKey(dexClass.type)) {
+ if (appView
+ .options()
+ .desugaredLibraryConfiguration
+ .getEmulateLibraryInterface()
+ .containsKey(dexClass.type)
+ || appView.rewritePrefix.hasRewrittenType(dexClass.type)) {
return false;
}
foundOverrideToRewrite = true;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
index 2bf43f2..e40252f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
@@ -19,6 +19,7 @@
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.DexProgramClass.ChecksumSupplier;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.FieldAccessFlags;
@@ -254,9 +255,24 @@
}, // Conversions methods will be added later.
virtualMethods,
factory.getSkipNameValidationForTesting(),
+ getChecksumSupplier(this, clazz.type),
Collections.emptyList());
}
+ private ChecksumSupplier getChecksumSupplier(DesugaredLibraryWrapperSynthesizer synthesizer, DexType keyType) {
+ return clazz -> {
+ // The synthesized type wrappers are constructed lazily, so their lookup must be delayed
+ // until the point the checksum is requested (at write time). The presence of a wrapper
+ // affects the implementation of the conversion functions, so they must be accounted for in
+ // the checksum.
+ boolean hasWrapper = synthesizer.typeWrappers.containsKey(keyType);
+ boolean hasViviWrapper = synthesizer.vivifiedTypeWrappers.containsKey(keyType);
+ return ((long) clazz.type.hashCode())
+ + 7 * (long) Boolean.hashCode(hasWrapper)
+ + 11 * (long) Boolean.hashCode(hasViviWrapper);
+ };
+ }
+
private DexEncodedMethod[] synthesizeVirtualMethodsForVivifiedTypeWrapper(
DexLibraryClass dexClass, DexEncodedField wrapperField) {
List<DexEncodedMethod> dexMethods = allImplementedMethods(dexClass);
@@ -470,29 +486,37 @@
void finalizeWrappers(
DexApplication.Builder<?> builder, IRConverter irConverter, ExecutorService executorService)
throws ExecutionException {
+ assert verifyAllClassesGenerated();
+ // We register first, then optimize to avoid warnings due to missing parameter types.
+ registerWrappers(builder, typeWrappers);
+ registerWrappers(builder, vivifiedTypeWrappers);
+ finalizeWrappers(irConverter, executorService, typeWrappers, this::generateTypeConversions);
finalizeWrappers(
- builder, irConverter, executorService, typeWrappers, this::generateTypeConversions);
- finalizeWrappers(
- builder,
irConverter,
executorService,
vivifiedTypeWrappers,
this::generateVivifiedTypeConversions);
}
+ private void registerWrappers(
+ DexApplication.Builder<?> builder, Map<DexType, Pair<DexType, DexProgramClass>> wrappers) {
+ for (DexType type : wrappers.keySet()) {
+ DexProgramClass pgrmClass = wrappers.get(type).getSecond();
+ assert pgrmClass != null;
+ registerSynthesizedClass(pgrmClass, builder);
+ }
+ }
+
private void finalizeWrappers(
- DexApplication.Builder<?> builder,
IRConverter irConverter,
ExecutorService executorService,
Map<DexType, Pair<DexType, DexProgramClass>> wrappers,
BiConsumer<DexType, DexProgramClass> generateConversions)
throws ExecutionException {
- assert verifyAllClassesGenerated();
for (DexType type : wrappers.keySet()) {
DexProgramClass pgrmClass = wrappers.get(type).getSecond();
assert pgrmClass != null;
generateConversions.accept(type, pgrmClass);
- registerSynthesizedClass(pgrmClass, builder);
irConverter.optimizeSynthesizedClass(pgrmClass, executorService);
}
}
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 b79a316..9d6e613 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
@@ -735,6 +735,7 @@
emulationMethods.toArray(DexEncodedMethod.EMPTY_ARRAY),
DexEncodedMethod.EMPTY_ARRAY,
factory.getSkipNameValidationForTesting(),
+ DexProgramClass::checksumFromType,
Collections.singletonList(theInterface));
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index 8a990f2..8b8ef05 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -21,6 +21,7 @@
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.DexProgramClass.ChecksumSupplier;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
@@ -190,10 +191,19 @@
companionMethods.toArray(DexEncodedMethod.EMPTY_ARRAY),
DexEncodedMethod.EMPTY_ARRAY,
rewriter.factory.getSkipNameValidationForTesting(),
+ getChecksumSupplier(iface),
Collections.singletonList(iface));
syntheticClasses.put(iface.type, companionClass);
}
+ private ChecksumSupplier getChecksumSupplier(DexProgramClass iface) {
+ if (!appView.options().encodeChecksums) {
+ return DexProgramClass::invalidChecksumRequest;
+ }
+ long checksum = iface.getChecksum();
+ return c -> 7 * checksum;
+ }
+
List<DexEncodedMethod> process(DexLibraryClass iface, Set<DexProgramClass> callers) {
assert iface.isInterface();
@@ -263,6 +273,7 @@
dispatchMethods.toArray(DexEncodedMethod.EMPTY_ARRAY),
DexEncodedMethod.EMPTY_ARRAY,
rewriter.factory.getSkipNameValidationForTesting(),
+ DexProgramClass::checksumFromType,
callers);
syntheticClasses.put(iface.type, dispatchClass);
return dispatchMethods;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index a397cd7..40932cb 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -30,12 +30,15 @@
import com.android.tools.r8.ir.synthetic.SynthesizedCode;
import com.android.tools.r8.origin.SynthesizedOrigin;
import com.google.common.base.Suppliers;
+import com.google.common.primitives.Longs;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
+import java.util.zip.CRC32;
/**
* Represents lambda class generated for a lambda descriptor in context of lambda instantiation
@@ -167,7 +170,8 @@
synthesizeInstanceFields(),
synthesizeDirectMethods(),
synthesizeVirtualMethods(mainMethod),
- rewriter.factory.getSkipNameValidationForTesting());
+ rewriter.factory.getSkipNameValidationForTesting(),
+ LambdaClass::computeChecksumForSynthesizedClass);
// Optimize main method.
rewriter.converter.appView.appInfo().addSynthesizedClass(clazz);
rewriter.converter.optimizeSynthesizedMethod(clazz.lookupVirtualMethod(mainMethod));
@@ -180,6 +184,24 @@
return clazz;
}
+ private static long computeChecksumForSynthesizedClass(DexProgramClass clazz) {
+ // Checksum of synthesized classes are compute based off the depending input. This might
+ // create false positives (ie: unchanged lambda class detected as changed even thought only
+ // an unrelated part from a synthesizedFrom class is changed).
+
+ // Ideally, we should use some hashcode of the dex program class that is deterministic across
+ // compiles.
+ Collection<DexProgramClass> synthesizedFrom = clazz.getSynthesizedFrom();
+ ByteBuffer buffer = ByteBuffer.allocate(synthesizedFrom.size() * Longs.BYTES);
+ for (DexProgramClass from : synthesizedFrom) {
+ buffer.putLong(from.getChecksum());
+ }
+ CRC32 crc = new CRC32();
+ byte[] array = buffer.array();
+ crc.update(array, 0, array.length);
+ return crc.getValue();
+ }
+
final DexField getCaptureField(int index) {
return rewriter.factory.createField(this.type,
descriptor.captures.values[index], rewriter.factory.createString("f$" + index));
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
index dc9c9a0..ff95053 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
@@ -164,7 +164,8 @@
DexEncodedField.EMPTY_ARRAY,
DexEncodedMethod.EMPTY_ARRAY,
DexEncodedMethod.EMPTY_ARRAY,
- appView.dexItemFactory().getSkipNameValidationForTesting());
+ appView.dexItemFactory().getSkipNameValidationForTesting(),
+ DexProgramClass::checksumFromType);
}
void synthesizeNestConstructor(DexApplication.Builder<?> builder) {
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 d746bf1..f43c7c6 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
@@ -152,6 +152,7 @@
new DexEncodedMethod[] {method},
DexEncodedMethod.EMPTY_ARRAY,
appView.dexItemFactory().getSkipNameValidationForTesting(),
+ DexProgramClass::checksumFromType,
referencingClasses);
// Process created class and method.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
index f93d6c5..29fa90f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
@@ -177,11 +177,12 @@
if (originalArg.hasLocalInfo() || !originalArg.getTypeLattice().isReference()) {
continue;
}
- TypeLatticeElement dynamicType = callSiteOptimizationInfo.getDynamicType(argumentsSeen - 1);
- if (dynamicType == null) {
+ TypeLatticeElement dynamicUpperBoundType =
+ callSiteOptimizationInfo.getDynamicUpperBoundType(argumentsSeen - 1);
+ if (dynamicUpperBoundType == null) {
continue;
}
- if (dynamicType.isDefinitelyNull()) {
+ if (dynamicUpperBoundType.isDefinitelyNull()) {
ConstNumber nullInstruction = code.createConstNull();
nullInstruction.setPosition(instr.getPosition());
affectedValues.addAll(originalArg.affectedValues());
@@ -191,20 +192,20 @@
}
// TODO(b/69963623): Handle other kinds of constants, e.g. number, string, or class.
Value specializedArg;
- if (dynamicType.strictlyLessThan(originalArg.getTypeLattice(), appView)) {
+ if (dynamicUpperBoundType.strictlyLessThan(originalArg.getTypeLattice(), appView)) {
specializedArg = code.createValue(originalArg.getTypeLattice());
affectedValues.addAll(originalArg.affectedValues());
originalArg.replaceUsers(specializedArg);
Assume<DynamicTypeAssumption> assumeType =
Assume.createAssumeDynamicTypeInstruction(
- dynamicType, null, specializedArg, originalArg, instr, appView);
+ dynamicUpperBoundType, null, specializedArg, originalArg, instr, appView);
assumeType.setPosition(instr.getPosition());
assumeInstructions.add(assumeType);
} else {
specializedArg = originalArg;
}
assert specializedArg != null && specializedArg.getTypeLattice().isReference();
- if (dynamicType.isDefinitelyNotNull()) {
+ if (dynamicUpperBoundType.isDefinitelyNotNull()) {
// If we already knew `arg` is never null, e.g., receiver, skip adding non-null.
if (!specializedArg.getTypeLattice().isDefinitelyNotNull()) {
Value nonNullArg = code.createValue(
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 a47ba2c..8631814 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
@@ -157,7 +157,7 @@
this.dexItemFactory = appView.dexItemFactory();
}
- public void removeAssumeInstructions(IRCode code) {
+ public static void removeAssumeInstructions(AppView<?> appView, IRCode code) {
// We need to update the types of all values whose definitions depend on a non-null value.
// This is needed to preserve soundness of the types after the Assume<NonNullAssumption>
// instructions have been removed.
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 c681571..0795424 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
@@ -19,6 +19,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
@@ -1353,27 +1354,29 @@
DexTypeList interfaces = DexTypeList.empty();
DexString sourceFile = appView.dexItemFactory().createString("outline");
ClassAccessFlags accessFlags = ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC);
- DexProgramClass clazz =
- new DexProgramClass(
- type,
- null,
- new SynthesizedOrigin("outlining", getClass()),
- accessFlags,
- superType,
- interfaces,
- sourceFile,
- null,
- Collections.emptyList(),
- null,
- Collections.emptyList(),
- // TODO: Build dex annotations structure.
- DexAnnotationSet.empty(),
- DexEncodedField.EMPTY_ARRAY, // Static fields.
- DexEncodedField.EMPTY_ARRAY, // Instance fields.
- direct,
- DexEncodedMethod.EMPTY_ARRAY, // Virtual methods.
- appView.dexItemFactory().getSkipNameValidationForTesting());
- return clazz;
+ assert !appView.options().encodeChecksums;
+ // The outliner is R8 only and checksum is not a supported part of R8 compilation.
+ ChecksumSupplier checksumSupplier = DexProgramClass::invalidChecksumRequest;
+ return new DexProgramClass(
+ type,
+ null,
+ new SynthesizedOrigin("outlining", getClass()),
+ accessFlags,
+ superType,
+ interfaces,
+ sourceFile,
+ null,
+ Collections.emptyList(),
+ null,
+ Collections.emptyList(),
+ // TODO: Build dex annotations structure.
+ DexAnnotationSet.empty(),
+ DexEncodedField.EMPTY_ARRAY, // Static fields.
+ DexEncodedField.EMPTY_ARRAY, // Instance fields.
+ direct,
+ DexEncodedMethod.EMPTY_ARRAY, // Virtual methods.
+ appView.dexItemFactory().getSkipNameValidationForTesting(),
+ checksumSupplier);
}
private List<Outline> selectOutlines() {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
index 7830f6e..451788f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
@@ -184,6 +185,8 @@
DexType serviceLoaderType =
appView.dexItemFactory().createType("L" + SERVICE_LOADER_CLASS_NAME + ";");
if (synthesizedClass == null) {
+ assert !appView.options().encodeChecksums;
+ ChecksumSupplier checksumSupplier = DexProgramClass::invalidChecksumRequest;
synthesizedClass =
new DexProgramClass(
serviceLoaderType,
@@ -203,7 +206,8 @@
DexEncodedField.EMPTY_ARRAY, // Instance fields.
DexEncodedMethod.EMPTY_ARRAY,
DexEncodedMethod.EMPTY_ARRAY, // Virtual methods.
- appView.dexItemFactory().getSkipNameValidationForTesting());
+ appView.dexItemFactory().getSkipNameValidationForTesting(),
+ checksumSupplier);
appView.appInfo().addSynthesizedClass(synthesizedClass);
}
DexProto proto = appView.dexItemFactory().createProto(appView.dexItemFactory().iteratorType);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java
index 8ad2ea0..d82f6c5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java
@@ -37,7 +37,9 @@
}
// The index exactly matches with in values of invocation, i.e., even including receiver.
- public abstract TypeLatticeElement getDynamicType(int argIndex);
+ public abstract TypeLatticeElement getDynamicUpperBoundType(int argIndex);
+
+ // TODO(b/139246447): dynamic lower bound type?
// TODO(b/69963623): collect constants and if they're all same, propagate it to the callee.
// then, we need to re-run unused argument removal?
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultCallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultCallSiteOptimizationInfo.java
index 0e48208..e67ec20 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultCallSiteOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultCallSiteOptimizationInfo.java
@@ -17,7 +17,7 @@
}
@Override
- public TypeLatticeElement getDynamicType(int argIndex) {
+ public TypeLatticeElement getDynamicUpperBoundType(int argIndex) {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableCallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableCallSiteOptimizationInfo.java
index 64b6bac..c18624e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableCallSiteOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableCallSiteOptimizationInfo.java
@@ -6,13 +6,13 @@
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.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
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.Value;
-import com.android.tools.r8.utils.StringUtils;
-import java.util.Arrays;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -30,15 +30,11 @@
private static class ArgumentCollection {
- TypeLatticeElement[] dynamicTypes;
+ private final int size;
+ private final Int2ReferenceArrayMap<TypeLatticeElement> dynamicUpperBoundTypes;
private static final ArgumentCollection BOTTOM = new ArgumentCollection() {
@Override
- TypeLatticeElement getDynamicType(int index) {
- return TypeLatticeElement.BOTTOM;
- }
-
- @Override
public int hashCode() {
return System.identityHashCode(this);
}
@@ -49,24 +45,21 @@
}
};
- private ArgumentCollection() {}
+ // Only used to create a canonical BOTTOM.
+ private ArgumentCollection() {
+ this.size = -1;
+ this.dynamicUpperBoundTypes = null;
+ }
ArgumentCollection(int size) {
- this.dynamicTypes = new TypeLatticeElement[size];
- Arrays.fill(this.dynamicTypes, TypeLatticeElement.BOTTOM);
+ this.size = size;
+ this.dynamicUpperBoundTypes = new Int2ReferenceArrayMap<>(size);
}
- TypeLatticeElement getDynamicType(int index) {
- assert dynamicTypes != null;
- assert 0 <= index && index < dynamicTypes.length;
- return dynamicTypes[index];
- }
-
- ArgumentCollection copy() {
- ArgumentCollection copy = new ArgumentCollection();
- copy.dynamicTypes = new TypeLatticeElement[this.dynamicTypes.length];
- System.arraycopy(this.dynamicTypes, 0, copy.dynamicTypes, 0, this.dynamicTypes.length);
- return copy;
+ TypeLatticeElement getDynamicUpperBoundType(int index) {
+ assert dynamicUpperBoundTypes != null;
+ assert 0 <= index && index < size;
+ return dynamicUpperBoundTypes.getOrDefault(index, null);
}
ArgumentCollection join(ArgumentCollection other, AppView<?> appView) {
@@ -76,10 +69,21 @@
if (this == BOTTOM) {
return other;
}
- assert this.dynamicTypes.length == other.dynamicTypes.length;
- ArgumentCollection result = this.copy();
- for (int i = 0; i < result.dynamicTypes.length; i++) {
- result.dynamicTypes[i] = result.dynamicTypes[i].join(other.dynamicTypes[i], appView);
+ assert this.size == other.size;
+ ArgumentCollection result = new ArgumentCollection(this.size);
+ assert result.dynamicUpperBoundTypes != null;
+ for (int i = 0; i < result.size; i++) {
+ TypeLatticeElement thisUpperBoundType = this.getDynamicUpperBoundType(i);
+ if (thisUpperBoundType == null) {
+ // This means the corresponding argument is primitive. The counterpart should be too.
+ assert other.getDynamicUpperBoundType(i) == null;
+ continue;
+ }
+ assert thisUpperBoundType.isReference();
+ TypeLatticeElement otherUpperBoundType = other.getDynamicUpperBoundType(i);
+ assert otherUpperBoundType != null && otherUpperBoundType.isReference();
+ result.dynamicUpperBoundTypes.put(
+ i, thisUpperBoundType.join(otherUpperBoundType, appView));
}
return result;
}
@@ -93,17 +97,20 @@
if (this == BOTTOM || otherCollection == BOTTOM) {
return this == BOTTOM && otherCollection == BOTTOM;
}
- return Arrays.equals(this.dynamicTypes, otherCollection.dynamicTypes);
+ assert this.dynamicUpperBoundTypes != null;
+ return this.dynamicUpperBoundTypes.equals(otherCollection.dynamicUpperBoundTypes);
}
@Override
public int hashCode() {
- return Arrays.hashCode(dynamicTypes);
+ assert this.dynamicUpperBoundTypes != null;
+ return System.identityHashCode(dynamicUpperBoundTypes);
}
@Override
public String toString() {
- return "(" + StringUtils.join(Arrays.asList(dynamicTypes), ", ") + ")";
+ assert this.dynamicUpperBoundTypes != null;
+ return dynamicUpperBoundTypes.toString();
}
}
@@ -159,19 +166,21 @@
if (!staticTypes[i].isReference()) {
continue;
}
- TypeLatticeElement dynamicType = getDynamicType(i);
- if (dynamicType == null) {
+ TypeLatticeElement dynamicUpperBoundType = getDynamicUpperBoundType(i);
+ if (dynamicUpperBoundType == null) {
continue;
}
// To avoid the full join of type lattices below, separately check if the nullability of
// arguments is improved, and if so, we can eagerly conclude that we've collected useful
// call site information for this method.
- Nullability nullability = dynamicType.nullability();
+ Nullability nullability = dynamicUpperBoundType.nullability();
if (nullability.isDefinitelyNull()) {
return true;
}
+ // TODO(b/139246447): Similar to nullability, if dynamic lower bound type is available,
+ // we stop here and regard that call sites of this method have useful info.
// In general, though, we're looking for (strictly) better dynamic types for arguments.
- if (dynamicType.strictlyLessThan(staticTypes[i], appView)) {
+ if (dynamicUpperBoundType.strictlyLessThan(staticTypes[i], appView)) {
return true;
}
}
@@ -179,12 +188,12 @@
}
@Override
- public TypeLatticeElement getDynamicType(int argIndex) {
+ public TypeLatticeElement getDynamicUpperBoundType(int argIndex) {
assert 0 <= argIndex && argIndex < size;
if (cachedRepresentative == null) {
return null;
}
- return cachedRepresentative.getDynamicType(argIndex);
+ return cachedRepresentative.getDynamicUpperBoundType(argIndex);
}
public static boolean hasArgumentsToRecord(List<Value> inValues) {
@@ -198,12 +207,20 @@
}
public void recordArguments(
- AppView<?> appView, DexEncodedMethod callingContext, List<Value> inValues) {
+ AppView<? extends AppInfoWithSubtyping> appView,
+ DexEncodedMethod callingContext,
+ List<Value> inValues) {
assert cachedRepresentative == null;
assert size == inValues.size();
ArgumentCollection newCallSiteInfo = new ArgumentCollection(size);
for (int i = 0; i < size; i++) {
- newCallSiteInfo.dynamicTypes[i] = inValues.get(i).getTypeLattice();
+ Value arg = inValues.get(i);
+ // TODO(b/69963623): may need different place to store constants.
+ if (arg.getTypeLattice().isPrimitive()) {
+ continue;
+ }
+ assert arg.getTypeLattice().isReference();
+ newCallSiteInfo.dynamicUpperBoundTypes.put(i, arg.getDynamicUpperBoundType(appView));
}
assert callingContext != null;
ArgumentCollection accumulatedArgumentCollection =
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
index 4ce3033..5c3e91c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.optimize.lambda.CodeProcessor.Strategy;
import com.android.tools.r8.kotlin.Kotlin;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThrowingConsumer;
import com.google.common.collect.Lists;
import com.google.common.io.BaseEncoding;
@@ -166,13 +167,19 @@
protected abstract String getGroupSuffix();
- final DexProgramClass synthesizeClass(DexItemFactory factory) {
+ final DexProgramClass synthesizeClass(InternalOptions options) {
assert classType == null;
assert verifyLambdaIds(true);
List<LambdaInfo> lambdas = Lists.newArrayList(this.lambdas.values());
- classType = factory.createType(
- "L" + getTypePackage() + "-$$LambdaGroup$" + getGroupSuffix() + createHash(lambdas) + ";");
- return getBuilder(factory).synthesizeClass();
+ classType =
+ options.itemFactory.createType(
+ "L"
+ + getTypePackage()
+ + "-$$LambdaGroup$"
+ + getGroupSuffix()
+ + createHash(lambdas)
+ + ";");
+ return getBuilder(options.itemFactory).synthesizeClass(options);
}
protected abstract LambdaGroupClassBuilder getBuilder(DexItemFactory factory);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
index 4f5f110..e569714 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.graph.EnclosingMethodAttribute;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.origin.SynthesizedOrigin;
+import com.android.tools.r8.utils.InternalOptions;
import java.util.Collections;
import java.util.List;
@@ -31,28 +32,31 @@
this.origin = origin;
}
- public final DexProgramClass synthesizeClass() {
+ public final DexProgramClass synthesizeClass(InternalOptions options) {
DexType groupClassType = group.getGroupClassType();
DexType superClassType = getSuperClassType();
-
- return new DexProgramClass(
- groupClassType,
- null,
- new SynthesizedOrigin(origin, getClass()),
- buildAccessFlags(),
- superClassType,
- buildInterfaces(),
- factory.createString(origin),
- null,
- Collections.emptyList(),
- buildEnclosingMethodAttribute(),
- buildInnerClasses(),
- buildAnnotations(),
- buildStaticFields(),
- buildInstanceFields(),
- buildDirectMethods(),
- buildVirtualMethods(),
- factory.getSkipNameValidationForTesting());
+ DexProgramClass programClass =
+ new DexProgramClass(
+ groupClassType,
+ null,
+ new SynthesizedOrigin(origin, getClass()),
+ buildAccessFlags(),
+ superClassType,
+ buildInterfaces(),
+ factory.createString(origin),
+ null,
+ Collections.emptyList(),
+ buildEnclosingMethodAttribute(),
+ buildInnerClasses(),
+ buildAnnotations(),
+ buildStaticFields(),
+ buildInstanceFields(),
+ buildDirectMethods(),
+ buildVirtualMethods(),
+ factory.getSkipNameValidationForTesting(),
+ // The name of the class is based on the hash of the content.
+ DexProgramClass::checksumFromType);
+ return programClass;
}
protected abstract DexType getSuperClassType();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index b226521..c759ac6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -393,7 +393,7 @@
for (LambdaGroup group : groups.values()) {
assert !group.isTrivial() : "No trivial group is expected here.";
group.compact();
- DexProgramClass lambdaGroupClass = group.synthesizeClass(factory);
+ DexProgramClass lambdaGroupClass = group.synthesizeClass(appView.options());
result.put(group, lambdaGroupClass);
// We have to register this new class as a subtype of object.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
index 1e7d21a..8682fc1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
+import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.NewInstance;
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.StaticPut;
@@ -31,6 +32,7 @@
import com.android.tools.r8.utils.ListUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -43,21 +45,13 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.BiConsumer;
import java.util.function.Predicate;
public final class ClassStaticizer {
- final AppView<AppInfoWithLiveness> appView;
- final DexItemFactory factory;
- final IRConverter converter;
-
- private enum Phase {
- None, Examine, Fixup
- }
-
- private Phase phase = Phase.None;
- private BiConsumer<DexEncodedMethod, IRCode> fixupStrategy = null;
+ private final AppView<AppInfoWithLiveness> appView;
+ private final DexItemFactory factory;
+ private final IRConverter converter;
// Represents a staticizing candidate with all information
// needed for staticizing.
@@ -71,6 +65,7 @@
final AtomicInteger instancesCreated = new AtomicInteger();
final Set<DexEncodedMethod> referencedFrom = Sets.newConcurrentHashSet();
final AtomicReference<DexEncodedMethod> constructor = new AtomicReference<>();
+ final AtomicReference<DexEncodedMethod> getter = new AtomicReference<>();
CandidateInfo(DexProgramClass candidate, DexEncodedField singletonField) {
assert candidate != null;
@@ -139,11 +134,9 @@
notEligible.add(field.field.type);
}
- // Let's also assume no methods should take or return a
- // value of this type.
+ // Don't allow methods that take a value of this type.
for (DexEncodedMethod method : cls.methods()) {
DexProto proto = method.method.proto;
- notEligible.add(proto.returnType);
notEligible.addAll(Arrays.asList(proto.parameters.values));
}
@@ -179,9 +172,6 @@
}
}
});
-
- // Next phase -- examine code for candidate usages
- phase = Phase.Examine;
}
private boolean isPinned(DexClass clazz, DexEncodedField singletonField) {
@@ -220,28 +210,50 @@
//
// NOTE: can be called concurrently.
public final void examineMethodCode(DexEncodedMethod method, IRCode code) {
- if (phase != Phase.Examine) {
- return;
- }
-
Set<Instruction> alreadyProcessed = Sets.newIdentityHashSet();
CandidateInfo receiverClassCandidateInfo = candidates.get(method.method.holder);
Value receiverValue = code.getThis(); // NOTE: is null for static methods.
- if (receiverClassCandidateInfo != null && receiverValue != null) {
- // We are inside an instance method of candidate class (not an instance initializer
- // which we will check later), check if all the references to 'this' are valid
- // (the call will invalidate the candidate if some of them are not valid).
- analyzeAllValueUsers(
- receiverClassCandidateInfo, receiverValue, factory.isConstructor(method.method));
+ if (receiverClassCandidateInfo != null) {
+ if (receiverValue != null) {
+ // We are inside an instance method of candidate class (not an instance initializer
+ // which we will check later), check if all the references to 'this' are valid
+ // (the call will invalidate the candidate if some of them are not valid).
+ analyzeAllValueUsers(
+ receiverClassCandidateInfo, receiverValue, factory.isConstructor(method.method));
- // If the candidate is still valid, ignore all instructions
- // we treat as valid usages on receiver.
- if (candidates.get(method.method.holder) != null) {
- alreadyProcessed.addAll(receiverValue.uniqueUsers());
+ // If the candidate is still valid, ignore all instructions
+ // we treat as valid usages on receiver.
+ if (candidates.get(method.method.holder) != null) {
+ alreadyProcessed.addAll(receiverValue.uniqueUsers());
+ }
+ } else {
+ // We are inside a static method of candidate class.
+ // Check if this is a valid getter of the singleton field.
+ if (method.method.proto.returnType == method.method.holder) {
+ List<Instruction> examined = isValidGetter(receiverClassCandidateInfo, code);
+ if (examined != null) {
+ DexEncodedMethod getter = receiverClassCandidateInfo.getter.get();
+ if (getter == null) {
+ receiverClassCandidateInfo.getter.set(method);
+ // Except for static-get and return, iterate other remaining instructions if any.
+ alreadyProcessed.addAll(examined);
+ } else {
+ assert getter != method;
+ // Not sure how to deal with many getters.
+ receiverClassCandidateInfo.invalidate();
+ }
+ } else {
+ // Invalidate the candidate if it has a static method whose return type is a candidate
+ // type but doesn't return the singleton field (in a trivial way).
+ receiverClassCandidateInfo.invalidate();
+ }
+ }
}
}
+ // TODO(b/143375203): if fully implemented, the following iterator could be:
+ // InstructionListIterator iterator = code.instructionListIterator();
ListIterator<Instruction> iterator =
Lists.newArrayList(code.instructionIterator()).listIterator();
while (iterator.hasNext()) {
@@ -288,7 +300,21 @@
CandidateInfo info = processStaticFieldRead(instruction.asStaticGet());
if (info != null) {
info.referencedFrom.add(method);
- // If the candidate still valid, ignore all usages in further analysis.
+ // If the candidate is still valid, ignore all usages in further analysis.
+ Value value = instruction.outValue();
+ if (value != null) {
+ alreadyProcessed.addAll(value.aliasedUsers());
+ }
+ }
+ continue;
+ }
+
+ if (instruction.isInvokeStatic()) {
+ // Check if it is a static singleton getter.
+ CandidateInfo info = processInvokeStatic(instruction.asInvokeStatic());
+ if (info != null) {
+ info.referencedFrom.add(method);
+ // If the candidate is still valid, ignore all usages in further analysis.
Value value = instruction.outValue();
if (value != null) {
alreadyProcessed.addAll(value.aliasedUsers());
@@ -465,6 +491,41 @@
return fieldAccessed == info.singletonField;
}
+ // Only allow a very trivial pattern: load the singleton field and return it, which looks like:
+ //
+ // v <- static-get singleton-field
+ // <assume instructions on v> // (optional)
+ // return v // or aliased value
+ //
+ // Returns a list of instructions that are examined (as long as the method is a trivial getter).
+ private List<Instruction> isValidGetter(CandidateInfo info, IRCode code) {
+ List<Instruction> instructions = new ArrayList<>();
+ StaticGet staticGet = null;
+ for (Instruction instr : code.instructions()) {
+ if (instr.isStaticGet()) {
+ staticGet = instr.asStaticGet();
+ DexEncodedField fieldAccessed =
+ appView.appInfo().lookupStaticTarget(staticGet.getField().holder, staticGet.getField());
+ if (fieldAccessed != info.singletonField) {
+ return null;
+ }
+ instructions.add(instr);
+ continue;
+ }
+ if (instr.isAssume() || instr.isReturn()) {
+ Value v = instr.inValues().get(0).getAliasedValue();
+ if (v.isPhi() || v.definition != staticGet) {
+ return null;
+ }
+ instructions.add(instr);
+ continue;
+ }
+ // All other instructions are not allowed.
+ return null;
+ }
+ return instructions;
+ }
+
// Static field get: can be a valid singleton field for a
// candidate in which case we should check if all the usages of the
// value read are eligible.
@@ -486,6 +547,23 @@
return candidateInfo;
}
+ // Static getter: if this invokes a registered getter, treat it as static field get.
+ // That is, we should check if all the usages of the out value are eligible.
+ private CandidateInfo processInvokeStatic(InvokeStatic invoke) {
+ DexType candidateType = invoke.getInvokedMethod().proto.returnType;
+ CandidateInfo candidateInfo = candidates.get(candidateType);
+ if (candidateInfo == null) {
+ return null;
+ }
+
+ if (invoke.hasOutValue()
+ && candidateInfo.getter.get() != null
+ && candidateInfo.getter.get().method == invoke.getInvokedMethod()) {
+ candidateInfo = analyzeAllValueUsers(candidateInfo, invoke.outValue(), false);
+ }
+ return candidateInfo;
+ }
+
private CandidateInfo analyzeAllValueUsers(
CandidateInfo candidateInfo, Value value, boolean ignoreSuperClassInitInvoke) {
assert value != null && value == value.getAliasedValue();
@@ -556,29 +634,7 @@
//
public final void staticizeCandidates(
OptimizationFeedback feedback, ExecutorService executorService) throws ExecutionException {
- phase = Phase.None; // We are done with processing/examining methods.
- new StaticizingProcessor(appView, this, executorService).run(feedback);
- }
-
- public final void fixupMethodCode(DexEncodedMethod method, IRCode code) {
- if (phase == Phase.Fixup) {
- assert fixupStrategy != null;
- fixupStrategy.accept(method, code);
- }
- }
-
- void setFixupStrategy(BiConsumer<DexEncodedMethod, IRCode> strategy) {
- assert phase == Phase.None;
- assert strategy != null;
- phase = Phase.Fixup;
- fixupStrategy = strategy;
- }
-
- void cleanFixupStrategy() {
- assert phase == Phase.Fixup;
- assert fixupStrategy != null;
- phase = Phase.None;
- fixupStrategy = null;
+ new StaticizingProcessor(appView, this, converter).run(feedback, executorService);
}
private class CallSiteReferencesInvalidator extends UseRegistry {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
index 3956609..fcb2c2f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -4,8 +4,6 @@
package com.android.tools.r8.ir.optimize.staticizer;
-import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
-
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexClass;
@@ -27,10 +25,11 @@
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.StaticPut;
import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.conversion.CallSiteInformation;
-import com.android.tools.r8.ir.optimize.Outliner;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.optimize.CodeRewriter;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.ir.optimize.staticizer.ClassStaticizer.CandidateInfo;
+import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.ThreadUtils;
@@ -39,6 +38,7 @@
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
@@ -48,14 +48,18 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
-import java.util.function.BiConsumer;
+import java.util.function.Consumer;
import java.util.stream.Collectors;
+// TODO(b/140766440): Use PostProcessor, instead of having its own post processing.
final class StaticizingProcessor {
private final AppView<AppInfoWithLiveness> appView;
private final ClassStaticizer classStaticizer;
- private final ExecutorService executorService;
+ private final IRConverter converter;
+
+ private final Map<DexEncodedMethod, Collection<Consumer<IRCode>>> processingQueue =
+ new IdentityHashMap<>();
private final Set<DexEncodedMethod> referencingExtraMethods = Sets.newIdentityHashSet();
private final Map<DexEncodedMethod, CandidateInfo> hostClassInits = new IdentityHashMap<>();
@@ -66,26 +70,32 @@
StaticizingProcessor(
AppView<AppInfoWithLiveness> appView,
ClassStaticizer classStaticizer,
- ExecutorService executorService) {
+ IRConverter converter) {
this.appView = appView;
this.classStaticizer = classStaticizer;
- this.executorService = executorService;
+ this.converter = converter;
}
- final void run(OptimizationFeedback feedback) throws ExecutionException {
+ final void run(OptimizationFeedback optimizationFeedback, ExecutorService executorService)
+ throws ExecutionException {
// Filter out candidates based on the information we collected while examining methods.
finalEligibilityCheck();
// Prepare interim data.
prepareCandidates();
- // Process all host class initializers (only remove instantiations).
- processMethodsConcurrently(
- hostClassInits.keySet(), this::removeCandidateInstantiation, feedback);
+ // Enqueue all host class initializers (only remove instantiations).
+ enqueueMethodsWithCodeOptimization(
+ hostClassInits.keySet(), this::removeCandidateInstantiation);
- // Process instance methods to be staticized (only remove references to 'this').
- processMethodsConcurrently(methodsToBeStaticized, this::removeReferencesToThis, feedback);
+ // Enqueue instance methods to be staticized (only remove references to 'this').
+ enqueueMethodsWithCodeOptimization(
+ methodsToBeStaticized, this::removeReferencesToThis);
+ // Process queued methods with associated optimizations
+ processMethodsConcurrently(optimizationFeedback, executorService);
+
+ // TODO(b/140767158): Merge the remaining part below.
// Convert instance methods into static methods with an extra parameter.
Set<DexEncodedMethod> methods = staticizeMethodSymbols();
@@ -94,7 +104,10 @@
// a result of staticizing.)
methods.addAll(referencingExtraMethods);
methods.addAll(hostClassInits.keySet());
- processMethodsConcurrently(methods, this::rewriteReferences, feedback);
+ enqueueMethodsWithCodeOptimization(methods, this::rewriteReferences);
+
+ // Process queued methods with associated optimizations
+ processMethodsConcurrently(optimizationFeedback, executorService);
}
private void finalEligibilityCheck() {
@@ -237,50 +250,65 @@
referencingExtraMethods.removeAll(removedInstanceMethods);
}
+ private void enqueueMethodsWithCodeOptimization(
+ Iterable<DexEncodedMethod> methods, Consumer<IRCode> optimization) {
+ for (DexEncodedMethod method : methods) {
+ processingQueue
+ .computeIfAbsent(
+ method,
+ // Optimization order might matter, hence a collection that preserves orderings.
+ k -> new ArrayList<>())
+ .add(optimization);
+ }
+ }
+
/**
* Processes the given methods concurrently using the given strategy.
*
- * <p>Note that, when the strategy {@link #rewriteReferences(DexEncodedMethod, IRCode)} is being
- * applied, it is important that we never inline a method from `methods` which has still not been
- * reprocessed. This could lead to broken code, because the strategy that rewrites the broken
- * references is applied *before* inlining (because the broken references in the inlinee are never
- * rewritten). We currently avoid this situation by processing all the methods concurrently
+ * <p>Note that, when the strategy {@link #rewriteReferences(IRCode)} is being applied, it is
+ * important that we never inline a method from `methods` which has still not been reprocessed.
+ * This could lead to broken code, because the strategy that rewrites the broken references is
+ * applied *before* inlining (because the broken references in the inlinee are never rewritten).
+ * We currently avoid this situation by processing all the methods concurrently
* (inlining of a method that is processed concurrently is not allowed).
*/
private void processMethodsConcurrently(
- Set<DexEncodedMethod> methods,
- BiConsumer<DexEncodedMethod, IRCode> strategy,
- OptimizationFeedback feedback)
- throws ExecutionException {
- classStaticizer.setFixupStrategy(strategy);
-
+ OptimizationFeedback feedback, ExecutorService executorService) throws ExecutionException {
List<Future<?>> futures = new ArrayList<>();
- for (DexEncodedMethod method : methods) {
+ for (DexEncodedMethod method : processingQueue.keySet()) {
futures.add(
executorService.submit(
() -> {
- classStaticizer.converter.processMethod(
- method,
- feedback,
- methods::contains,
- CallSiteInformation.empty(),
- Outliner::noProcessing);
+ forEachMethod(method, processingQueue.get(method), feedback);
return null; // we want a Callable not a Runnable to be able to throw
}));
}
ThreadUtils.awaitFutures(futures);
-
- classStaticizer.cleanFixupStrategy();
+ // TODO(b/140767158): No need to clear if we can do every thing in one go.
+ processingQueue.clear();
}
- private void removeCandidateInstantiation(DexEncodedMethod method, IRCode code) {
- CandidateInfo candidateInfo = hostClassInits.get(method);
+ // TODO(b/140766440): Should be part or variant of PostProcessor.
+ private void forEachMethod(
+ DexEncodedMethod method,
+ Collection<Consumer<IRCode>> codeOptimizations,
+ OptimizationFeedback feedback) {
+ Origin origin = appView.appInfo().originFor(method.method.holder);
+ IRCode code = method.buildIR(appView, origin);
+ codeOptimizations.forEach(codeOptimization -> codeOptimization.accept(code));
+ converter.collectOptimizationInfo(code, feedback);
+ CodeRewriter.removeAssumeInstructions(appView, code);
+ converter.finalizeIR(method, code, feedback);
+ }
+
+ private void removeCandidateInstantiation(IRCode code) {
+ CandidateInfo candidateInfo = hostClassInits.get(code.method);
assert candidateInfo != null;
// Find and remove instantiation and its users.
for (Instruction instruction : code.instructions()) {
- if (instruction.isNewInstance() &&
- instruction.asNewInstance().clazz == candidateInfo.candidate.type) {
+ if (instruction.isNewInstance()
+ && instruction.asNewInstance().clazz == candidateInfo.candidate.type) {
// Remove all usages
// NOTE: requiring (a) the instance initializer to be trivial, (b) not allowing
// candidates with instance fields and (c) requiring candidate to directly
@@ -300,11 +328,11 @@
assert false : "Must always be able to find and remove the instantiation";
}
- private void removeReferencesToThis(DexEncodedMethod method, IRCode code) {
+ private void removeReferencesToThis(IRCode code) {
fixupStaticizedThisUsers(code, code.getThis());
}
- private void rewriteReferences(DexEncodedMethod method, IRCode code) {
+ private void rewriteReferences(IRCode code) {
// Process all singleton field reads and rewrite their users.
List<StaticGet> singletonFieldReads =
Streams.stream(code.instructionIterator())
@@ -513,7 +541,7 @@
new StaticGet(
code.createValue(
TypeLatticeElement.fromDexType(
- field.type, maybeNull(), classStaticizer.appView),
+ field.type, outValue.getTypeLattice().nullability(), appView),
outValue.getLocalInfo()),
field));
}
@@ -536,12 +564,13 @@
if (hostType != null) {
DexMethod newMethod = factory().createMethod(hostType, method.proto, method.name);
Value outValue = invoke.outValue();
+ DexType returnType = method.proto.returnType;
Value newOutValue =
- method.proto.returnType.isVoidType()
+ returnType.isVoidType()
? null
: code.createValue(
TypeLatticeElement.fromDexType(
- method.proto.returnType, maybeNull(), classStaticizer.appView),
+ returnType, outValue.getTypeLattice().nullability(), appView),
outValue == null ? null : outValue.getLocalInfo());
it.replaceCurrentInstruction(new InvokeStatic(newMethod, newOutValue, invoke.inValues()));
}
@@ -588,7 +617,7 @@
// Consider moving static members from candidate into host.
DexType hostType = candidate.hostType();
if (candidateClass.type != hostType) {
- DexClass hostClass = classStaticizer.appView.definitionFor(hostType);
+ DexClass hostClass = appView.definitionFor(hostType);
assert hostClass != null;
if (!classMembersConflict(candidateClass, hostClass)) {
// Move all members of the candidate class into host class.
@@ -681,6 +710,6 @@
}
private DexItemFactory factory() {
- return classStaticizer.factory;
+ return appView.dexItemFactory();
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceCore.java b/src/main/java/com/android/tools/r8/retrace/RetraceCore.java
index f783cbc..8db2ace 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceCore.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceCore.java
@@ -408,7 +408,8 @@
}
private String retracedFileName(String retracedClazz) {
- if (!UNKNOWN_SOURCEFILE_NAMES.contains(fileName)) {
+ boolean fileNameProbablyChanged = retracedClazz != null && !retracedClazz.startsWith(clazz);
+ if (!UNKNOWN_SOURCEFILE_NAMES.contains(fileName) && !fileNameProbablyChanged) {
return fileName;
}
if (retracedClazz == null) {
@@ -426,13 +427,9 @@
private String getClassSimpleName(String clazz) {
int lastIndexOfPeriod = clazz.lastIndexOf('.');
- if (lastIndexOfPeriod > -1) {
- // Check if we can find a subclass separator.
- int endIndex = firstCharFromIndex(clazz, lastIndexOfPeriod, '$');
- return clazz.substring(lastIndexOfPeriod + 1, endIndex);
- } else {
- return clazz;
- }
+ // Check if we can find a subclass separator.
+ int endIndex = firstCharFromIndex(clazz, lastIndexOfPeriod + 1, '$');
+ return clazz.substring(lastIndexOfPeriod + 1, endIndex);
}
@Override
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 687e7da..aaee831 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -68,7 +68,6 @@
// Set to true to run compilation in a single thread and without randomly shuffling the input.
// This makes life easier when running R8 in a debugger.
public static final boolean DETERMINISTIC_DEBUGGING = false;
-
public enum LineNumberOptimization {
OFF,
ON
@@ -176,6 +175,8 @@
// To print memory one also have to enable printtimes.
public boolean printMemory = System.getProperty("com.android.tools.r8.printmemory") != null;
+ public String dumpInputToFile = System.getProperty("com.android.tools.r8.dumpinputtofile");
+
// Flag to toggle if DEX code objects should pass-through without IR processing.
public boolean passthroughDexCode = false;
// TODO(b/134705306): Currently allow merging dex files resulting from Java 8 library
diff --git a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
index 079c89c..8c659b1 100644
--- a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
+++ b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
@@ -1772,6 +1772,7 @@
"util.concurrent.SynchronousQueue.ConstructorZ.SynchronousQueue_Constructor_A01",
anyDexVm())
.put("lang.Thread.getState.Thread_getState_A01", anyDexVm())
+ .put("lang.Thread.join.Thread_join_A01", anyDexVm())
.put(
"util.concurrent.ScheduledThreadPoolExecutor.getTaskCount.ScheduledThreadPoolExecutor_getTaskCount_A01",
any())
diff --git a/src/test/java/com/android/tools/r8/L8CommandTest.java b/src/test/java/com/android/tools/r8/L8CommandTest.java
index 7a72d1b..0a55b8a 100644
--- a/src/test/java/com/android/tools/r8/L8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/L8CommandTest.java
@@ -114,6 +114,7 @@
}
@Test
+ @Ignore("b/143431384: Re-enable shrinking")
public void addProguardConfigurationString() throws Throwable {
String keepRule = "-keep class java.time.*";
List<String> keepRules = new ArrayList<>();
@@ -129,6 +130,7 @@
}
@Test
+ @Ignore("b/143431384: Re-enable shrinking")
public void addProguardConfigurationFile() throws Throwable {
String keepRule = "-keep class java.time.*";
Path keepRuleFile = temp.newFile("keepRuleFile.txt").toPath();
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
index 3082da8..8282c77 100644
--- a/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
@@ -147,7 +147,6 @@
private TestDiagnosticMessages assertNoMessageThatMatches(
List<Diagnostic> diagnostics, String tag, Matcher<String> matcher) {
- assertNotEquals(0, diagnostics.size());
for (int i = 0; i < diagnostics.size(); i++) {
String message = diagnostics.get(i).getDiagnosticMessage();
if (matcher.matches(message)) {
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/HelloWorldCompiledOnArtTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/HelloWorldCompiledOnArtTest.java
index 044f075..aa511f6 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/HelloWorldCompiledOnArtTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/HelloWorldCompiledOnArtTest.java
@@ -6,18 +6,22 @@
import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+import static org.hamcrest.core.StringContains.containsString;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.D8;
import com.android.tools.r8.D8TestBuilder;
import com.android.tools.r8.D8TestCompileResult;
import com.android.tools.r8.R8;
+import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
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.ToolHelper.ProcessResult;
+import com.android.tools.r8.desugar.corelib.conversionTests.APIConversionTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import java.io.IOException;
@@ -31,7 +35,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class HelloWorldCompiledOnArtTest extends CoreLibDesugarTestBase {
+public class HelloWorldCompiledOnArtTest extends APIConversionTestBase {
// TODO(b/142621961): Create an abstraction to easily run tests on External DexR8.
// Manage pathMock in the abstraction.
@@ -124,11 +128,20 @@
if (parameters.getApiLevel().getLevel() < AndroidApiLevel.O.getLevel()) {
d8TestBuilder.addProgramFiles(getPathBackport());
}
- return d8TestBuilder
- .setMinApi(parameters.getApiLevel())
- .enableCoreLibraryDesugaring(parameters.getApiLevel())
- .compile()
- .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, parameters.getApiLevel())
+ D8TestCompileResult compile =
+ d8TestBuilder
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel())
+ .addOptionsModification(opt -> opt.testing.trackDesugaredAPIConversions = true)
+ .compile();
+ TestDiagnosticMessages diagnosticMessages = compile.getDiagnosticMessages();
+ assertTrue(
+ diagnosticMessages.getWarnings().isEmpty()
+ || diagnosticMessages.getWarnings().stream()
+ .noneMatch(x -> x.getDiagnosticMessage().contains("andThen")));
+ return compile
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibraryWithConversionExtension, parameters.getApiLevel())
.withArt6Plus64BitsLib()
.withArtFrameworks();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/JavaUtilFunctionTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/JavaUtilFunctionTest.java
index 2ddacd7..53c9a9d 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/JavaUtilFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/JavaUtilFunctionTest.java
@@ -103,40 +103,6 @@
.assertSuccessWithOutput(expectedOutput);
}
- @Test
- public void testWrapperWithChecksum() throws Exception {
- KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
- testForD8()
- .addInnerClasses(JavaUtilFunctionTest.class)
- .setMinApi(parameters.getApiLevel())
- .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
- .setIncludeClassesChecksum(true) // Compilation fails if some classes are missing checksum.
- .compile()
- .inspect(
- inspector -> {
- assertEquals(
- parameters.getApiLevel().getLevel() >= AndroidApiLevel.N.getLevel() ? 0 : 1,
- inspector.allClasses().stream()
- .filter(
- clazz ->
- clazz
- .getFinalName()
- .contains(DesugaredLibraryWrapperSynthesizer.TYPE_WRAPPER_SUFFIX))
- .count());
- assertEquals(
- parameters.getApiLevel().getLevel() >= AndroidApiLevel.N.getLevel() ? 0 : 1,
- inspector.allClasses().stream()
- .filter(
- clazz ->
- clazz
- .getFinalName()
- .contains(
- DesugaredLibraryWrapperSynthesizer
- .VIVIFIED_TYPE_WRAPPER_SUFFIX))
- .count());
- });
- }
-
static class TestClass {
@NeverInline
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/FunctionConversionTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/FunctionConversionTest.java
index 702eb05..cc44509 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/FunctionConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/FunctionConversionTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.TestRuntime.DexRuntime;
import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryWrapperSynthesizer;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -24,6 +25,7 @@
import java.util.function.LongConsumer;
import java.util.function.LongSupplier;
import java.util.stream.Collectors;
+import org.junit.Assert;
import org.junit.Test;
public class FunctionConversionTest extends APIConversionTestBase {
@@ -67,6 +69,41 @@
doubleSupplierWrapperClasses.size());
}
+ @Test
+ public void testWrapperWithChecksum() throws Exception {
+ testForD8()
+ .addProgramClasses(
+ Executor.class, Executor.Object1.class, Executor.Object2.class, Executor.Object3.class)
+ .addLibraryClasses(CustomLibClass.class)
+ .setMinApi(AndroidApiLevel.B)
+ .enableCoreLibraryDesugaring(AndroidApiLevel.B)
+ .setIncludeClassesChecksum(true) // Compilation fails if some classes are missing checksum.
+ .compile()
+ .inspect(
+ inspector -> {
+ Assert.assertEquals(
+ 8,
+ inspector.allClasses().stream()
+ .filter(
+ clazz ->
+ clazz
+ .getFinalName()
+ .contains(DesugaredLibraryWrapperSynthesizer.TYPE_WRAPPER_SUFFIX))
+ .count());
+ Assert.assertEquals(
+ 6,
+ inspector.allClasses().stream()
+ .filter(
+ clazz ->
+ clazz
+ .getFinalName()
+ .contains(
+ DesugaredLibraryWrapperSynthesizer
+ .VIVIFIED_TYPE_WRAPPER_SUFFIX))
+ .count());
+ });
+ }
+
static class Executor {
public static void main(String[] args) {
@@ -99,6 +136,7 @@
}
static class Object3 {
+
private Object2 field;
private Object3(Object2 o) {
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11ConcurrentMapTests.java b/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11ConcurrentMapTests.java
index f80607a..092b14b 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11ConcurrentMapTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11ConcurrentMapTests.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.ToolHelper.JDK_TESTS_BUILD_DIR;
import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
import static com.android.tools.r8.utils.FileUtils.JAVA_EXTENSION;
+import static junit.framework.TestCase.assertTrue;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.endsWith;
import static org.hamcrest.CoreMatchers.not;
@@ -16,9 +17,11 @@
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.ir.desugar.DesugaredLibraryWrapperSynthesizer;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
@@ -111,6 +114,7 @@
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
.compile()
+ .inspect(this::assertNoConversions)
.withArt6Plus64BitsLib()
.addDesugaredCoreLibraryRunClassPath(
this::buildDesugaredLibrary,
@@ -122,6 +126,15 @@
endsWith(StringUtils.lines("ConcurrentModification: SUCCESS")));
}
+ private void assertNoConversions(CodeInspector inspector) {
+ assertTrue(
+ inspector.allClasses().stream()
+ .noneMatch(
+ cl ->
+ cl.getOriginalName()
+ .startsWith(DesugaredLibraryWrapperSynthesizer.WRAPPER_PREFIX)));
+ }
+
private Path[] concurrentHashTestToCompile() {
// We exclude WhiteBox.class because of Method handles, they are not supported on old devices
// and the test uses methods not present even on 28.
diff --git a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
index 8b40ea9..5b94aac 100644
--- a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
+++ b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
@@ -87,49 +87,60 @@
code);
}
- private DexProgramClass makeClass(String name, int stringCount, int startOffset,
+ private DexProgramClass makeClass(
+ InternalOptions options,
+ String name,
+ int stringCount,
+ int startOffset,
Collection<DexProgramClass> synthesizedFrom) {
String desc = DescriptorUtils.javaTypeToDescriptor(name);
DexType type = dexItemFactory.createType(desc);
- return new DexProgramClass(
- type,
- null,
- new SynthesizedOrigin("test", getClass()),
- ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC),
- dexItemFactory.objectType,
- DexTypeList.empty(),
- null,
- null,
- Collections.emptyList(),
- null,
- Collections.emptyList(),
- DexAnnotationSet.empty(),
- DexEncodedField.EMPTY_ARRAY,
- DexEncodedField.EMPTY_ARRAY,
- DexEncodedMethod.EMPTY_ARRAY,
- new DexEncodedMethod[] {makeMethod(type, stringCount, startOffset)},
- false,
- synthesizedFrom);
+ DexProgramClass programClass =
+ new DexProgramClass(
+ type,
+ null,
+ new SynthesizedOrigin("test", getClass()),
+ ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC),
+ dexItemFactory.objectType,
+ DexTypeList.empty(),
+ null,
+ null,
+ Collections.emptyList(),
+ null,
+ Collections.emptyList(),
+ DexAnnotationSet.empty(),
+ DexEncodedField.EMPTY_ARRAY,
+ DexEncodedField.EMPTY_ARRAY,
+ DexEncodedMethod.EMPTY_ARRAY,
+ new DexEncodedMethod[] {makeMethod(type, stringCount, startOffset)},
+ false,
+ DexProgramClass::invalidChecksumRequest,
+ synthesizedFrom);
+ return programClass;
}
@Test
public void manyFilesWithSharedSynthesizedClass() throws ExecutionException, IOException {
+ InternalOptions options = new InternalOptions(dexItemFactory, new Reporter());
// Create classes that all reference enough strings to overflow the index, but are all
// at different offsets in the strings array. This ensures we trigger multiple rounds of
// rewrites.
List<DexProgramClass> classes = new ArrayList<>();
for (int i = 0; i < NUMBER_OF_FILES; i++) {
- classes.add(makeClass("Class" + i, Constants.MAX_NON_JUMBO_INDEX - 1, i % 100,
- Collections.emptyList()));
+ classes.add(
+ makeClass(
+ options,
+ "Class" + i,
+ Constants.MAX_NON_JUMBO_INDEX - 1,
+ i % 100,
+ Collections.emptyList()));
}
// Create a shared class that references strings above the maximum.
- DexProgramClass sharedSynthesizedClass = makeClass("SharedSynthesized", 100,
- Constants.MAX_NON_JUMBO_INDEX - 1,
- classes);
+ DexProgramClass sharedSynthesizedClass =
+ makeClass(options, "SharedSynthesized", 100, Constants.MAX_NON_JUMBO_INDEX - 1, classes);
- InternalOptions options = new InternalOptions(dexItemFactory, new Reporter());
DexApplication.Builder builder =
DirectMappedDexApplication.builder(options, new Timing("SharedClassWritingTest"));
builder.addSynthesizedClass(sharedSynthesizedClass, false);
@@ -144,7 +155,6 @@
null,
options,
null,
- null,
GraphLense.getIdentityLense(),
NamingLens.getIdentityLens(),
null);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
index 3a4ceda..50742b5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
@@ -48,7 +48,7 @@
testAugmentedIRCode.accept(code);
}
- new CodeRewriter(appView, null).removeAssumeInstructions(code);
+ CodeRewriter.removeAssumeInstructions(appView, code);
assertTrue(code.isConsistentSSA());
checkCountOfNonNull(code, 0);
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeDirectNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectNegativeTest.java
similarity index 97%
rename from src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeDirectNegativeTest.java
rename to src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectNegativeTest.java
index d2687fd..fc40fd9 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeDirectNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectNegativeTest.java
@@ -1,7 +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 com.android.tools.r8.ir.optimize.callsites.dynamictype;
+package com.android.tools.r8.ir.optimize.callsites.dynamicupperboundtype;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeDirectPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java
similarity index 97%
rename from src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeDirectPositiveTest.java
rename to src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java
index 0145b82..a1b889c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeDirectPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java
@@ -1,7 +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 com.android.tools.r8.ir.optimize.callsites.dynamictype;
+package com.android.tools.r8.ir.optimize.callsites.dynamicupperboundtype;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeInterfaceNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfaceNegativeTest.java
similarity index 97%
rename from src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeInterfaceNegativeTest.java
rename to src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfaceNegativeTest.java
index 3508b62..f309eb8 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeInterfaceNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfaceNegativeTest.java
@@ -1,7 +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 com.android.tools.r8.ir.optimize.callsites.dynamictype;
+package com.android.tools.r8.ir.optimize.callsites.dynamicupperboundtype;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeInterfacePositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfacePositiveTest.java
similarity index 97%
rename from src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeInterfacePositiveTest.java
rename to src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfacePositiveTest.java
index 8cca30a..7b8818e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeInterfacePositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfacePositiveTest.java
@@ -1,7 +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 com.android.tools.r8.ir.optimize.callsites.dynamictype;
+package com.android.tools.r8.ir.optimize.callsites.dynamicupperboundtype;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeStaticNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticNegativeTest.java
similarity index 97%
rename from src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeStaticNegativeTest.java
rename to src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticNegativeTest.java
index fb463a0..b63d571 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeStaticNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticNegativeTest.java
@@ -1,7 +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 com.android.tools.r8.ir.optimize.callsites.dynamictype;
+package com.android.tools.r8.ir.optimize.callsites.dynamicupperboundtype;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeStaticPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java
similarity index 97%
rename from src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeStaticPositiveTest.java
rename to src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java
index 47e8c1c..e8b75c5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeStaticPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java
@@ -1,7 +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 com.android.tools.r8.ir.optimize.callsites.dynamictype;
+package com.android.tools.r8.ir.optimize.callsites.dynamicupperboundtype;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeVirtualNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualNegativeTest.java
similarity index 97%
rename from src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeVirtualNegativeTest.java
rename to src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualNegativeTest.java
index 9da6912..6902f40 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeVirtualNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualNegativeTest.java
@@ -1,7 +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 com.android.tools.r8.ir.optimize.callsites.dynamictype;
+package com.android.tools.r8.ir.optimize.callsites.dynamicupperboundtype;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java
similarity index 97%
rename from src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeVirtualPositiveTest.java
rename to src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java
index cc0f93d..5f3dda0 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeVirtualPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java
@@ -1,7 +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 com.android.tools.r8.ir.optimize.callsites.dynamictype;
+package com.android.tools.r8.ir.optimize.callsites.dynamicupperboundtype;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
index 99d27fa..d0a4064 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
@@ -42,9 +42,11 @@
import com.android.tools.r8.ir.optimize.staticizer.movetohost.MoveToHostTestClass;
import com.android.tools.r8.ir.optimize.staticizer.trivial.Simple;
import com.android.tools.r8.ir.optimize.staticizer.trivial.SimpleWithGetter;
+import com.android.tools.r8.ir.optimize.staticizer.trivial.SimpleWithLazyInit;
import com.android.tools.r8.ir.optimize.staticizer.trivial.SimpleWithParams;
import com.android.tools.r8.ir.optimize.staticizer.trivial.SimpleWithPhi;
import com.android.tools.r8.ir.optimize.staticizer.trivial.SimpleWithSideEffects;
+import com.android.tools.r8.ir.optimize.staticizer.trivial.SimpleWithThrowingGetter;
import com.android.tools.r8.ir.optimize.staticizer.trivial.TrivialTestClass;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.InternalOptions;
@@ -79,11 +81,13 @@
NeverInline.class,
TrivialTestClass.class,
Simple.class,
- SimpleWithSideEffects.class,
- SimpleWithParams.class,
SimpleWithGetter.class,
+ SimpleWithLazyInit.class,
+ SimpleWithParams.class,
SimpleWithPhi.class,
- SimpleWithPhi.Companion.class
+ SimpleWithPhi.Companion.class,
+ SimpleWithSideEffects.class,
+ SimpleWithThrowingGetter.class
};
String javaOutput = runOnJava(main);
TestRunResult result =
@@ -92,7 +96,7 @@
.enableInliningAnnotations()
.addKeepMainRule(main)
.noMinification()
- .addKeepRules("-keepattributes InnerClasses,EnclosingMethod")
+ .addKeepAttributes("InnerClasses", "EnclosingMethod")
.addOptionsModification(this::configure)
.allowAccessModification()
.setMinApi(parameters.getApiLevel())
@@ -104,9 +108,9 @@
assertEquals(
Lists.newArrayList(
- "STATIC: String trivial.Simple.bar(String)",
- "STATIC: String trivial.Simple.foo()",
- "STATIC: String trivial.TrivialTestClass.next()"),
+ "STATIC: String Simple.bar(String)",
+ "STATIC: String Simple.foo()",
+ "STATIC: String TrivialTestClass.next()"),
references(clazz, "testSimple", "void"));
ClassSubject simple = inspector.clazz(Simple.class);
@@ -115,10 +119,10 @@
assertEquals(
Lists.newArrayList(
- "STATIC: String trivial.SimpleWithPhi.bar(String)",
- "STATIC: String trivial.SimpleWithPhi.foo()",
- "STATIC: String trivial.SimpleWithPhi.foo()",
- "STATIC: String trivial.TrivialTestClass.next()"),
+ "STATIC: String SimpleWithPhi.bar(String)",
+ "STATIC: String SimpleWithPhi.foo()",
+ "STATIC: String SimpleWithPhi.foo()",
+ "STATIC: String TrivialTestClass.next()"),
references(clazz, "testSimpleWithPhi", "void", "int"));
ClassSubject simpleWithPhi = inspector.clazz(SimpleWithPhi.class);
@@ -127,9 +131,9 @@
assertEquals(
Lists.newArrayList(
- "STATIC: String trivial.SimpleWithParams.bar(String)",
- "STATIC: String trivial.SimpleWithParams.foo()",
- "STATIC: String trivial.TrivialTestClass.next()"),
+ "STATIC: String SimpleWithParams.bar(String)",
+ "STATIC: String SimpleWithParams.foo()",
+ "STATIC: String TrivialTestClass.next()"),
references(clazz, "testSimpleWithParams", "void"));
ClassSubject simpleWithParams = inspector.clazz(SimpleWithParams.class);
@@ -138,11 +142,11 @@
assertEquals(
Lists.newArrayList(
- "STATIC: String trivial.SimpleWithSideEffects.bar(String)",
- "STATIC: String trivial.SimpleWithSideEffects.foo()",
- "STATIC: String trivial.TrivialTestClass.next()",
- "trivial.SimpleWithSideEffects trivial.SimpleWithSideEffects.INSTANCE",
- "trivial.SimpleWithSideEffects trivial.SimpleWithSideEffects.INSTANCE"),
+ "STATIC: String SimpleWithSideEffects.bar(String)",
+ "STATIC: String SimpleWithSideEffects.foo()",
+ "STATIC: String TrivialTestClass.next()",
+ "SimpleWithSideEffects SimpleWithSideEffects.INSTANCE",
+ "SimpleWithSideEffects SimpleWithSideEffects.INSTANCE"),
references(clazz, "testSimpleWithSideEffects", "void"));
ClassSubject simpleWithSideEffects = inspector.clazz(SimpleWithSideEffects.class);
@@ -150,19 +154,49 @@
// As its name implies, its clinit has side effects.
assertThat(simpleWithSideEffects.clinit(), isPresent());
- // TODO(b/111832046): add support for singleton instance getters.
assertEquals(
Lists.newArrayList(
- "STATIC: String trivial.TrivialTestClass.next()",
- "VIRTUAL: String trivial.SimpleWithGetter.bar(String)",
- "VIRTUAL: String trivial.SimpleWithGetter.foo()",
- "trivial.SimpleWithGetter trivial.SimpleWithGetter.INSTANCE",
- "trivial.SimpleWithGetter trivial.SimpleWithGetter.INSTANCE"),
+ "STATIC: String SimpleWithGetter.bar(String)",
+ "STATIC: String SimpleWithGetter.foo()",
+ "STATIC: String TrivialTestClass.next()"),
references(clazz, "testSimpleWithGetter", "void"));
ClassSubject simpleWithGetter = inspector.clazz(SimpleWithGetter.class);
- assertFalse(instanceMethods(simpleWithGetter).isEmpty());
- assertThat(simpleWithGetter.clinit(), isPresent());
+ assertTrue(instanceMethods(simpleWithGetter).isEmpty());
+ assertThat(simpleWithGetter.clinit(), not(isPresent()));
+
+ assertEquals(
+ Lists.newArrayList(
+ "STATIC: SimpleWithThrowingGetter SimpleWithThrowingGetter.getInstance()",
+ "STATIC: SimpleWithThrowingGetter SimpleWithThrowingGetter.getInstance()",
+ "STATIC: String TrivialTestClass.next()",
+ "VIRTUAL: String SimpleWithThrowingGetter.bar(String)",
+ "VIRTUAL: String SimpleWithThrowingGetter.foo()"),
+ references(clazz, "testSimpleWithThrowingGetter", "void"));
+
+ ClassSubject simpleWithThrowingGetter = inspector.clazz(SimpleWithThrowingGetter.class);
+ assertFalse(instanceMethods(simpleWithThrowingGetter).isEmpty());
+ assertThat(simpleWithThrowingGetter.clinit(), isPresent());
+
+ // TODO(b/143389508): add support for lazy init in singleton instance getter.
+ assertEquals(
+ Lists.newArrayList(
+ "DIRECT: void SimpleWithLazyInit.<init>()",
+ "DIRECT: void SimpleWithLazyInit.<init>()",
+ "STATIC: String TrivialTestClass.next()",
+ "SimpleWithLazyInit SimpleWithLazyInit.INSTANCE",
+ "SimpleWithLazyInit SimpleWithLazyInit.INSTANCE",
+ "SimpleWithLazyInit SimpleWithLazyInit.INSTANCE",
+ "SimpleWithLazyInit SimpleWithLazyInit.INSTANCE",
+ "SimpleWithLazyInit SimpleWithLazyInit.INSTANCE",
+ "SimpleWithLazyInit SimpleWithLazyInit.INSTANCE",
+ "VIRTUAL: String SimpleWithLazyInit.bar(String)",
+ "VIRTUAL: String SimpleWithLazyInit.foo()"),
+ references(clazz, "testSimpleWithLazyInit", "void"));
+
+ ClassSubject simpleWithLazyInit = inspector.clazz(SimpleWithLazyInit.class);
+ assertFalse(instanceMethods(simpleWithLazyInit).isEmpty());
+ assertThat(simpleWithLazyInit.clinit(), not(isPresent()));
}
@Test
@@ -316,6 +350,7 @@
.filter(method -> isTypeOfInterest(method.holder))
.map(method -> "DIRECT: " + method.toSourceString()))
.map(txt -> txt.replace("java.lang.", ""))
+ .map(txt -> txt.replace("com.android.tools.r8.ir.optimize.staticizer.trivial.", ""))
.map(txt -> txt.replace("com.android.tools.r8.ir.optimize.staticizer.", ""))
.sorted()
.collect(Collectors.toList());
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithLazyInit.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithLazyInit.java
new file mode 100644
index 0000000..056f452
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithLazyInit.java
@@ -0,0 +1,27 @@
+// 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.staticizer.trivial;
+
+import com.android.tools.r8.NeverInline;
+
+public class SimpleWithLazyInit {
+ private static SimpleWithLazyInit INSTANCE = null;
+
+ static SimpleWithLazyInit getInstance() {
+ if (INSTANCE == null) {
+ INSTANCE = new SimpleWithLazyInit();
+ }
+ return INSTANCE;
+ }
+
+ @NeverInline
+ String foo() {
+ return bar("Simple::foo()");
+ }
+
+ @NeverInline
+ String bar(String other) {
+ return "Simple::bar(" + other + ")";
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithThrowingGetter.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithThrowingGetter.java
new file mode 100644
index 0000000..099d692
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithThrowingGetter.java
@@ -0,0 +1,27 @@
+// 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.staticizer.trivial;
+
+import com.android.tools.r8.NeverInline;
+
+public class SimpleWithThrowingGetter {
+ private static SimpleWithThrowingGetter INSTANCE = new SimpleWithThrowingGetter();
+
+ static SimpleWithThrowingGetter getInstance() {
+ if (System.currentTimeMillis() < 0) {
+ throw new AssertionError("This should not happen!");
+ }
+ return INSTANCE;
+ }
+
+ @NeverInline
+ String foo() {
+ return bar("Simple::foo()");
+ }
+
+ @NeverInline
+ String bar(String other) {
+ return "Simple::bar(" + other + ")";
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/TrivialTestClass.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/TrivialTestClass.java
index 964567c..1dca1e0 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/TrivialTestClass.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/TrivialTestClass.java
@@ -20,6 +20,8 @@
test.testSimpleWithSideEffects();
test.testSimpleWithParams();
test.testSimpleWithGetter();
+ test.testSimpleWithThrowingGetter();
+ test.testSimpleWithLazyInit();
}
@NeverInline
@@ -60,5 +62,17 @@
System.out.println(SimpleWithGetter.getInstance().foo());
System.out.println(SimpleWithGetter.getInstance().bar(next()));
}
+
+ @NeverInline
+ private void testSimpleWithThrowingGetter() {
+ System.out.println(SimpleWithThrowingGetter.getInstance().foo());
+ System.out.println(SimpleWithThrowingGetter.getInstance().bar(next()));
+ }
+
+ @NeverInline
+ private void testSimpleWithLazyInit() {
+ System.out.println(SimpleWithLazyInit.getInstance().foo());
+ System.out.println(SimpleWithLazyInit.getInstance().bar(next()));
+ }
}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index fdc4296..6ebd77c 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -836,7 +836,7 @@
method.setCode(ir, allocator, appView);
directMethods[i] = method;
}
- builder.addProgramClass(
+ DexProgramClass programClass =
new DexProgramClass(
type,
null,
@@ -854,7 +854,9 @@
DexEncodedField.EMPTY_ARRAY,
directMethods,
DexEncodedMethod.EMPTY_ARRAY,
- false));
+ false,
+ DexProgramClass::invalidChecksumRequest);
+ builder.addProgramClass(programClass);
}
DirectMappedDexApplication application = builder.build().toDirect();
ApplicationWriter writer =
@@ -863,7 +865,6 @@
null,
options,
null,
- null,
GraphLense.getIdentityLense(),
NamingLens.getIdentityLens(),
null);
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
index 44a973a..77d0846 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -20,6 +20,7 @@
import com.android.tools.r8.retrace.stacktraces.AmbiguousMissingLineStackTrace;
import com.android.tools.r8.retrace.stacktraces.AmbiguousStackTrace;
import com.android.tools.r8.retrace.stacktraces.FileNameExtensionStackTrace;
+import com.android.tools.r8.retrace.stacktraces.InlineFileNameStackTrace;
import com.android.tools.r8.retrace.stacktraces.InlineNoLineNumberStackTrace;
import com.android.tools.r8.retrace.stacktraces.InlineWithLineNumbersStackTrace;
import com.android.tools.r8.retrace.stacktraces.InvalidStackTrace;
@@ -62,6 +63,11 @@
}
@Test
+ public void testInlineFileNameStackTrace() {
+ runRetraceTest(new InlineFileNameStackTrace());
+ }
+
+ @Test
public void testNullLineTrace() {
TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
NullStackTrace nullStackTrace = new NullStackTrace();
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineFileNameStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineFileNameStackTrace.java
new file mode 100644
index 0000000..65f9824
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineFileNameStackTrace.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.retrace.stacktraces;
+
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
+import java.util.List;
+
+public class InlineFileNameStackTrace implements StackTraceForTest {
+
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return Arrays.asList(
+ "Exception in thread \"main\" java.lang.NullPointerException",
+ "\tat com.android.tools.r8.naming.retrace.Main.main(Main.dummy:3)");
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return Arrays.asList(
+ "Exception in thread \"main\" java.lang.NullPointerException",
+ "\tat foo.Bar$Baz.baz(Bar.dummy:0)",
+ "\tat Foo$Bar.bar(Foo.dummy:2)",
+ "\tat com.android.tools.r8.naming.retrace.Main$Foo.method1(Main.dummy:8)",
+ "\tat com.android.tools.r8.naming.retrace.Main.main(Main.dummy:7)");
+ }
+
+ @Override
+ public String mapping() {
+ return StringUtils.lines(
+ "com.android.tools.r8.naming.retrace.Main -> com.android.tools.r8.naming.retrace.Main:",
+ " 3:3:void foo.Bar$Baz.baz(long):0:0 -> main",
+ " 3:3:void Foo$Bar.bar(int):2 -> main",
+ " 3:3:void com.android.tools.r8.naming.retrace.Main$Foo.method1(java.lang.String):8:8"
+ + " -> main",
+ " 3:3:void main(java.lang.String[]):7 -> main");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 0;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/Smali.java b/src/test/java/com/android/tools/r8/utils/Smali.java
index 3d1ec2b..0092be2 100644
--- a/src/test/java/com/android/tools/r8/utils/Smali.java
+++ b/src/test/java/com/android/tools/r8/utils/Smali.java
@@ -117,7 +117,6 @@
null,
options,
null,
- null,
GraphLense.getIdentityLense(),
NamingLens.getIdentityLens(),
null);
diff --git a/tools/internal_test.py b/tools/internal_test.py
index d761a98..9d4bc2b 100755
--- a/tools/internal_test.py
+++ b/tools/internal_test.py
@@ -116,7 +116,7 @@
'--version=%s' % record['version'],
'--no-debug',
'--no-build',
- '--max-memory=%s' % int(record['oom-threshold'] * 1.1)
+ '--max-memory=%s' % int(record['oom-threshold'] * 1.15)
]
def compile_with_memory_min_command(record):
@@ -129,7 +129,7 @@
'--no-debug',
'--no-build',
'--expect-oom',
- '--max-memory=%s' % int(record['oom-threshold'] * 0.9)
+ '--max-memory=%s' % int(record['oom-threshold'] * 0.85)
]
TEST_COMMANDS = [
diff --git a/tools/r8_release.py b/tools/r8_release.py
index 1d3cfbb..3575edf 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -10,11 +10,124 @@
import shutil
import subprocess
import sys
-import tempfile
-import update_prebuilds_in_android
import urllib
+
+import update_prebuilds_in_android
import utils
+R8_DEV_BRANCH = '1.7'
+R8_VERSION_FILE = os.path.join(
+ 'src', 'main', 'java', 'com', 'android', 'tools', 'r8', 'Version.java')
+
+
+def prepare_release(args):
+ if args.version:
+ print "Cannot manually specify version when making a dev release."
+ sys.exit(1)
+
+ def make_release(args):
+ commithash = args.dev_release
+
+ with utils.TempDir() as temp:
+ subprocess.check_call(['git', 'clone', utils.REPO_SOURCE, temp])
+ with utils.ChangedWorkingDirectory(temp):
+ subprocess.check_call([
+ 'git',
+ 'new-branch',
+ '--upstream',
+ 'origin/%s' % R8_DEV_BRANCH,
+ 'dev-release'])
+
+ # Compute the current and new version on the branch.
+ result = None
+ for line in open(R8_VERSION_FILE, 'r'):
+ result = re.match(
+ r'.*LABEL = "%s\.(\d+)\-dev";' % R8_DEV_BRANCH, line)
+ if result:
+ break
+ if not result or not result.group(1):
+ print 'Failed to find version label matching %s(\d+)-dev'\
+ % R8_DEV_BRANCH
+ sys.exit(1)
+ try:
+ patch_version = int(result.group(1))
+ except ValueError:
+ print 'Failed to convert version to integer: %s' % result.group(1)
+
+ old_version = '%s.%s-dev' % (R8_DEV_BRANCH, patch_version)
+ version = '%s.%s-dev' % (R8_DEV_BRANCH, patch_version + 1)
+
+ # Verify that the merge point from master is not empty.
+ merge_diff_output = subprocess.check_output([
+ 'git', 'diff', 'HEAD..%s' % commithash])
+ other_diff = version_change_diff(
+ merge_diff_output, old_version, "master")
+ if not other_diff:
+ print 'Merge point from master (%s)' % commithash, \
+ 'is the same as exiting release (%s).' % old_version
+ sys.exit(1)
+
+ # Merge the desired commit from master on to the branch.
+ subprocess.check_call([
+ 'git', 'merge', '--no-ff', '--no-edit', commithash])
+
+ # Rewrite the version, commit and validate.
+ sed(old_version, version, R8_VERSION_FILE)
+
+ subprocess.check_call([
+ 'git', 'commit', '-a', '-m', '"Version %s"' % version])
+
+ version_diff_output = subprocess.check_output([
+ 'git', 'diff', '%s..HEAD' % commithash])
+
+ invalid = version_change_diff(version_diff_output, "master", version)
+ if invalid:
+ print "Unexpected diff content for line:"
+ print invalid
+ sys.exit(1)
+
+ # Double check that we want to push the release.
+ if not args.dry_run:
+ input = raw_input('Publish dev release version %s [y/N]:' % version)
+ if input != 'y':
+ print 'Aborting dev release for %s' % version
+ sys.exit(1)
+
+ maybe_check_call(args, [
+ 'git', 'push', 'origin', 'HEAD:%s' % R8_DEV_BRANCH])
+ maybe_check_call(args, [
+ 'git', 'tag', '-a', version, '-m', '"%s"' % version])
+ maybe_check_call(args, [
+ 'git', 'push', 'origin', 'refs/tags/%s' % version])
+
+ return "%s dev version %s from hash %s" % (
+ 'DryRun: omitted publish of' if args.dry_run else 'Published',
+ version,
+ commithash)
+
+ return make_release
+
+
+def version_change_diff(diff, old_version, new_version):
+ invalid_line = None
+ for line in diff.splitlines():
+ if line.startswith('- ') and \
+ line != '- public static final String LABEL = "%s";' % old_version:
+ invalid_line = line
+ elif line.startswith('+ ') and \
+ line != '+ public static final String LABEL = "%s";' % new_version:
+ invalid_line = line
+ return invalid_line
+
+
+def maybe_check_call(args, cmd):
+ if args.dry_run:
+ print 'DryRun:', ' '.join(cmd)
+ else:
+ print ' '.join(cmd)
+ return subprocess.check_call(cmd)
+
+
def update_prebuilds(version, checkout):
update_prebuilds_in_android.main_download('', True, 'lib', checkout, version)
@@ -40,10 +153,14 @@
def prepare_aosp(args):
+ assert args.version
assert os.path.exists(args.aosp), "Could not find AOSP path %s" % args.aosp
def release_aosp(options):
print "Releasing for AOSP"
+ if options.dry_run:
+ return 'DryRun: omitting AOSP release for %s' % options.version
+
git_message = ("""Update D8 and R8 to %s
Version: master %s
@@ -80,11 +197,15 @@
def prepare_studio(args):
+ assert args.version
assert os.path.exists(args.studio), ("Could not find STUDIO path %s"
% args.studio)
def release_studio(options):
print "Releasing for STUDIO"
+ if options.dry_run:
+ return 'DryRun: omitting studio release for %s' % options.version
+
git_message = (git_message_dev(options.version)
if 'dev' in options.version
else git_message_release(options.version, options.bug))
@@ -107,7 +228,8 @@
def g4_change(version, r8version):
return subprocess.check_output(
- 'g4 change --desc "Update R8 to version %s %s"' % (version, r8version), shell=True)
+ 'g4 change --desc "Update R8 to version %s %s"' % (version, r8version),
+ shell=True)
def sed(pattern, replace, path):
@@ -129,7 +251,8 @@
'blaze run %s' % target, shell=True, stderr=subprocess.STDOUT)
-def prepare_google3():
+def prepare_google3(args):
+ assert args.version
# Check if an existing client exists.
if ':update-r8:' in subprocess.check_output('g4 myclients', shell=True):
print "Remove the existing 'update-r8' client before continuing."
@@ -137,6 +260,8 @@
def release_google3(options):
print "Releasing for Google 3"
+ if options.dry_run:
+ return 'DryRun: omitting g3 release for %s' % options.version
google3_base = subprocess.check_output(
['p4', 'g4d', '-f', 'update-r8']).rstrip()
@@ -212,8 +337,10 @@
def parse_options():
result = argparse.ArgumentParser(description='Release r8')
- result.add_argument('--version',
- required=True,
+ group = result.add_mutually_exclusive_group()
+ group.add_argument('--dev-release',
+ help='The hash to use for the new dev version of R8')
+ group.add_argument('--version',
help='The new version of R8 (e.g., 1.4.51)')
result.add_argument('--no-sync', '--no_sync',
default=False,
@@ -233,22 +360,37 @@
default=False,
action='store_true',
help='Release for google 3')
+ result.add_argument('--dry-run',
+ default=False,
+ action='store_true',
+ help='Only perform non-commiting tasks and print others.')
args = result.parse_args()
- if not 'dev' in args.version and args.bug == []:
+ if args.version and not 'dev' in args.version and args.bug == []:
print "When releasing a release version add the list of bugs by using '--bug'"
sys.exit(1)
+ if args.version and not 'dev' in args.version and args.google3:
+ print "You should not roll a release version into google 3"
+ sys.exit(1)
+
return args
def main():
args = parse_options()
targets_to_run = []
+
+ if args.dev_release:
+ if args.google3 or args.studio or args.aosp:
+ print 'Cannot create a dev release and roll at the same time.'
+ sys.exit(1)
+ targets_to_run.append(prepare_release(args))
+
if args.google3 or args.studio:
utils.check_prodacces()
if args.google3:
- targets_to_run.append(prepare_google3())
+ targets_to_run.append(prepare_google3(args))
if args.studio:
targets_to_run.append(prepare_studio(args))
if args.aosp:
diff --git a/tools/utils.py b/tools/utils.py
index fb937e6..cede4c7 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -34,6 +34,7 @@
GENERATED_LICENSE_DIR = os.path.join(BUILD, 'generatedLicense')
SRC_ROOT = os.path.join(REPO_ROOT, 'src', 'main', 'java')
TEST_ROOT = os.path.join(REPO_ROOT, 'src', 'test', 'java')
+REPO_SOURCE = 'https://r8.googlesource.com/r8'
D8 = 'd8'
R8 = 'r8'