Merge commit '1d6b2cf4236058a6792f8c83f265192520e10a22' into dev-release
diff --git a/doc/compilerdump.md b/doc/compilerdump.md
new file mode 100644
index 0000000..2db569a
--- /dev/null
+++ b/doc/compilerdump.md
@@ -0,0 +1,80 @@
+# Compiler-input dumps
+
+The D8 and R8 compilers support generating a compiler-input dump for use in
+reproducing compiler issues.
+
+
+## The content of a dump
+
+The dump contains almost all of the inputs that are given to the compiler as
+part of compilation. In particular, it contains *all* class definitions in the
+form of Java classfiles (i.e., bytecode, not the Java source files).
+In addition to the classfiles, the dump also includes Java resources, the
+compiler type, version, and flags, such as `--debug` or `--release`,
+main-dex lists or rules, and more. For R8 the dump also contains the full
+concatenation of all keep rules.
+
+The dump is a zip file containing the above. You should unzip it and review
+the content locally. The program, classpath and library content will be in
+nested zip files. The remaining content is in plain text files.
+
+
+## Generating a dump
+
+To generate a dump file, run the compiler with the
+`com.android.tools.r8.dumpinputtofile` system property set:
+
+```
+java -cp r8.jar -Dcom.android.tools.r8.dumpinputtofile=mydump.zip com.android.tools.r8.D8 <other-compiler-args>
+```
+
+This will generate a dump file `mydump.zip` and exit the compiler with a
+non-zero exit value printing an error message about the location of the dump
+file that was written.
+
+For some builds, there may be many compilations taking place which cannot be
+easily isolated as individual compilation steps. If so, the system property
+`com.android.tools.r8.dumpinputtodirectory` can be set to a directory instead.
+Doing so will dump the inputs of each compilation to the directory in
+individual zip files and they will be named using a timestamp in an attempt
+to maintain an order. The compiler will compile as usual thus not disrupting
+the build.
+
+### Generating a dump with Gradle and the Android Studio Plugin
+
+To generate a dump from studio, the system property should be set for the
+build command. This can typically be done by amending the command-line gradle
+build-target command. Say your build target is `assembleRelease`, you would run:
+
+```
+./gradlew assembleRelease -Dcom.android.tools.r8.dumpinputtofile=mydump.zip --no-daemon
+```
+
+If the build is a debug build, such as `assembleDebug`, then it will likely be an
+incremental D8 build in which case the best option is to provide a dump
+directory and then locate the particular dump file associated with the
+problematic compilation (if the compilation fails, the interesting dump will
+hopefully be the last dump):
+
+```
+./gradlew assembleDebug -Dcom.android.tools.r8.dumpinputtodirectory=mydumps/ --no-daemon
+```
+
+
+## Reproducing using a dump
+
+To reproduce a compiler issue with a dump use the script `tools/compiledump.py`
+from the R8 repository. Note that the script is *not* a standalone script so you
+will need a full checkout of the R8 project.
+
+Reproducing should then be just:
+```
+./tools/compiledump.py -d mydump.zip
+```
+
+Depending on the compiler version that generated the dump additional flags may
+be needed to specify the compiler and its version. Run `--help` for a list of
+options.
+
+The flags can also be used to override the setting specified in the dump.
+Doing so allows compiling with other compiler versions, or other settings.
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 4fdf194..cae27a3 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -196,7 +196,7 @@
clazz -> {
ProgramMethod classInitializer = clazz.getProgramClassInitializer();
if (classInitializer != null) {
- analysis.processNewlyLiveMethod(classInitializer);
+ analysis.processNewlyLiveMethod(classInitializer, clazz);
}
},
executor);
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index ea95988..1dc771e 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.dex.Marker.Tool;
import com.android.tools.r8.errors.CheckDiscardDiagnostic;
-import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppServices;
@@ -43,7 +42,7 @@
import com.android.tools.r8.graph.classmerging.StaticallyMergedClasses;
import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMergerGraphLens;
+import com.android.tools.r8.horizontalclassmerging.HorizontalClassMergerResult;
import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.conversion.IRConverter;
@@ -96,7 +95,7 @@
import com.android.tools.r8.shaking.MainDexClasses;
import com.android.tools.r8.shaking.MainDexListBuilder;
import com.android.tools.r8.shaking.MainDexTracingResult;
-import com.android.tools.r8.shaking.ProguardClassFilter;
+import com.android.tools.r8.shaking.MissingClasses;
import com.android.tools.r8.shaking.ProguardConfigurationRule;
import com.android.tools.r8.shaking.ProguardConfigurationUtils;
import com.android.tools.r8.shaking.RootSetBuilder;
@@ -122,7 +121,6 @@
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
import com.google.common.io.ByteStreams;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
@@ -252,13 +250,6 @@
}
}
- private Set<DexType> filterMissingClasses(Set<DexType> missingClasses,
- ProguardClassFilter dontWarnPatterns) {
- Set<DexType> result = new HashSet<>(missingClasses);
- dontWarnPatterns.filterOutMatches(result);
- return result;
- }
-
static void runForTesting(AndroidApp app, InternalOptions options)
throws CompilationFailedException {
ExecutorService executor = ThreadUtils.getExecutorService(options);
@@ -329,35 +320,19 @@
List<ProguardConfigurationRule> synthesizedProguardRules = new ArrayList<>();
timing.begin("Strip unused code");
Set<DexType> classesToRetainInnerClassAttributeFor = null;
- Set<DexType> missingClasses = null;
RuntimeTypeCheckInfo.Builder classMergingEnqueuerExtensionBuilder =
new RuntimeTypeCheckInfo.Builder(appView.dexItemFactory());
try {
- // TODO(b/154849103): Find a better way to determine missing classes.
- missingClasses = new SubtypingInfo(appView).getMissingClasses();
- missingClasses = filterMissingClasses(
- missingClasses, options.getProguardConfiguration().getDontWarnPatterns());
- if (!missingClasses.isEmpty()) {
- missingClasses.forEach(
- clazz -> {
- options.reporter.warning(
- new StringDiagnostic("Missing class: " + clazz.toSourceString()));
- });
- if (!options.ignoreMissingClasses) {
- DexType missingClass = missingClasses.iterator().next();
- if (missingClasses.size() == 1) {
- throw new CompilationError(
- "Compilation can't be completed because the class `"
- + missingClass.toSourceString()
- + "` is missing.");
- } else {
- throw new CompilationError(
- "Compilation can't be completed because `" + missingClass.toSourceString()
- + "` and " + (missingClasses.size() - 1) + " other classes are missing.");
- }
- }
+ // TODO(b/154849103): Remove once reported by the Enqueuer.
+ if (!appView.testing().enableExperimentalMissingClassesReporting) {
+ appView.setAppInfo(
+ appView
+ .appInfo()
+ .rebuildWithClassHierarchy(
+ MissingClasses.builderForInitialMissingClasses()
+ .addNewMissingClasses(new SubtypingInfo(appView).getMissingClasses())
+ .reportMissingClasses(options)));
}
- options.reporter.failIfPendingErrors();
// Add synthesized -assumenosideeffects from min api if relevant.
if (options.isGeneratingDex()) {
@@ -392,9 +367,6 @@
assert appView.rootSet().verifyKeptTypesAreLive(appViewWithLiveness.appInfo());
assert appView.rootSet().verifyKeptItemsAreKept(appView);
- missingClasses =
- Sets.union(missingClasses, appViewWithLiveness.appInfo().getMissingTypes());
-
appView.rootSet().checkAllRulesAreUsed(options);
if (options.proguardSeedsConsumer != null) {
@@ -588,20 +560,26 @@
HorizontalClassMerger merger = new HorizontalClassMerger(appViewWithLiveness);
DirectMappedDexApplication.Builder appBuilder =
appView.appInfo().app().asDirect().builder();
- HorizontalClassMergerGraphLens lens =
+ HorizontalClassMergerResult horizontalClassMergerResult =
merger.run(appBuilder, mainDexTracingResult, runtimeTypeCheckInfo);
- if (lens != null) {
- DirectMappedDexApplication app = appBuilder.build();
+ if (horizontalClassMergerResult != null) {
+ // Must rewrite AppInfoWithLiveness before pruning the merged classes, to ensure that
+ // allocations sites, fields accesses, etc. are correctly transferred to the target
+ // classes.
+ appView.rewriteWithLensAndApplication(
+ horizontalClassMergerResult.getGraphLens(), appBuilder.build());
+ horizontalClassMergerResult
+ .getFieldAccessInfoCollectionModifier()
+ .modify(appViewWithLiveness);
+
appView.pruneItems(
PrunedItems.builder()
- .setPrunedApp(app)
+ .setPrunedApp(appView.appInfo().app())
.addRemovedClasses(appView.horizontallyMergedClasses().getSources())
.addNoLongerSyntheticItems(appView.horizontallyMergedClasses().getTargets())
.build());
- appView.rewriteWithLens(lens);
- // Only required for class merging, clear instance to save memory.
- runtimeTypeCheckInfo = null;
+ mainDexTracingResult = horizontalClassMergerResult.getMainDexTracingResult();
}
timing.end();
} else {
@@ -717,7 +695,6 @@
appView,
new SubtypingInfo(appView),
keptGraphConsumer,
- missingClasses,
prunedTypes);
appView.setAppInfo(
enqueuer.traceApplication(
diff --git a/src/main/java/com/android/tools/r8/dex/DebugBytecodeWriter.java b/src/main/java/com/android/tools/r8/dex/DebugBytecodeWriter.java
index 06dbcfe..2c3e18a 100644
--- a/src/main/java/com/android/tools/r8/dex/DebugBytecodeWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/DebugBytecodeWriter.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.DexDebugInfo;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.ObjectToOffsetMapping;
import com.android.tools.r8.utils.LebUtils;
import java.nio.ByteBuffer;
@@ -15,12 +16,15 @@
public class DebugBytecodeWriter {
private final ObjectToOffsetMapping mapping;
+ private final GraphLens graphLens;
private final DexDebugInfo info;
private ByteBuffer buffer;
- public DebugBytecodeWriter(DexDebugInfo info, ObjectToOffsetMapping mapping) {
+ public DebugBytecodeWriter(
+ DexDebugInfo info, ObjectToOffsetMapping mapping, GraphLens graphLens) {
this.info = info;
this.mapping = mapping;
+ this.graphLens = graphLens;
// Never allocate a zero-sized buffer, as we need to write the header, and the growth policy
// requires it to have a positive capacity.
this.buffer = ByteBuffer.allocate(info.events.length * 5 + 4);
@@ -35,7 +39,7 @@
}
// Body.
for (DexDebugEvent event : info.events) {
- event.writeOn(this, mapping);
+ event.writeOn(this, mapping, graphLens);
}
// Tail.
putByte(Constants.DBG_END_SEQUENCE);
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 48fded1..2408b27 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -53,6 +53,8 @@
import com.android.tools.r8.graph.FieldAccessFlags;
import com.android.tools.r8.graph.GenericSignature;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.OffsetToObjectMapping;
@@ -62,7 +64,6 @@
import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Pair;
-import com.android.tools.r8.utils.Reporter;
import com.google.common.io.ByteStreams;
import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
@@ -636,18 +637,19 @@
}
}
DexAnnotationSet fieldAnnotations = annotationIterator.getNextFor(field);
- String fieldSignature = DexAnnotation.getSignature(fieldAnnotations, dexItemFactory);
- if (fieldSignature != null) {
- fieldAnnotations = fieldAnnotations.getWithout(dexItemFactory.annotationSignature);
+ FieldTypeSignature fieldTypeSignature = FieldTypeSignature.noSignature();
+ if (!options.passthroughDexCode) {
+ String fieldSignature = DexAnnotation.getSignature(fieldAnnotations, dexItemFactory);
+ if (fieldSignature != null) {
+ fieldAnnotations = fieldAnnotations.getWithout(dexItemFactory.annotationSignature);
+ fieldTypeSignature =
+ GenericSignature.parseFieldTypeSignature(
+ field.name.toString(), fieldSignature, origin, dexItemFactory, options.reporter);
+ }
}
fields[i] =
new DexEncodedField(
- field,
- accessFlags,
- GenericSignature.parseFieldTypeSignature(
- field.name.toString(), fieldSignature, origin, dexItemFactory, options.reporter),
- fieldAnnotations,
- staticValue);
+ field, accessFlags, fieldTypeSignature, fieldAnnotations, staticValue);
}
return fields;
}
@@ -676,20 +678,25 @@
DexMethod method = indexedItems.getMethod(methodIndex);
accessFlags.setConstructor(method, dexItemFactory);
DexAnnotationSet methodAnnotations = annotationIterator.getNextFor(method);
- String methodSignature = DexAnnotation.getSignature(methodAnnotations, dexItemFactory);
- if (methodSignature != null) {
- methodAnnotations = methodAnnotations.getWithout(dexItemFactory.annotationSignature);
- }
- methods[i] =
- new DexEncodedMethod(
- method,
- accessFlags,
+ MethodTypeSignature methodTypeSignature = MethodTypeSignature.noSignature();
+ if (!options.passthroughDexCode) {
+ String methodSignature = DexAnnotation.getSignature(methodAnnotations, dexItemFactory);
+ if (methodSignature != null) {
+ methodAnnotations = methodAnnotations.getWithout(dexItemFactory.annotationSignature);
+ methodTypeSignature =
GenericSignature.parseMethodSignature(
method.name.toString(),
methodSignature,
origin,
dexItemFactory,
- options.reporter),
+ options.reporter);
+ }
+ }
+ methods[i] =
+ new DexEncodedMethod(
+ method,
+ accessFlags,
+ methodTypeSignature,
methodAnnotations,
parameterAnnotationsIterator.getNextFor(method),
code);
@@ -783,8 +790,7 @@
}
AttributesAndAnnotations attrs =
- new AttributesAndAnnotations(
- type, origin, annotationsDirectory.clazz, options.itemFactory, options.reporter);
+ new AttributesAndAnnotations(type, origin, annotationsDirectory.clazz, options);
Long finalChecksum = checksum;
ChecksumSupplier checksumSupplier =
@@ -1380,15 +1386,12 @@
}
public AttributesAndAnnotations(
- DexType type,
- Origin origin,
- DexAnnotationSet annotations,
- DexItemFactory factory,
- Reporter reporter) {
+ DexType type, Origin origin, DexAnnotationSet annotations, InternalOptions options) {
this.originalAnnotations = annotations;
DexType enclosingClass = null;
DexMethod enclosingMethod = null;
List<DexType> memberClasses = null;
+ DexItemFactory factory = options.dexItemFactory();
for (int i = 0; i < annotations.annotations.length; i++) {
DexAnnotation annotation = annotations.annotations[i];
@@ -1415,12 +1418,13 @@
} else {
memberClasses.addAll(members);
}
- } else if (DexAnnotation.isSignatureAnnotation(annotation, factory)) {
+ } else if (DexAnnotation.isSignatureAnnotation(annotation, factory)
+ && !options.passthroughDexCode) {
ensureAnnotations(i);
String signature = DexAnnotation.getSignature(annotation);
classSignature =
GenericSignature.parseClassSignature(
- type.getName(), signature, origin, factory, reporter);
+ type.getName(), signature, origin, factory, options.reporter);
} else {
copyAnnotation(annotation);
}
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index 6f2f904..ef6b7cd 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -194,7 +194,7 @@
for (ProgramDexCode code : codes) {
DexDebugInfoForWriting info = code.getCode().getDebugInfoForWriting();
if (info != null && seen.add(info)) {
- writeDebugItem(info);
+ writeDebugItem(info, graphLens);
}
}
}
@@ -487,9 +487,9 @@
dest.putInt(mixedSectionOffsets.getOffsetFor(staticFieldValues.get(clazz)));
}
- private void writeDebugItem(DexDebugInfo debugInfo) {
+ private void writeDebugItem(DexDebugInfo debugInfo, GraphLens graphLens) {
mixedSectionOffsets.setOffsetFor(debugInfo, dest.position());
- dest.putBytes(new DebugBytecodeWriter(debugInfo, mapping).generate());
+ dest.putBytes(new DebugBytecodeWriter(debugInfo, mapping, graphLens).generate());
}
private void writeCodeItem(ProgramDexCode code) {
diff --git a/src/main/java/com/android/tools/r8/graph/AccessFlags.java b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
index e47b428..98806f6 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
@@ -204,8 +204,9 @@
set(Constants.ACC_FINAL);
}
- public void unsetFinal() {
+ public T unsetFinal() {
unset(Constants.ACC_FINAL);
+ return self();
}
public boolean isSynthetic() {
@@ -216,8 +217,9 @@
set(Constants.ACC_SYNTHETIC);
}
- public void unsetSynthetic() {
+ public T unsetSynthetic() {
unset(Constants.ACC_SYNTHETIC);
+ return self();
}
public void demoteFromSynthetic() {
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
index b6100c2..e747aa0 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -17,11 +17,14 @@
import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.shaking.MainDexClasses;
+import com.android.tools.r8.shaking.MissingClasses;
import com.android.tools.r8.synthesis.CommittedItems;
import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.TraversalContinuation;
+import com.android.tools.r8.utils.TriConsumer;
+import com.android.tools.r8.utils.TriFunction;
import com.android.tools.r8.utils.WorkList;
import com.google.common.collect.Sets;
import java.util.ArrayDeque;
@@ -33,8 +36,6 @@
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
-import java.util.function.BiConsumer;
-import java.util.function.BiFunction;
import java.util.function.Function;
/* Specific subclass of AppInfo designed to support desugaring in D8. Desugaring requires a
@@ -56,24 +57,34 @@
return new AppInfoWithClassHierarchy(
SyntheticItems.createInitialSyntheticItems(application),
classToFeatureSplitMap,
- mainDexClasses);
+ mainDexClasses,
+ MissingClasses.empty());
}
private final ClassToFeatureSplitMap classToFeatureSplitMap;
+ /** Set of types that are mentioned in the program, but for which no definition exists. */
+ // TODO(b/175659048): Consider hoisting to AppInfo to allow using MissingClasses in D8 desugar.
+ private final MissingClasses missingClasses;
+
// For AppInfoWithLiveness subclass.
protected AppInfoWithClassHierarchy(
CommittedItems committedItems,
ClassToFeatureSplitMap classToFeatureSplitMap,
- MainDexClasses mainDexClasses) {
+ MainDexClasses mainDexClasses,
+ MissingClasses missingClasses) {
super(committedItems, mainDexClasses);
this.classToFeatureSplitMap = classToFeatureSplitMap;
+ this.missingClasses = missingClasses;
}
// For desugaring.
private AppInfoWithClassHierarchy(CreateDesugaringViewOnAppInfo witness, AppInfo appInfo) {
super(witness, appInfo);
this.classToFeatureSplitMap = ClassToFeatureSplitMap.createEmptyClassToFeatureSplitMap();
+ // TODO(b/175659048): Migrate the reporting of missing classes in D8 desugar to MissingClasses,
+ // and use the missing classes from AppInfo instead of MissingClasses.empty().
+ this.missingClasses = MissingClasses.empty();
}
public static AppInfoWithClassHierarchy createForDesugaring(AppInfo appInfo) {
@@ -82,7 +93,16 @@
}
public final AppInfoWithClassHierarchy rebuildWithClassHierarchy(CommittedItems commit) {
- return new AppInfoWithClassHierarchy(commit, getClassToFeatureSplitMap(), getMainDexClasses());
+ return new AppInfoWithClassHierarchy(
+ commit, getClassToFeatureSplitMap(), getMainDexClasses(), getMissingClasses());
+ }
+
+ public final AppInfoWithClassHierarchy rebuildWithClassHierarchy(MissingClasses missingClasses) {
+ return new AppInfoWithClassHierarchy(
+ getSyntheticItems().commit(app()),
+ getClassToFeatureSplitMap(),
+ getMainDexClasses(),
+ missingClasses);
}
public AppInfoWithClassHierarchy rebuildWithClassHierarchy(
@@ -90,7 +110,8 @@
return new AppInfoWithClassHierarchy(
getSyntheticItems().commit(fn.apply(app())),
getClassToFeatureSplitMap(),
- getMainDexClasses());
+ getMainDexClasses(),
+ getMissingClasses());
}
@Override
@@ -104,13 +125,18 @@
return new AppInfoWithClassHierarchy(
getSyntheticItems().commitPrunedItems(prunedItems),
getClassToFeatureSplitMap().withoutPrunedItems(prunedItems),
- getMainDexClasses().withoutPrunedItems(prunedItems));
+ getMainDexClasses().withoutPrunedItems(prunedItems),
+ getMissingClasses());
}
public ClassToFeatureSplitMap getClassToFeatureSplitMap() {
return classToFeatureSplitMap;
}
+ public MissingClasses getMissingClasses() {
+ return missingClasses;
+ }
+
@Override
public boolean hasClassHierarchy() {
assert checkIfObsolete();
@@ -131,7 +157,7 @@
* result of the traversal is BREAK iff the function returned BREAK.
*/
public TraversalContinuation traverseSuperTypes(
- final DexClass clazz, BiFunction<DexType, Boolean, TraversalContinuation> fn) {
+ final DexClass clazz, TriFunction<DexType, DexClass, Boolean, TraversalContinuation> fn) {
// We do an initial zero-allocation pass over the class super chain as it does not require a
// worklist/seen-set. Only if the traversal is not aborted and there actually are interfaces,
// do we continue traversal over the interface types. This is assuming that the second pass
@@ -144,7 +170,7 @@
if (currentClass.superType == null) {
break;
}
- TraversalContinuation stepResult = fn.apply(currentClass.superType, false);
+ TraversalContinuation stepResult = fn.apply(currentClass.superType, currentClass, false);
if (stepResult.shouldBreak()) {
return stepResult;
}
@@ -163,7 +189,7 @@
while (currentClass != null) {
for (DexType iface : currentClass.interfaces.values) {
if (seen.add(iface)) {
- TraversalContinuation stepResult = fn.apply(iface, true);
+ TraversalContinuation stepResult = fn.apply(iface, currentClass, true);
if (stepResult.shouldBreak()) {
return stepResult;
}
@@ -183,7 +209,7 @@
if (definition != null) {
for (DexType iface : definition.interfaces.values) {
if (seen.add(iface)) {
- TraversalContinuation stepResult = fn.apply(iface, true);
+ TraversalContinuation stepResult = fn.apply(iface, definition, true);
if (stepResult.shouldBreak()) {
return stepResult;
}
@@ -200,11 +226,11 @@
*
* <p>Same as traverseSuperTypes, but unconditionally visits all.
*/
- public void forEachSuperType(final DexClass clazz, BiConsumer<DexType, Boolean> fn) {
+ public void forEachSuperType(DexClass clazz, TriConsumer<DexType, DexClass, Boolean> fn) {
traverseSuperTypes(
clazz,
- (type, isInterface) -> {
- fn.accept(type, isInterface);
+ (superType, subclass, isInterface) -> {
+ fn.accept(superType, subclass, isInterface);
return CONTINUE;
});
}
@@ -241,8 +267,7 @@
}
// TODO(b/123506120): Report missing types when the predicate is inconclusive.
return traverseSuperTypes(
- clazz,
- (type, isInterface) -> type == supertype ? BREAK : CONTINUE)
+ clazz, (superType, subclass, isInterface) -> superType == supertype ? BREAK : CONTINUE)
.shouldBreak();
}
@@ -279,11 +304,13 @@
if (clazz.isInterface()) {
interfaces.add(type);
}
- forEachSuperType(clazz, (dexType, isInterface) -> {
- if (isInterface) {
- interfaces.add(dexType);
- }
- });
+ forEachSuperType(
+ clazz,
+ (superType, subclass, isInterface) -> {
+ if (isInterface) {
+ interfaces.add(superType);
+ }
+ });
return interfaces;
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index d51e21c..6bedacf 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.classmerging.StaticallyMergedClasses;
import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
+import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraintFactory;
import com.android.tools.r8.ir.analysis.proto.EnumLiteProtoShrinker;
import com.android.tools.r8.ir.analysis.proto.GeneratedExtensionRegistryShrinker;
import com.android.tools.r8.ir.analysis.proto.GeneratedMessageLiteBuilderShrinker;
@@ -67,6 +68,8 @@
private final InstanceFieldInitializationInfoFactory instanceFieldInitializationInfoFactory =
new InstanceFieldInitializationInfoFactory();
private final MethodProcessingId.Factory methodProcessingIdFactory;
+ private final SimpleInliningConstraintFactory simpleInliningConstraintFactory =
+ new SimpleInliningConstraintFactory();
// Desugaring.
public final PrefixRewritingMapper rewritePrefix;
@@ -187,6 +190,10 @@
return methodProcessingIdFactory;
}
+ public SimpleInliningConstraintFactory simpleInliningConstraintFactory() {
+ return simpleInliningConstraintFactory;
+ }
+
public T appInfo() {
assert !appInfo.hasClassHierarchy() || enableWholeProgramOptimizations();
return appInfo;
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 2e1eeb6..eb83898 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -344,26 +344,34 @@
Map<Integer, DebugLocalInfo> parameterInfo = method.getDefinition().getParameterInfo();
for (Entry<Integer, DebugLocalInfo> entry : parameterInfo.entrySet()) {
writeLocalVariableEntry(
- visitor, namingLens, entry.getValue(), parameterLabel, parameterLabel, entry.getKey());
+ visitor,
+ graphLens,
+ namingLens,
+ entry.getValue(),
+ parameterLabel,
+ parameterLabel,
+ entry.getKey());
}
} else {
for (LocalVariableInfo local : localVariables) {
writeLocalVariableEntry(
- visitor, namingLens, local.local, local.start, local.end, local.index);
+ visitor, graphLens, namingLens, local.local, local.start, local.end, local.index);
}
}
}
private void writeLocalVariableEntry(
MethodVisitor visitor,
+ GraphLens graphLens,
NamingLens namingLens,
DebugLocalInfo info,
CfLabel start,
CfLabel end,
int index) {
+ DexType rewrittenType = graphLens.lookupType(info.type);
visitor.visitLocalVariable(
info.name.toString(),
- namingLens.lookupDescriptor(info.type).toString(),
+ namingLens.lookupDescriptor(rewrittenType).toString(),
info.signature == null ? null : info.signature.toString(),
start.getLabel(),
end.getLabel(),
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java
index 535c08f..b0d84f9 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -57,10 +57,15 @@
return false;
}
+ public boolean isHorizontalClassMergingCode() {
+ return false;
+ }
+
public boolean isOutlineCode() {
return false;
}
+
/** Estimate the number of IR instructions emitted by buildIR(). */
public int estimatedSizeForInlining() {
return Integer.MAX_VALUE;
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index be3a802..82faf34 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -381,6 +381,10 @@
assert verifyNoDuplicateFields();
}
+ public void clearInstanceFields() {
+ instanceFields = DexEncodedField.EMPTY_ARRAY;
+ }
+
private boolean verifyCorrectnessOfFieldHolder(DexEncodedField field) {
assert field.getHolderType() == type
: "Expected field `"
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndField.java b/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
index c0a3125..bba0f82 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
@@ -24,6 +24,10 @@
return getDefinition().getAccessFlags();
}
+ public DexType getType() {
+ return getReference().getType();
+ }
+
public boolean isProgramField() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
index 9dba226..b213660 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
@@ -72,7 +72,8 @@
internalAcceptHashing(visitor);
}
- public abstract void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping);
+ public abstract void writeOn(
+ DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens);
public abstract void accept(DexDebugEventVisitor visitor);
@@ -89,7 +90,8 @@
public final int delta;
@Override
- public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+ public void writeOn(
+ DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
writer.putByte(Constants.DBG_ADVANCE_PC);
writer.putUleb128(delta);
}
@@ -138,7 +140,8 @@
}
@Override
- public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+ public void writeOn(
+ DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
writer.putByte(Constants.DBG_SET_PROLOGUE_END);
}
@@ -182,7 +185,8 @@
}
@Override
- public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+ public void writeOn(
+ DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
writer.putByte(Constants.DBG_SET_EPILOGUE_BEGIN);
}
@@ -227,7 +231,8 @@
}
@Override
- public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+ public void writeOn(
+ DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
writer.putByte(Constants.DBG_ADVANCE_LINE);
writer.putSleb128(delta);
}
@@ -294,13 +299,14 @@
}
@Override
- public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+ public void writeOn(
+ DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
writer.putByte(signature == null
? Constants.DBG_START_LOCAL
: Constants.DBG_START_LOCAL_EXTENDED);
writer.putUleb128(registerNum);
writer.putString(name);
- writer.putType(type);
+ writer.putType(graphLens.lookupType(type));
if (signature != null) {
writer.putString(signature);
}
@@ -364,7 +370,8 @@
}
@Override
- public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+ public void writeOn(
+ DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
writer.putByte(Constants.DBG_END_LOCAL);
writer.putUleb128(registerNum);
}
@@ -410,7 +417,8 @@
}
@Override
- public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+ public void writeOn(
+ DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
writer.putByte(Constants.DBG_RESTART_LOCAL);
writer.putUleb128(registerNum);
}
@@ -456,7 +464,8 @@
}
@Override
- public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+ public void writeOn(
+ DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
writer.putByte(Constants.DBG_SET_FILE);
writer.putString(fileName);
}
@@ -514,7 +523,8 @@
}
@Override
- public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+ public void writeOn(
+ DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
// CallerPosition will not be written.
}
@@ -574,7 +584,8 @@
}
@Override
- public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+ public void writeOn(
+ DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
writer.putByte(value);
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index 6839371..fc0eeb0 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -158,6 +158,10 @@
return field;
}
+ public DexType getType() {
+ return getReference().getType();
+ }
+
@Override
public boolean isDexEncodedField() {
return true;
@@ -216,6 +220,10 @@
return isStatic();
}
+ public boolean isSynthetic() {
+ return accessFlags.isSynthetic();
+ }
+
public boolean isVolatile() {
return accessFlags.isVolatile();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index fb1ad730..4acd029 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -207,6 +207,16 @@
return accessFlags;
}
+ public DexType getArgumentType(int argumentIndex) {
+ if (isStatic()) {
+ return getReference().getParameter(argumentIndex);
+ }
+ if (argumentIndex == 0) {
+ return getHolderType();
+ }
+ return getReference().getParameter(argumentIndex - 1);
+ }
+
public CompilationState getCompilationState() {
return compilationState;
}
@@ -605,6 +615,10 @@
return (accessFlags.isPrivate() || accessFlags.isConstructor()) && !accessFlags.isStatic();
}
+ public boolean isInstance() {
+ return !isStatic();
+ }
+
@Override
public boolean isStatic() {
checkIfObsolete();
@@ -1553,7 +1567,10 @@
annotations = from.annotations();
code = from.code;
compilationState = CompilationState.NOT_PROCESSED;
- optimizationInfo = from.optimizationInfo.mutableCopy();
+ optimizationInfo =
+ from.optimizationInfo.isDefaultMethodOptimizationInfo()
+ ? DefaultMethodOptimizationInfo.getInstance()
+ : from.optimizationInfo.mutableCopy();
kotlinMemberInfo = from.kotlinMemberInfo;
classFileVersion = from.classFileVersion;
this.d8R8Synthesized = d8R8Synthesized;
@@ -1568,6 +1585,13 @@
}
}
+ public Builder fixupOptimizationInfo(Consumer<UpdatableMethodOptimizationInfo> consumer) {
+ if (optimizationInfo.isUpdatableMethodOptimizationInfo()) {
+ consumer.accept(optimizationInfo.asUpdatableMethodOptimizationInfo());
+ }
+ return this;
+ }
+
public void setAccessFlags(MethodAccessFlags accessFlags) {
this.accessFlags = accessFlags.copy();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexField.java b/src/main/java/com/android/tools/r8/graph/DexField.java
index 70bb89b..e9c2fc5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexField.java
@@ -57,6 +57,11 @@
}
@Override
+ public <T> T apply(Function<DexField, T> fieldConsumer, Function<DexMethod, T> methodConsumer) {
+ return fieldConsumer.apply(this);
+ }
+
+ @Override
public <T> T apply(
Function<DexType, T> classConsumer,
Function<DexField, T> fieldConsumer,
@@ -170,6 +175,10 @@
return dexItemFactory.createField(holder, type, name);
}
+ public DexField withType(DexType type, DexItemFactory dexItemFactory) {
+ return dexItemFactory.createField(holder, type, name);
+ }
+
public FieldReference asFieldReference() {
return Reference.field(
Reference.classFromDescriptor(holder.toDescriptorString()),
diff --git a/src/main/java/com/android/tools/r8/graph/DexMember.java b/src/main/java/com/android/tools/r8/graph/DexMember.java
index 790fd33..1c02f3b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMember.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.graph;
import com.google.common.collect.Iterables;
+import java.util.function.Function;
public abstract class DexMember<D extends DexEncodedMember<D, R>, R extends DexMember<D, R>>
extends DexReference implements NamingLensComparable<R> {
@@ -18,6 +19,9 @@
this.name = name;
}
+ public abstract <T> T apply(
+ Function<DexField, T> fieldConsumer, Function<DexMethod, T> methodConsumer);
+
public abstract DexEncodedMember<?, ?> lookupOnClass(DexClass clazz);
public abstract ProgramMember<?, ?> lookupOnProgramClass(DexProgramClass clazz);
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index 8ab4935..e00c1fe 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -67,6 +67,11 @@
}
@Override
+ public <T> T apply(Function<DexField, T> fieldConsumer, Function<DexMethod, T> methodConsumer) {
+ return methodConsumer.apply(this);
+ }
+
+ @Override
public <T> T apply(
Function<DexType, T> classConsumer,
Function<DexField, T> fieldConsumer,
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 71d89ef..2a6ebf5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -144,16 +144,21 @@
synthesizedDirectlyFrom.forEach(this::addSynthesizedFrom);
}
- public void forEachProgramField(Consumer<ProgramField> consumer) {
+ public void forEachProgramField(Consumer<? super ProgramField> consumer) {
forEachField(field -> consumer.accept(new ProgramField(this, field)));
}
- public void forEachProgramMethod(Consumer<ProgramMethod> consumer) {
+ public void forEachProgramMember(Consumer<? super ProgramMember<?, ?>> consumer) {
+ forEachProgramField(consumer);
+ forEachProgramMethod(consumer);
+ }
+
+ public void forEachProgramMethod(Consumer<? super ProgramMethod> consumer) {
forEachProgramMethodMatching(alwaysTrue(), consumer);
}
public void forEachProgramMethodMatching(
- Predicate<DexEncodedMethod> predicate, Consumer<ProgramMethod> consumer) {
+ Predicate<DexEncodedMethod> predicate, Consumer<? super ProgramMethod> consumer) {
methodCollection.forEachMethodMatching(
predicate, method -> consumer.accept(new ProgramMethod(this, method)));
}
@@ -180,6 +185,16 @@
predicate, method -> consumer.accept(new ProgramMethod(this, method)));
}
+ public void forEachProgramInstanceInitializer(Consumer<ProgramMethod> consumer) {
+ forEachProgramInstanceInitializerMatching(alwaysTrue(), consumer);
+ }
+
+ public void forEachProgramInstanceInitializerMatching(
+ Predicate<DexEncodedMethod> predicate, Consumer<ProgramMethod> consumer) {
+ forEachProgramDirectMethodMatching(
+ method -> method.isInstanceInitializer() && predicate.test(method), consumer);
+ }
+
public void forEachProgramVirtualMethod(Consumer<ProgramMethod> consumer) {
forEachProgramVirtualMethodMatching(alwaysTrue(), consumer);
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index 249439c..376abd6 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -115,18 +115,21 @@
}
public boolean isAlwaysNull(AppView<AppInfoWithLiveness> appView) {
- if (isClassType()) {
- DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(this));
- if (clazz == null) {
- return false;
- }
- if (appView.options().enableUninstantiatedTypeOptimizationForInterfaces) {
- return !appView.appInfo().isInstantiatedDirectlyOrIndirectly(clazz);
- } else {
- return !clazz.isInterface() && !appView.appInfo().isInstantiatedDirectlyOrIndirectly(clazz);
- }
+ return isAlwaysNull(appView.appInfo());
+ }
+
+ public boolean isAlwaysNull(AppInfoWithLiveness appInfo) {
+ if (!isClassType()) {
+ return false;
}
- return false;
+ DexProgramClass clazz = asProgramClassOrNull(appInfo.definitionFor(this));
+ if (clazz == null) {
+ return false;
+ }
+ if (appInfo.options().enableUninstantiatedTypeOptimizationForInterfaces) {
+ return !appInfo.isInstantiatedDirectlyOrIndirectly(clazz);
+ }
+ return !clazz.isInterface() && !appInfo.isInstantiatedDirectlyOrIndirectly(clazz);
}
public boolean isSamePackage(DexType other) {
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java
index b01445d..a7e9927 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java
@@ -12,38 +12,45 @@
import com.android.tools.r8.graph.GenericSignature.ReturnType;
import com.android.tools.r8.graph.GenericSignature.TypeSignature;
import com.android.tools.r8.graph.analysis.EnqueuerAnalysis;
+import com.android.tools.r8.shaking.Enqueuer.EnqueuerDefinitionSupplier;
import com.android.tools.r8.shaking.EnqueuerWorklist;
import java.util.List;
public class GenericSignatureEnqueuerAnalysis extends EnqueuerAnalysis {
- private final GenericSignatureVisitor visitor;
+ private final EnqueuerDefinitionSupplier enqueuerDefinitionSupplier;
- public GenericSignatureEnqueuerAnalysis(DexDefinitionSupplier definitionSupplier) {
- visitor = new GenericSignatureTypeVisitor(definitionSupplier);
+ public GenericSignatureEnqueuerAnalysis(EnqueuerDefinitionSupplier enqueuerDefinitionSupplier) {
+ this.enqueuerDefinitionSupplier = enqueuerDefinitionSupplier;
}
@Override
public void processNewlyLiveClass(DexProgramClass clazz, EnqueuerWorklist worklist) {
- visitor.visitClassSignature(clazz.getClassSignature());
+ new GenericSignatureTypeVisitor(clazz, enqueuerDefinitionSupplier)
+ .visitClassSignature(clazz.getClassSignature());
}
@Override
- public void processNewlyLiveField(ProgramField field) {
- visitor.visitFieldTypeSignature(field.getDefinition().getGenericSignature());
+ public void processNewlyLiveField(ProgramField field, ProgramDefinition context) {
+ new GenericSignatureTypeVisitor(context, enqueuerDefinitionSupplier)
+ .visitFieldTypeSignature(field.getDefinition().getGenericSignature());
}
@Override
- public void processNewlyLiveMethod(ProgramMethod method) {
- visitor.visitMethodSignature(method.getDefinition().getGenericSignature());
+ public void processNewlyLiveMethod(ProgramMethod method, ProgramDefinition context) {
+ new GenericSignatureTypeVisitor(context, enqueuerDefinitionSupplier)
+ .visitMethodSignature(method.getDefinition().getGenericSignature());
}
private static class GenericSignatureTypeVisitor implements GenericSignatureVisitor {
- private final DexDefinitionSupplier definitionSupplier;
+ private final ProgramDefinition context;
+ private final EnqueuerDefinitionSupplier enqueuerDefinitionSupplier;
- private GenericSignatureTypeVisitor(DexDefinitionSupplier definitionSupplier) {
- this.definitionSupplier = definitionSupplier;
+ private GenericSignatureTypeVisitor(
+ ProgramDefinition context, EnqueuerDefinitionSupplier enqueuerDefinitionSupplier) {
+ this.context = context;
+ this.enqueuerDefinitionSupplier = enqueuerDefinitionSupplier;
}
@Override
@@ -82,7 +89,7 @@
}
private void visitClassTypeSignature(ClassTypeSignature classTypeSignature) {
- definitionSupplier.definitionFor(classTypeSignature.type);
+ enqueuerDefinitionSupplier.definitionFor(classTypeSignature.type, context);
classTypeSignature.visit(this);
}
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
index e88d379..c0cc708 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.shaking.GraphReporter;
import com.android.tools.r8.shaking.InstantiationReason;
import com.android.tools.r8.shaking.KeepReason;
+import com.android.tools.r8.shaking.MissingClasses;
import com.android.tools.r8.utils.LensUtils;
import com.android.tools.r8.utils.TraversalContinuation;
import com.android.tools.r8.utils.WorkList;
@@ -233,7 +234,7 @@
}
public boolean verifyAllocatedTypesAreLive(
- Set<DexType> liveTypes, Set<DexType> missingTypes, DexDefinitionSupplier definitions) {
+ Set<DexType> liveTypes, MissingClasses missingClasses, DexDefinitionSupplier definitions) {
for (DexProgramClass clazz : classesWithAllocationSiteTracking.keySet()) {
assert liveTypes.contains(clazz.getType());
}
@@ -244,7 +245,7 @@
assert liveTypes.contains(iface.getType());
}
for (DexType iface : instantiatedLambdas.keySet()) {
- assert missingTypes.contains(iface)
+ assert missingClasses.contains(iface)
|| definitions.definitionFor(iface).isNotProgramClass()
|| liveTypes.contains(iface);
}
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java b/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java
index 6c5ff48..c55ad6f 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java
@@ -36,6 +36,14 @@
return null;
}
+ default boolean isProgramMember() {
+ return false;
+ }
+
+ default ProgramMember<?, ?> asProgramMember() {
+ return null;
+ }
+
default boolean isProgramMethod() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramField.java b/src/main/java/com/android/tools/r8/graph/ProgramField.java
index 7721c67..866d230 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramField.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramField.java
@@ -37,6 +37,16 @@
}
@Override
+ public boolean isProgramMember() {
+ return true;
+ }
+
+ @Override
+ public ProgramField asProgramMember() {
+ return this;
+ }
+
+ @Override
public DexProgramClass getHolder() {
DexClass holder = super.getHolder();
assert holder.isProgramClass();
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
index 0765824..33063ba 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
@@ -67,6 +67,16 @@
}
@Override
+ public boolean isProgramMember() {
+ return true;
+ }
+
+ @Override
+ public ProgramMethod asProgramMember() {
+ return this;
+ }
+
+ @Override
public boolean isProgramMethod() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
index eb82383..37e08d2 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.google.common.collect.ImmutableList;
@@ -37,7 +38,7 @@
}
@Override
- public void processNewlyLiveMethod(ProgramMethod method) {
+ public void processNewlyLiveMethod(ProgramMethod method, ProgramDefinition context) {
DexEncodedMethod definition = method.getDefinition();
if (definition.isClassInitializer()) {
Code code = definition.getCode();
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java
index 1fa68dd..4291896 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.DexClasspathClass;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter;
import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter.Mode;
@@ -29,7 +30,7 @@
}
@Override
- public void processNewlyLiveMethod(ProgramMethod method) {
+ public void processNewlyLiveMethod(ProgramMethod method, ProgramDefinition context) {
converter.registerCallbackIfRequired(method);
}
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
index 1a00007..9e09724 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.graph.analysis;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.shaking.Enqueuer;
@@ -20,10 +21,10 @@
public void processNewlyLiveClass(DexProgramClass clazz, EnqueuerWorklist worklist) {}
/** Called when a field is found to be live. */
- public void processNewlyLiveField(ProgramField field) {}
+ public void processNewlyLiveField(ProgramField field, ProgramDefinition context) {}
/** Called when a method is found to be live. */
- public void processNewlyLiveMethod(ProgramMethod method) {}
+ public void processNewlyLiveMethod(ProgramMethod method, ProgramDefinition context) {}
/**
* Called when the Enqueuer reaches a fixpoint. This may happen multiple times, since each
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
index 96282ee..23cb57c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
@@ -4,16 +4,17 @@
package com.android.tools.r8.horizontalclassmerging;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMergerGraphLens.Builder;
+import com.android.tools.r8.horizontalclassmerging.policies.SameInstanceFields.InstanceFieldInfo;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.IterableUtils;
-import com.android.tools.r8.utils.ListUtils;
import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
@@ -21,12 +22,19 @@
public class ClassInstanceFieldsMerger {
- // Map from target class field to all fields which should be merged into that field.
- private final Map<DexEncodedField, List<DexEncodedField>> fieldMappings = new LinkedHashMap<>();
+ private final AppView<AppInfoWithLiveness> appView;
private final Builder lensBuilder;
+ private DexEncodedField classIdField;
+
+ // Map from target class field to all fields which should be merged into that field.
+ private final Map<DexEncodedField, List<DexEncodedField>> fieldMappings = new LinkedHashMap<>();
+
public ClassInstanceFieldsMerger(
- HorizontalClassMergerGraphLens.Builder lensBuilder, MergeGroup group) {
+ AppView<AppInfoWithLiveness> appView,
+ HorizontalClassMergerGraphLens.Builder lensBuilder,
+ MergeGroup group) {
+ this.appView = appView;
this.lensBuilder = lensBuilder;
group
.getTarget()
@@ -34,39 +42,122 @@
.forEach(field -> fieldMappings.computeIfAbsent(field, ignore -> new ArrayList<>()));
}
+ /**
+ * Adds all fields from {@param clazz} to the class merger. For each field, we must choose which
+ * field on the target class to merge into.
+ *
+ * <p>A field that stores a reference type can be merged into a field that stores a different
+ * reference type. To avoid that we change fields that store a reference type to have type
+ * java.lang.Object when it is not needed (e.g., class Foo has fields 'A a' and 'B b' and class
+ * Bar has fields 'A b' and 'B a'), we make a prepass that matches fields with the same reference
+ * type.
+ */
public void addFields(DexProgramClass clazz) {
- Map<DexType, List<DexEncodedField>> availableFields = new IdentityHashMap<>();
- for (DexEncodedField field : fieldMappings.keySet()) {
- availableFields.computeIfAbsent(field.type(), ignore -> new LinkedList<>()).add(field);
+ Map<InstanceFieldInfo, LinkedList<DexEncodedField>> availableFieldsByExactInfo =
+ getAvailableFieldsByExactInfo();
+ List<DexEncodedField> needsMerge = new ArrayList<>();
+
+ // Pass 1: Match fields that have the exact same type.
+ for (DexEncodedField oldField : clazz.instanceFields()) {
+ InstanceFieldInfo info = InstanceFieldInfo.createExact(oldField);
+ LinkedList<DexEncodedField> availableFieldsWithExactSameInfo =
+ availableFieldsByExactInfo.get(info);
+ if (availableFieldsWithExactSameInfo == null || availableFieldsWithExactSameInfo.isEmpty()) {
+ needsMerge.add(oldField);
+ } else {
+ DexEncodedField newField = availableFieldsWithExactSameInfo.removeFirst();
+ fieldMappings.get(newField).add(oldField);
+ if (availableFieldsWithExactSameInfo.isEmpty()) {
+ availableFieldsByExactInfo.remove(info);
+ }
+ }
}
- for (DexEncodedField oldField : clazz.instanceFields()) {
+ // Pass 2: Match fields that do not have the same reference type.
+ Map<InstanceFieldInfo, LinkedList<DexEncodedField>> availableFieldsByRelaxedInfo =
+ getAvailableFieldsByRelaxedInfo(availableFieldsByExactInfo);
+ for (DexEncodedField oldField : needsMerge) {
+ assert oldField.getType().isReferenceType();
DexEncodedField newField =
- ListUtils.removeFirstMatch(
- availableFields.get(oldField.type()),
- field -> field.getAccessFlags().isSameVisibility(oldField.getAccessFlags()))
- .get();
+ availableFieldsByRelaxedInfo
+ .get(InstanceFieldInfo.createRelaxed(oldField, appView.dexItemFactory()))
+ .removeFirst();
assert newField != null;
+ assert newField.getType().isReferenceType();
fieldMappings.get(newField).add(oldField);
}
}
+ private Map<InstanceFieldInfo, LinkedList<DexEncodedField>> getAvailableFieldsByExactInfo() {
+ Map<InstanceFieldInfo, LinkedList<DexEncodedField>> availableFieldsByInfo =
+ new LinkedHashMap<>();
+ for (DexEncodedField field : fieldMappings.keySet()) {
+ availableFieldsByInfo
+ .computeIfAbsent(InstanceFieldInfo.createExact(field), ignore -> new LinkedList<>())
+ .add(field);
+ }
+ return availableFieldsByInfo;
+ }
+
+ private Map<InstanceFieldInfo, LinkedList<DexEncodedField>> getAvailableFieldsByRelaxedInfo(
+ Map<InstanceFieldInfo, LinkedList<DexEncodedField>> availableFieldsByExactInfo) {
+ Map<InstanceFieldInfo, LinkedList<DexEncodedField>> availableFieldsByRelaxedInfo =
+ new LinkedHashMap<>();
+ availableFieldsByExactInfo.forEach(
+ (info, fields) ->
+ availableFieldsByRelaxedInfo
+ .computeIfAbsent(
+ info.toInfoWithRelaxedType(appView.dexItemFactory()),
+ ignore -> new LinkedList<>())
+ .addAll(fields));
+ return availableFieldsByRelaxedInfo;
+ }
+
private void fixAccessFlags(DexEncodedField newField, Collection<DexEncodedField> oldFields) {
+ if (newField.isSynthetic() && Iterables.any(oldFields, oldField -> !oldField.isSynthetic())) {
+ newField.getAccessFlags().demoteFromSynthetic();
+ }
if (newField.isFinal() && Iterables.any(oldFields, oldField -> !oldField.isFinal())) {
newField.getAccessFlags().demoteFromFinal();
}
}
- private void mergeFields(DexEncodedField newField, List<DexEncodedField> oldFields) {
- fixAccessFlags(newField, oldFields);
- lensBuilder.recordNewFieldSignature(
- Iterables.transform(
- IterableUtils.append(oldFields, newField), DexEncodedField::getReference),
- newField.getReference(),
- newField.getReference());
+ public void setClassIdField(DexEncodedField classIdField) {
+ this.classIdField = classIdField;
}
- public void merge() {
- fieldMappings.forEach(this::mergeFields);
+ public DexEncodedField[] merge() {
+ List<DexEncodedField> newFields = new ArrayList<>();
+ assert classIdField != null;
+ newFields.add(classIdField);
+ fieldMappings.forEach(
+ (targetField, oldFields) ->
+ newFields.add(mergeSourceFieldsToTargetField(targetField, oldFields)));
+ return newFields.toArray(DexEncodedField.EMPTY_ARRAY);
+ }
+
+ private DexEncodedField mergeSourceFieldsToTargetField(
+ DexEncodedField targetField, List<DexEncodedField> oldFields) {
+ fixAccessFlags(targetField, oldFields);
+
+ DexEncodedField newField;
+ DexType targetFieldType = targetField.type();
+ if (!Iterables.all(oldFields, oldField -> oldField.getType() == targetFieldType)) {
+ newField =
+ targetField.toTypeSubstitutedField(
+ targetField
+ .getReference()
+ .withType(appView.dexItemFactory().objectType, appView.dexItemFactory()));
+ } else {
+ newField = targetField;
+ }
+
+ lensBuilder.recordNewFieldSignature(
+ Iterables.transform(
+ IterableUtils.append(oldFields, targetField), DexEncodedField::getReference),
+ newField.getReference(),
+ targetField.getReference());
+
+ return newField;
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index 950cf9a..0a6a9b9 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
@@ -26,7 +27,7 @@
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
+import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.Iterables;
@@ -57,7 +58,6 @@
private final ClassInitializerSynthesizedCode classInitializerSynthesizedCode;
private final HorizontalClassMergerGraphLens.Builder lensBuilder;
private final HorizontallyMergedClasses.Builder mergedClassesBuilder;
- private final FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder;
private final ClassMethodsBuilder classMethodsBuilder = new ClassMethodsBuilder();
private final Reference2IntMap<DexType> classIdentifiers = new Reference2IntOpenHashMap<>();
@@ -70,7 +70,6 @@
AppView<AppInfoWithLiveness> appView,
HorizontalClassMergerGraphLens.Builder lensBuilder,
HorizontallyMergedClasses.Builder mergedClassesBuilder,
- FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder,
MergeGroup group,
Collection<VirtualMethodMerger> virtualMethodMergers,
Collection<ConstructorMerger> constructorMergers,
@@ -78,7 +77,6 @@
this.appView = appView;
this.lensBuilder = lensBuilder;
this.mergedClassesBuilder = mergedClassesBuilder;
- this.fieldAccessChangesBuilder = fieldAccessChangesBuilder;
this.group = group;
this.virtualMethodMergers = virtualMethodMergers;
this.constructorMergers = constructorMergers;
@@ -86,7 +84,7 @@
this.dexItemFactory = appView.dexItemFactory();
this.classInitializerSynthesizedCode = classInitializerSynthesizedCode;
this.classStaticFieldsMerger = new ClassStaticFieldsMerger(appView, lensBuilder, group);
- this.classInstanceFieldsMerger = new ClassInstanceFieldsMerger(lensBuilder, group);
+ this.classInstanceFieldsMerger = new ClassInstanceFieldsMerger(appView, lensBuilder, group);
buildClassIdentifierMap();
}
@@ -185,29 +183,25 @@
merger.merge(
classMethodsBuilder,
lensBuilder,
- fieldAccessChangesBuilder,
classIdentifiers,
syntheticArgumentClass));
}
void mergeVirtualMethods() {
virtualMethodMergers.forEach(
- merger ->
- merger.merge(
- classMethodsBuilder, lensBuilder, fieldAccessChangesBuilder, classIdentifiers));
+ merger -> merger.merge(classMethodsBuilder, lensBuilder, classIdentifiers));
group.forEachSource(clazz -> clazz.getMethodCollection().clearVirtualMethods());
}
void appendClassIdField() {
- DexEncodedField encodedField =
+ classInstanceFieldsMerger.setClassIdField(
new DexEncodedField(
group.getClassIdField(),
FieldAccessFlags.fromSharedAccessFlags(
Constants.ACC_PUBLIC + Constants.ACC_FINAL + Constants.ACC_SYNTHETIC),
FieldTypeSignature.noSignature(),
DexAnnotationSet.empty(),
- null);
- group.getTarget().appendInstanceField(encodedField);
+ null));
}
void mergeStaticFields() {
@@ -238,9 +232,9 @@
group.forEachSource(
clazz -> {
classInstanceFieldsMerger.addFields(clazz);
- clazz.setInstanceFields(null);
+ clazz.clearInstanceFields();
});
- classInstanceFieldsMerger.merge();
+ group.getTarget().setInstanceFields(classInstanceFieldsMerger.merge());
}
public void mergeGroup(SyntheticArgumentClass syntheticArgumentClass) {
@@ -277,7 +271,8 @@
private Builder setup() {
DexItemFactory dexItemFactory = appView.dexItemFactory();
- DexProgramClass target = group.iterator().next();
+ DexProgramClass target =
+ IterableUtils.findOrDefault(group, DexClass::isPublic, group.iterator().next());
// TODO(b/165498187): ensure the name for the field is fresh
group.setClassIdField(
dexItemFactory.createField(
@@ -327,8 +322,7 @@
public ClassMerger build(
HorizontallyMergedClasses.Builder mergedClassesBuilder,
- HorizontalClassMergerGraphLens.Builder lensBuilder,
- FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder) {
+ HorizontalClassMergerGraphLens.Builder lensBuilder) {
setup();
List<VirtualMethodMerger> virtualMethodMergers =
new ArrayList<>(virtualMethodMergerBuilders.size());
@@ -354,7 +348,6 @@
appView,
lensBuilder,
mergedClassesBuilder,
- fieldAccessChangesBuilder,
group,
virtualMethodMergers,
constructorMergers,
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPointSynthesizedCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPointSynthesizedCode.java
index 7d250bb..bbce62b 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPointSynthesizedCode.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPointSynthesizedCode.java
@@ -45,4 +45,9 @@
registry.registerInvokeDirect(typeConstructor);
}
}
+
+ @Override
+ public boolean isHorizontalClassMergingCode() {
+ return true;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
index 01d5771..b4e9887 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
@@ -21,7 +21,6 @@
import com.android.tools.r8.ir.conversion.ExtraParameter;
import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.structural.Ordered;
import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
@@ -139,7 +138,6 @@
void merge(
ClassMethodsBuilder classMethodsBuilder,
HorizontalClassMergerGraphLens.Builder lensBuilder,
- FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder,
Reference2IntMap<DexType> classIdentifiers,
SyntheticArgumentClass syntheticArgumentClass) {
// Tree map as must be sorted.
@@ -216,8 +214,5 @@
lensBuilder.recordNewMethodSignature(bridgeConstructorReference, newConstructorReference);
classMethodsBuilder.addDirectMethod(newConstructor);
-
- fieldAccessChangesBuilder.fieldWrittenByMethod(
- group.getClassIdField(), newConstructorReference);
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/FieldMultiset.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/FieldMultiset.java
deleted file mode 100644
index 6cbffcc..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/FieldMultiset.java
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.horizontalclassmerging;
-
-import com.android.tools.r8.graph.AccessFlags;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexType;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-
-public class FieldMultiset {
-
- private static class VisibilitySignature {
- private int publicVisible = 0;
- private int protectedVisible = 0;
- private int privateVisible = 0;
- private int packagePrivateVisible = 0;
-
- public void addAccessModifier(AccessFlags accessFlags) {
- if (accessFlags.isPublic()) {
- publicVisible++;
- } else if (accessFlags.isPrivate()) {
- privateVisible++;
- } else if (accessFlags.isPackagePrivate()) {
- packagePrivateVisible++;
- } else if (accessFlags.isProtected()) {
- protectedVisible++;
- }
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- VisibilitySignature that = (VisibilitySignature) o;
- return publicVisible == that.publicVisible
- && protectedVisible == that.protectedVisible
- && privateVisible == that.privateVisible
- && packagePrivateVisible == that.packagePrivateVisible;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(publicVisible, protectedVisible, privateVisible, packagePrivateVisible);
- }
- }
-
- // This *must* not be an IdentityHashMap, because hash equality does not work for the values.
- private final Map<DexType, VisibilitySignature> fields = new HashMap<>();
-
- public FieldMultiset(DexProgramClass clazz) {
- for (DexEncodedField field : clazz.instanceFields()) {
- fields
- .computeIfAbsent(field.type(), ignore -> new VisibilitySignature())
- .addAccessModifier(field.getAccessFlags());
- }
- }
-
- @Override
- public int hashCode() {
- return fields.hashCode();
- }
-
- @Override
- public boolean equals(Object object) {
- if (object instanceof FieldMultiset) {
- FieldMultiset other = (FieldMultiset) object;
- return fields.equals(other.fields);
- } else {
- return false;
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index 5983618..495be54 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -10,10 +10,10 @@
import com.android.tools.r8.horizontalclassmerging.policies.AllInstantiatedOrUninstantiated;
import com.android.tools.r8.horizontalclassmerging.policies.CheckAbstractClasses;
import com.android.tools.r8.horizontalclassmerging.policies.DontInlinePolicy;
-import com.android.tools.r8.horizontalclassmerging.policies.DontMergeIntoLessVisible;
import com.android.tools.r8.horizontalclassmerging.policies.DontMergeSynchronizedClasses;
import com.android.tools.r8.horizontalclassmerging.policies.IgnoreSynthetics;
import com.android.tools.r8.horizontalclassmerging.policies.LimitGroups;
+import com.android.tools.r8.horizontalclassmerging.policies.MinimizeFieldCasts;
import com.android.tools.r8.horizontalclassmerging.policies.NoAnnotations;
import com.android.tools.r8.horizontalclassmerging.policies.NoClassInitializerWithObservableSideEffects;
import com.android.tools.r8.horizontalclassmerging.policies.NoClassesOrMembersWithAnnotations;
@@ -34,11 +34,12 @@
import com.android.tools.r8.horizontalclassmerging.policies.PreventMethodImplementation;
import com.android.tools.r8.horizontalclassmerging.policies.RespectPackageBoundaries;
import com.android.tools.r8.horizontalclassmerging.policies.SameFeatureSplit;
-import com.android.tools.r8.horizontalclassmerging.policies.SameFields;
+import com.android.tools.r8.horizontalclassmerging.policies.SameInstanceFields;
import com.android.tools.r8.horizontalclassmerging.policies.SameNestHost;
import com.android.tools.r8.horizontalclassmerging.policies.SameParentClass;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
+import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.shaking.MainDexTracingResult;
import com.android.tools.r8.shaking.RuntimeTypeCheckInfo;
import com.google.common.collect.ImmutableList;
@@ -49,6 +50,7 @@
import java.util.List;
public class HorizontalClassMerger {
+
private final AppView<AppInfoWithLiveness> appView;
public HorizontalClassMerger(AppView<AppInfoWithLiveness> appView) {
@@ -57,7 +59,7 @@
}
// TODO(b/165577835): replace Collection<DexProgramClass> with MergeGroup
- public HorizontalClassMergerGraphLens run(
+ public HorizontalClassMergerResult run(
DirectMappedDexApplication.Builder appBuilder,
MainDexTracingResult mainDexTracingResult,
RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
@@ -78,27 +80,51 @@
new HorizontallyMergedClasses.Builder();
HorizontalClassMergerGraphLens.Builder lensBuilder =
new HorizontalClassMergerGraphLens.Builder();
- FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder =
- new FieldAccessInfoCollectionModifier.Builder();
+ MainDexTracingResult.Builder mainDexTracingResultBuilder =
+ mainDexTracingResult.extensionBuilder(appView.appInfo());
// Set up a class merger for each group.
List<ClassMerger> classMergers =
- initializeClassMergers(
- mergedClassesBuilder, lensBuilder, fieldAccessChangesBuilder, groups);
+ initializeClassMergers(mergedClassesBuilder, lensBuilder, groups);
Iterable<DexProgramClass> allMergeClasses =
Iterables.concat(
Iterables.transform(classMergers, classMerger -> classMerger.getGroup().getClasses()));
// Merge the classes.
SyntheticArgumentClass syntheticArgumentClass =
- new SyntheticArgumentClass.Builder().build(appView, appBuilder, allMergeClasses);
+ new SyntheticArgumentClass.Builder(
+ appBuilder, appView, mainDexTracingResult, mainDexTracingResultBuilder)
+ .build(allMergeClasses);
applyClassMergers(classMergers, syntheticArgumentClass);
- // Generate the class lens.
+ // Generate the graph lens.
HorizontallyMergedClasses mergedClasses = mergedClassesBuilder.build();
appView.setHorizontallyMergedClasses(mergedClasses);
- return createLens(
- mergedClasses, lensBuilder, fieldAccessChangesBuilder, syntheticArgumentClass);
+ HorizontalClassMergerGraphLens lens =
+ createLens(mergedClasses, lensBuilder, syntheticArgumentClass);
+
+ // Prune keep info.
+ KeepInfoCollection keepInfo = appView.appInfo().getKeepInfo();
+ keepInfo.mutate(mutator -> mutator.removeKeepInfoForPrunedItems(mergedClasses.getSources()));
+
+ return new HorizontalClassMergerResult(
+ createFieldAccessInfoCollectionModifier(groups), lens, mainDexTracingResultBuilder.build());
+ }
+
+ private FieldAccessInfoCollectionModifier createFieldAccessInfoCollectionModifier(
+ Collection<MergeGroup> groups) {
+ FieldAccessInfoCollectionModifier.Builder builder =
+ new FieldAccessInfoCollectionModifier.Builder();
+ for (MergeGroup group : groups) {
+ DexProgramClass target = group.getTarget();
+ target.forEachProgramInstanceInitializerMatching(
+ definition -> definition.getCode().isHorizontalClassMergingCode(),
+ method -> builder.recordFieldWrittenInContext(group.getClassIdField(), method));
+ target.forEachProgramVirtualMethodMatching(
+ definition -> definition.hasCode() && definition.getCode().isHorizontalClassMergingCode(),
+ method -> builder.recordFieldReadInContext(group.getClassIdField(), method));
+ }
+ return builder.build();
}
private List<Policy> getPolicies(
@@ -106,7 +132,7 @@
RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
return ImmutableList.of(
new NotMatchedByNoHorizontalClassMerging(appView),
- new SameFields(),
+ new SameInstanceFields(appView),
new NoInterfaces(),
new NoAnnotations(),
new NoEnums(appView),
@@ -133,12 +159,8 @@
new SameFeatureSplit(appView),
new RespectPackageBoundaries(appView),
new DontMergeSynchronizedClasses(appView),
- new LimitGroups(appView),
- // TODO(b/166577694): no policies should be run after this policy, as it would
- // potentially break tests
- new DontMergeIntoLessVisible()
- // TODO: add policies
- );
+ new MinimizeFieldCasts(),
+ new LimitGroups(appView));
}
/**
@@ -148,7 +170,6 @@
private List<ClassMerger> initializeClassMergers(
HorizontallyMergedClasses.Builder mergedClassesBuilder,
HorizontalClassMergerGraphLens.Builder lensBuilder,
- FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder,
Collection<MergeGroup> groups) {
List<ClassMerger> classMergers = new ArrayList<>();
@@ -156,8 +177,7 @@
for (MergeGroup group : groups) {
assert !group.isEmpty();
ClassMerger merger =
- new ClassMerger.Builder(appView, group)
- .build(mergedClassesBuilder, lensBuilder, fieldAccessChangesBuilder);
+ new ClassMerger.Builder(appView, group).build(mergedClassesBuilder, lensBuilder);
classMergers.add(merger);
}
@@ -179,17 +199,8 @@
private HorizontalClassMergerGraphLens createLens(
HorizontallyMergedClasses mergedClasses,
HorizontalClassMergerGraphLens.Builder lensBuilder,
- FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder,
SyntheticArgumentClass syntheticArgumentClass) {
-
- HorizontalClassMergerGraphLens lens =
- new TreeFixer(
- appView,
- mergedClasses,
- lensBuilder,
- fieldAccessChangesBuilder,
- syntheticArgumentClass)
- .fixupTypeReferences();
- return lens;
+ return new TreeFixer(appView, mergedClasses, lensBuilder, syntheticArgumentClass)
+ .fixupTypeReferences();
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
index b42cb24..f5fdd6f 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
@@ -72,6 +72,22 @@
.build();
}
+ @Override
+ protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
+ FieldLookupResult lookup = super.internalDescribeLookupField(previous);
+ if (lookup.getReference() == previous.getReference()) {
+ return lookup;
+ }
+ return FieldLookupResult.builder(this)
+ .setReference(lookup.getReference())
+ .setReboundReference(lookup.getReboundReference())
+ .setCastType(
+ lookup.getReference().getType() != previous.getReference().getType()
+ ? lookupType(previous.getReference().getType())
+ : null)
+ .build();
+ }
+
public static class Builder {
private final MutableBidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap =
@@ -127,12 +143,10 @@
if (originalFieldSignatures.isEmpty()) {
fieldMap.put(oldFieldSignature, newFieldSignature);
} else if (originalFieldSignatures.size() == 1) {
- fieldMap.put(originalFieldSignatures.iterator().next(), newFieldSignature);
+ fieldMap.put(originalFieldSignatures, newFieldSignature);
} else {
assert representative != null;
- for (DexField originalFieldSignature : originalFieldSignatures) {
- fieldMap.put(originalFieldSignature, newFieldSignature);
- }
+ fieldMap.put(originalFieldSignatures, newFieldSignature);
fieldMap.setRepresentative(newFieldSignature, representative);
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerResult.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerResult.java
new file mode 100644
index 0000000..75aafb4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerResult.java
@@ -0,0 +1,36 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.horizontalclassmerging;
+
+import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
+import com.android.tools.r8.shaking.MainDexTracingResult;
+
+public class HorizontalClassMergerResult {
+
+ private final FieldAccessInfoCollectionModifier fieldAccessInfoCollectionModifier;
+ private final HorizontalClassMergerGraphLens graphLens;
+ private final MainDexTracingResult mainDexTracingResult;
+
+ HorizontalClassMergerResult(
+ FieldAccessInfoCollectionModifier fieldAccessInfoCollectionModifier,
+ HorizontalClassMergerGraphLens graphLens,
+ MainDexTracingResult mainDexTracingResult) {
+ this.fieldAccessInfoCollectionModifier = fieldAccessInfoCollectionModifier;
+ this.graphLens = graphLens;
+ this.mainDexTracingResult = mainDexTracingResult;
+ }
+
+ public FieldAccessInfoCollectionModifier getFieldAccessInfoCollectionModifier() {
+ return fieldAccessInfoCollectionModifier;
+ }
+
+ public HorizontalClassMergerGraphLens getGraphLens() {
+ return graphLens;
+ }
+
+ public MainDexTracingResult getMainDexTracingResult() {
+ return mainDexTracingResult;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/MergeGroup.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/MergeGroup.java
index 02e4684..9e4ebf5 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/MergeGroup.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/MergeGroup.java
@@ -45,6 +45,10 @@
classes.add(clazz);
}
+ public void add(MergeGroup group) {
+ classes.addAll(group.getClasses());
+ }
+
public void addAll(Collection<DexProgramClass> classes) {
this.classes.addAll(classes);
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java
index 14b249a..3b96a76 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java
@@ -72,6 +72,10 @@
policy.clear();
+ if (linkedGroups.isEmpty()) {
+ break;
+ }
+
// Any policy should not return any trivial groups.
assert linkedGroups.stream().allMatch(group -> group.size() >= 2);
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
index c3aa3c0..ec64e8a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
@@ -17,6 +17,8 @@
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.MainDexTracingResult;
+import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -53,13 +55,27 @@
public static class Builder {
- private DexType synthesizeClass(
- AppView<AppInfoWithLiveness> appView,
+ private final DirectMappedDexApplication.Builder appBuilder;
+ private final AppView<AppInfoWithLiveness> appView;
+ private final MainDexTracingResult mainDexTracingResult;
+ private final MainDexTracingResult.Builder mainDexTracingResultBuilder;
+
+ Builder(
DirectMappedDexApplication.Builder appBuilder,
+ AppView<AppInfoWithLiveness> appView,
+ MainDexTracingResult mainDexTracingResult,
+ MainDexTracingResult.Builder mainDexTracingResultBuilder) {
+ this.appBuilder = appBuilder;
+ this.appView = appView;
+ this.mainDexTracingResult = mainDexTracingResult;
+ this.mainDexTracingResultBuilder = mainDexTracingResultBuilder;
+ }
+
+ private DexType synthesizeClass(
DexProgramClass context,
boolean requiresMainDex,
+ boolean addToMainDexTracingResult,
int index) {
-
DexType syntheticClassType =
appView
.dexItemFactory()
@@ -92,26 +108,31 @@
appBuilder.addSynthesizedClass(clazz);
appView.appInfo().addSynthesizedClass(clazz, requiresMainDex);
+ if (addToMainDexTracingResult) {
+ mainDexTracingResultBuilder.addRoot(clazz);
+ }
return clazz.type;
}
- public SyntheticArgumentClass build(
- AppView<AppInfoWithLiveness> appView,
- DirectMappedDexApplication.Builder appBuilder,
- Iterable<DexProgramClass> mergeClasses) {
-
+ public SyntheticArgumentClass build(Iterable<DexProgramClass> mergeClasses) {
// Find a fresh name in an existing package.
DexProgramClass context = mergeClasses.iterator().next();
+ // Add to the main dex list if one of the merged classes is in the main dex.
boolean requiresMainDex = appView.appInfo().getMainDexClasses().containsAnyOf(mergeClasses);
+ // Also add as a root to the main dex tracing result if any of the merged classes is a root.
+ // This is needed to satisfy an assertion in the inliner that verifies that we do not inline
+ // methods with references to non-roots into classes that are roots.
+ boolean addToMainDexTracingResult = Iterables.any(mergeClasses, mainDexTracingResult::isRoot);
+
List<DexType> syntheticArgumentTypes = new ArrayList<>();
for (int i = 0;
i < appView.options().horizontalClassMergerOptions().getSyntheticArgumentCount();
i++) {
syntheticArgumentTypes.add(
- synthesizeClass(appView, appBuilder, context, requiresMainDex, i));
+ synthesizeClass(context, requiresMainDex, addToMainDexTracingResult, i));
}
return new SyntheticArgumentClass(syntheticArgumentTypes);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
index 87a1e31..c66d5de 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -14,24 +14,20 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexMethodSignature;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.TreeFixerBase;
import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
import com.android.tools.r8.shaking.AnnotationFixer;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
import com.android.tools.r8.utils.OptionalBool;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import java.util.Collections;
-import java.util.IdentityHashMap;
import java.util.LinkedHashSet;
import java.util.List;
-import java.util.Map;
import java.util.Optional;
import java.util.Set;
@@ -41,10 +37,8 @@
* tracked in {@link TreeFixer#lensBuilder}.
*/
class TreeFixer extends TreeFixerBase {
- private final Map<DexProto, DexProto> protoFixupCache = new IdentityHashMap<>();
private final HorizontallyMergedClasses mergedClasses;
private final HorizontalClassMergerGraphLens.Builder lensBuilder;
- private final FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder;
private final AppView<AppInfoWithLiveness> appView;
private final DexItemFactory dexItemFactory;
private final SyntheticArgumentClass syntheticArgumentClass;
@@ -55,13 +49,11 @@
AppView<AppInfoWithLiveness> appView,
HorizontallyMergedClasses mergedClasses,
HorizontalClassMergerGraphLens.Builder lensBuilder,
- FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder,
SyntheticArgumentClass syntheticArgumentClass) {
super(appView);
this.appView = appView;
this.mergedClasses = mergedClasses;
this.lensBuilder = lensBuilder;
- this.fieldAccessChangesBuilder = fieldAccessChangesBuilder;
this.syntheticArgumentClass = syntheticArgumentClass;
this.dexItemFactory = appView.dexItemFactory();
}
@@ -130,7 +122,6 @@
subtypingForrest.traverseNodeDepthFirst(root, HashBiMap.create(), this::fixupProgramClass);
}
HorizontalClassMergerGraphLens lens = lensBuilder.build(appView, mergedClasses);
- fieldAccessChangesBuilder.build(this::fixupMethodReference).modify(appView);
new AnnotationFixer(lens).run(appView.appInfo().classes());
return lens;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodEntryPointSynthesizedCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodEntryPointSynthesizedCode.java
index 7428b92..a1aaa6b 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodEntryPointSynthesizedCode.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodEntryPointSynthesizedCode.java
@@ -38,4 +38,9 @@
registry.registerInvokeDirect(mappedMethod);
}
}
+
+ @Override
+ public boolean isHorizontalClassMergingCode() {
+ return true;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
index 9a44de7..24a1b84 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -19,7 +19,6 @@
import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.synthetic.AbstractSynthesizedCode;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.structural.Ordered;
@@ -200,17 +199,11 @@
public void merge(
ClassMethodsBuilder classMethodsBuilder,
HorizontalClassMergerGraphLens.Builder lensBuilder,
- FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder,
Reference2IntMap<DexType> classIdentifiers) {
assert !methods.isEmpty();
- // Handle nop merges.
- if (isNop()) {
- return;
- }
-
// Handle trivial merges.
- if (isTrivial()) {
+ if (isNop() || isTrivial()) {
mergeTrivial(classMethodsBuilder, lensBuilder);
return;
}
@@ -280,7 +273,5 @@
lensBuilder.recordNewMethodSignature(bridgeMethodReference, newMethodReference);
classMethodsBuilder.addVirtualMethod(newMethod);
-
- fieldAccessChangesBuilder.fieldReadByMethod(group.getClassIdField(), newMethodReference);
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontMergeIntoLessVisible.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontMergeIntoLessVisible.java
deleted file mode 100644
index 954a238..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontMergeIntoLessVisible.java
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.horizontalclassmerging.policies;
-
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.horizontalclassmerging.MergeGroup;
-import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
-import java.util.Collection;
-import java.util.Collections;
-
-public class DontMergeIntoLessVisible extends MultiClassPolicy {
-
- @Override
- public Collection<MergeGroup> apply(MergeGroup group) {
- DexProgramClass clazz = group.removeFirst(DexClass::isPublic);
- if (clazz != null) {
- group.addFirst(clazz);
- }
- return Collections.singletonList(group);
- }
-}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/MinimizeFieldCasts.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/MinimizeFieldCasts.java
new file mode 100644
index 0000000..73e0786
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/MinimizeFieldCasts.java
@@ -0,0 +1,73 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.horizontalclassmerging.policies;
+
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.horizontalclassmerging.MergeGroup;
+import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
+import com.google.common.collect.HashMultiset;
+import com.google.common.collect.Multiset;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class MinimizeFieldCasts extends MultiClassPolicy {
+
+ @Override
+ public final Collection<MergeGroup> apply(MergeGroup group) {
+ // First find all classes that can be merged without changing field types.
+ Map<Multiset<DexType>, MergeGroup> newGroups = new LinkedHashMap<>();
+ group.forEach(clazz -> addExact(clazz, newGroups));
+
+ // Create a single group from all trivial groups.
+ MergeGroup pendingGroup = new MergeGroup();
+ newGroups
+ .values()
+ .removeIf(
+ newGroup -> {
+ if (newGroup.isTrivial()) {
+ pendingGroup.add(newGroup);
+ return true;
+ }
+ return false;
+ });
+
+ if (pendingGroup.isEmpty()) {
+ return newGroups.values();
+ }
+
+ if (!pendingGroup.isTrivial()) {
+ List<MergeGroup> newGroupsIncludingRelaxedGroup = new ArrayList<>(newGroups.values());
+ newGroupsIncludingRelaxedGroup.add(pendingGroup);
+ return newGroupsIncludingRelaxedGroup;
+ }
+
+ MergeGroup smallestNewGroup = null;
+ for (MergeGroup newGroup : newGroups.values()) {
+ if (smallestNewGroup == null || newGroup.size() < smallestNewGroup.size()) {
+ smallestNewGroup = newGroup;
+ }
+ }
+ assert smallestNewGroup != null;
+ smallestNewGroup.add(pendingGroup);
+ return newGroups.values();
+ }
+
+ private void addExact(DexProgramClass clazz, Map<Multiset<DexType>, MergeGroup> groups) {
+ groups.computeIfAbsent(getExactMergeKey(clazz), ignore -> new MergeGroup()).add(clazz);
+ }
+
+ private Multiset<DexType> getExactMergeKey(DexProgramClass clazz) {
+ Multiset<DexType> fieldTypes = HashMultiset.create();
+ for (DexEncodedField field : clazz.instanceFields()) {
+ fieldTypes.add(field.getType());
+ }
+ return fieldTypes;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
index adcde5c..5165bd9 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.horizontalclassmerging.MergeGroup;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
+import com.android.tools.r8.utils.OptionalBool;
import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.Collection;
@@ -25,15 +26,17 @@
static class MethodCharacteristics {
+ private final OptionalBool isLibraryMethodOverride;
private final int visibilityOrdinal;
private MethodCharacteristics(DexEncodedMethod method) {
+ this.isLibraryMethodOverride = method.isLibraryMethodOverride();
this.visibilityOrdinal = method.getAccessFlags().getVisibilityOrdinal();
}
@Override
public int hashCode() {
- return visibilityOrdinal;
+ return (visibilityOrdinal << 2) | isLibraryMethodOverride.ordinal();
}
@Override
@@ -45,7 +48,8 @@
return false;
}
MethodCharacteristics characteristics = (MethodCharacteristics) obj;
- return visibilityOrdinal == characteristics.visibilityOrdinal;
+ return isLibraryMethodOverride == characteristics.isLibraryMethodOverride
+ && visibilityOrdinal == characteristics.visibilityOrdinal;
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventChangingVisibility.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventChangingVisibility.java
deleted file mode 100644
index cd58e10..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventChangingVisibility.java
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.horizontalclassmerging.policies;
-
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.horizontalclassmerging.MergeGroup;
-import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
-import com.android.tools.r8.utils.MethodSignatureEquivalence;
-import com.google.common.base.Equivalence.Wrapper;
-import com.google.common.collect.Iterables;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class PreventChangingVisibility extends MultiClassPolicy {
- public PreventChangingVisibility() {}
-
- public static class TargetGroup {
- private final MergeGroup group = new MergeGroup();
- private final Map<Wrapper<DexMethod>, MethodAccessFlags> methodMap = new HashMap<>();
-
- public MergeGroup getGroup() {
- return group;
- }
-
- public boolean tryAdd(DexProgramClass clazz) {
- Map<Wrapper<DexMethod>, MethodAccessFlags> newMethods = new HashMap<>();
- for (DexEncodedMethod method : clazz.methods()) {
- Wrapper<DexMethod> methodSignature =
- MethodSignatureEquivalence.get().wrap(method.getReference());
- MethodAccessFlags flags = methodMap.get(methodSignature);
-
- if (flags == null) {
- newMethods.put(methodSignature, method.getAccessFlags());
- } else {
- if (!flags.isSameVisibility(method.getAccessFlags())) {
- return false;
- }
- }
- }
-
- methodMap.putAll(newMethods);
- group.add(clazz);
- return true;
- }
- }
-
- @Override
- public Collection<MergeGroup> apply(MergeGroup group) {
- List<TargetGroup> groups = new ArrayList<>();
-
- for (DexProgramClass clazz : group) {
- boolean added = Iterables.any(groups, targetGroup -> targetGroup.tryAdd(clazz));
- if (!added) {
- TargetGroup newGroup = new TargetGroup();
- added = newGroup.tryAdd(clazz);
- assert added;
- groups.add(newGroup);
- }
- }
-
- Collection<MergeGroup> newGroups = new ArrayList<>();
- for (TargetGroup newGroup : groups) {
- if (!newGroup.getGroup().isTrivial()) {
- newGroups.add(newGroup.getGroup());
- }
- }
- return newGroups;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFields.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFields.java
deleted file mode 100644
index 63134de..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFields.java
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.horizontalclassmerging.policies;
-
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.horizontalclassmerging.FieldMultiset;
-import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
-
-public class SameFields extends MultiClassSameReferencePolicy<FieldMultiset> {
-
- @Override
- public FieldMultiset getMergeKey(DexProgramClass clazz) {
- return new FieldMultiset(clazz);
- }
-}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameInstanceFields.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameInstanceFields.java
new file mode 100644
index 0000000..13e0388
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameInstanceFields.java
@@ -0,0 +1,84 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.horizontalclassmerging.policies;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.FieldAccessFlags;
+import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
+import com.android.tools.r8.horizontalclassmerging.policies.SameInstanceFields.InstanceFieldInfo;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.HashMultiset;
+import com.google.common.collect.Multiset;
+import java.util.Objects;
+
+public class SameInstanceFields extends MultiClassSameReferencePolicy<Multiset<InstanceFieldInfo>> {
+
+ private final DexItemFactory dexItemFactory;
+
+ public SameInstanceFields(AppView<AppInfoWithLiveness> appView) {
+ this.dexItemFactory = appView.dexItemFactory();
+ }
+
+ @Override
+ public Multiset<InstanceFieldInfo> getMergeKey(DexProgramClass clazz) {
+ Multiset<InstanceFieldInfo> fields = HashMultiset.create();
+ for (DexEncodedField field : clazz.instanceFields()) {
+ fields.add(InstanceFieldInfo.createRelaxed(field, dexItemFactory));
+ }
+ return fields;
+ }
+
+ public static class InstanceFieldInfo {
+
+ private final FieldAccessFlags accessFlags;
+ private final DexType type;
+
+ private InstanceFieldInfo(FieldAccessFlags accessFlags, DexType type) {
+ this.accessFlags =
+ FieldAccessFlags.fromSharedAccessFlags(accessFlags.materialize())
+ .unsetFinal()
+ .unsetSynthetic();
+ this.type = type;
+ }
+
+ public static InstanceFieldInfo createExact(DexEncodedField field) {
+ return new InstanceFieldInfo(field.getAccessFlags(), field.getType());
+ }
+
+ public static InstanceFieldInfo createRelaxed(
+ DexEncodedField field, DexItemFactory dexItemFactory) {
+ return new InstanceFieldInfo(
+ field.getAccessFlags(),
+ field.getType().isReferenceType() ? dexItemFactory.objectType : field.getType());
+ }
+
+ public FieldAccessFlags getAccessFlags() {
+ return accessFlags;
+ }
+
+ public InstanceFieldInfo toInfoWithRelaxedType(DexItemFactory dexItemFactory) {
+ return new InstanceFieldInfo(
+ accessFlags, type.isReferenceType() ? dexItemFactory.objectType : type);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ InstanceFieldInfo info = (InstanceFieldInfo) obj;
+ return accessFlags.materialize() == info.accessFlags.materialize() && type == info.type;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(accessFlags, type);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/AlwaysSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/AlwaysSimpleInliningConstraint.java
new file mode 100644
index 0000000..32e01d8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/AlwaysSimpleInliningConstraint.java
@@ -0,0 +1,36 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.inlining;
+
+import com.android.tools.r8.ir.code.InvokeMethod;
+import it.unimi.dsi.fastutil.ints.IntList;
+
+/** Constraint that is always satisfied. */
+public class AlwaysSimpleInliningConstraint extends SimpleInliningConstraint {
+
+ public static final AlwaysSimpleInliningConstraint INSTANCE =
+ new AlwaysSimpleInliningConstraint();
+
+ private AlwaysSimpleInliningConstraint() {}
+
+ public static AlwaysSimpleInliningConstraint getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public boolean isAlways() {
+ return true;
+ }
+
+ @Override
+ public boolean isSatisfied(InvokeMethod invoke) {
+ return false;
+ }
+
+ @Override
+ public SimpleInliningConstraint rewrittenWithUnboxedArguments(IntList unboxedArgumentIndices) {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/BooleanFalseSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/BooleanFalseSimpleInliningConstraint.java
new file mode 100644
index 0000000..a4a6a73
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/BooleanFalseSimpleInliningConstraint.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.inlining;
+
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Value;
+import it.unimi.dsi.fastutil.ints.IntList;
+
+/** Constraint that is satisfied if a specific argument is always false. */
+public class BooleanFalseSimpleInliningConstraint extends SimpleInliningArgumentConstraint {
+
+ private BooleanFalseSimpleInliningConstraint(int argumentIndex) {
+ super(argumentIndex);
+ }
+
+ static BooleanFalseSimpleInliningConstraint create(
+ int argumentIndex, SimpleInliningConstraintFactory witness) {
+ assert witness != null;
+ return new BooleanFalseSimpleInliningConstraint(argumentIndex);
+ }
+
+ @Override
+ public boolean isBooleanFalse() {
+ return true;
+ }
+
+ @Override
+ public boolean isSatisfied(InvokeMethod invoke) {
+ Value argument = getArgument(invoke);
+ assert argument.getType().isInt();
+ return argument.isConstBoolean(false);
+ }
+
+ @Override
+ public SimpleInliningConstraint rewrittenWithUnboxedArguments(IntList unboxedArgumentIndices) {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/BooleanTrueSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/BooleanTrueSimpleInliningConstraint.java
new file mode 100644
index 0000000..f69381f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/BooleanTrueSimpleInliningConstraint.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.inlining;
+
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Value;
+import it.unimi.dsi.fastutil.ints.IntList;
+
+/** Constraint that is satisfied if a specific argument is always true. */
+public class BooleanTrueSimpleInliningConstraint extends SimpleInliningArgumentConstraint {
+
+ private BooleanTrueSimpleInliningConstraint(int argumentIndex) {
+ super(argumentIndex);
+ }
+
+ static BooleanTrueSimpleInliningConstraint create(
+ int argumentIndex, SimpleInliningConstraintFactory witness) {
+ assert witness != null;
+ return new BooleanTrueSimpleInliningConstraint(argumentIndex);
+ }
+
+ @Override
+ public boolean isBooleanTrue() {
+ return true;
+ }
+
+ @Override
+ public boolean isSatisfied(InvokeMethod invoke) {
+ Value argument = getArgument(invoke);
+ assert argument.getType().isInt();
+ return argument.isConstBoolean(true);
+ }
+
+ @Override
+ public SimpleInliningConstraint rewrittenWithUnboxedArguments(IntList unboxedArgumentIndices) {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NeverSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NeverSimpleInliningConstraint.java
new file mode 100644
index 0000000..9407b7b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NeverSimpleInliningConstraint.java
@@ -0,0 +1,35 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.inlining;
+
+import com.android.tools.r8.ir.code.InvokeMethod;
+import it.unimi.dsi.fastutil.ints.IntList;
+
+/** Constraint that is never satisfied. */
+public class NeverSimpleInliningConstraint extends SimpleInliningConstraint {
+
+ public static final NeverSimpleInliningConstraint INSTANCE = new NeverSimpleInliningConstraint();
+
+ private NeverSimpleInliningConstraint() {}
+
+ public static NeverSimpleInliningConstraint getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public boolean isNever() {
+ return true;
+ }
+
+ @Override
+ public boolean isSatisfied(InvokeMethod invoke) {
+ return false;
+ }
+
+ @Override
+ public SimpleInliningConstraint rewrittenWithUnboxedArguments(IntList unboxedArgumentIndices) {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NotNullSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NotNullSimpleInliningConstraint.java
new file mode 100644
index 0000000..ea41ccb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NotNullSimpleInliningConstraint.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.inlining;
+
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Value;
+import it.unimi.dsi.fastutil.ints.IntList;
+
+/** Constraint that is satisfied if a specific argument is always non-null. */
+public class NotNullSimpleInliningConstraint extends SimpleInliningArgumentConstraint {
+
+ private NotNullSimpleInliningConstraint(int argumentIndex) {
+ super(argumentIndex);
+ }
+
+ static NotNullSimpleInliningConstraint create(
+ int argumentIndex, SimpleInliningConstraintFactory witness) {
+ assert witness != null;
+ return new NotNullSimpleInliningConstraint(argumentIndex);
+ }
+
+ @Override
+ public boolean isNotNull() {
+ return true;
+ }
+
+ @Override
+ public boolean isSatisfied(InvokeMethod invoke) {
+ Value argument = getArgument(invoke);
+ assert argument.getType().isReferenceType() : invoke;
+ return argument.isNeverNull();
+ }
+
+ @Override
+ public SimpleInliningConstraint rewrittenWithUnboxedArguments(IntList unboxedArgumentIndices) {
+ if (unboxedArgumentIndices.contains(getArgumentIndex())) {
+ // TODO(b/176067541): Could be refined to an argument-equals-int constraint.
+ return NeverSimpleInliningConstraint.getInstance();
+ }
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NullSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NullSimpleInliningConstraint.java
new file mode 100644
index 0000000..76d075d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NullSimpleInliningConstraint.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.inlining;
+
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Value;
+import it.unimi.dsi.fastutil.ints.IntList;
+
+/** Constraint that is satisfied if a specific argument is always null. */
+public class NullSimpleInliningConstraint extends SimpleInliningArgumentConstraint {
+
+ private NullSimpleInliningConstraint(int argumentIndex) {
+ super(argumentIndex);
+ }
+
+ static NullSimpleInliningConstraint create(
+ int argumentIndex, SimpleInliningConstraintFactory witness) {
+ assert witness != null;
+ return new NullSimpleInliningConstraint(argumentIndex);
+ }
+
+ @Override
+ public boolean isNull() {
+ return true;
+ }
+
+ @Override
+ public boolean isSatisfied(InvokeMethod invoke) {
+ Value argument = getArgument(invoke);
+ assert argument.getType().isReferenceType();
+ return argument.getType().isDefinitelyNull();
+ }
+
+ @Override
+ public SimpleInliningConstraint rewrittenWithUnboxedArguments(IntList unboxedArgumentIndices) {
+ if (unboxedArgumentIndices.contains(getArgumentIndex())) {
+ // TODO(b/176067541): Could be refined to an argument-equals-int constraint.
+ return NeverSimpleInliningConstraint.getInstance();
+ }
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningArgumentConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningArgumentConstraint.java
new file mode 100644
index 0000000..749d6f4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningArgumentConstraint.java
@@ -0,0 +1,30 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.inlining;
+
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Value;
+
+public abstract class SimpleInliningArgumentConstraint extends SimpleInliningConstraint {
+
+ private final int argumentIndex;
+
+ SimpleInliningArgumentConstraint(int argumentIndex) {
+ this.argumentIndex = argumentIndex;
+ }
+
+ Value getArgument(InvokeMethod invoke) {
+ return invoke.getArgument(argumentIndex);
+ }
+
+ int getArgumentIndex() {
+ return argumentIndex;
+ }
+
+ @Override
+ public boolean isArgumentConstraint() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraint.java
new file mode 100644
index 0000000..b411c8b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraint.java
@@ -0,0 +1,111 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.inlining;
+
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.google.common.collect.ImmutableList;
+import it.unimi.dsi.fastutil.ints.IntList;
+import java.util.function.Supplier;
+
+public abstract class SimpleInliningConstraint {
+
+ public boolean isAlways() {
+ return false;
+ }
+
+ public boolean isArgumentConstraint() {
+ return false;
+ }
+
+ public boolean isBooleanFalse() {
+ return false;
+ }
+
+ public boolean isBooleanTrue() {
+ return false;
+ }
+
+ public boolean isConjunction() {
+ return false;
+ }
+
+ public SimpleInliningConstraintConjunction asConjunction() {
+ return null;
+ }
+
+ public boolean isDisjunction() {
+ return false;
+ }
+
+ public SimpleInliningConstraintDisjunction asDisjunction() {
+ return null;
+ }
+
+ public boolean isNever() {
+ return false;
+ }
+
+ public boolean isNotNull() {
+ return false;
+ }
+
+ public boolean isNull() {
+ return false;
+ }
+
+ public abstract boolean isSatisfied(InvokeMethod invoke);
+
+ public final SimpleInliningConstraint meet(SimpleInliningConstraint other) {
+ if (isAlways()) {
+ return other;
+ }
+ if (other.isAlways()) {
+ return this;
+ }
+ if (isNever() || other.isNever()) {
+ return NeverSimpleInliningConstraint.getInstance();
+ }
+ if (isConjunction()) {
+ return asConjunction().add(other);
+ }
+ if (other.isConjunction()) {
+ return other.asConjunction().add(this);
+ }
+ assert isArgumentConstraint() || isDisjunction();
+ assert other.isArgumentConstraint() || other.isDisjunction();
+ return new SimpleInliningConstraintConjunction(ImmutableList.of(this, other));
+ }
+
+ public final SimpleInliningConstraint lazyMeet(Supplier<SimpleInliningConstraint> supplier) {
+ if (isNever()) {
+ return NeverSimpleInliningConstraint.getInstance();
+ }
+ return meet(supplier.get());
+ }
+
+ public final SimpleInliningConstraint join(SimpleInliningConstraint other) {
+ if (isAlways() || other.isAlways()) {
+ return AlwaysSimpleInliningConstraint.getInstance();
+ }
+ if (isNever()) {
+ return other;
+ }
+ if (other.isNever()) {
+ return this;
+ }
+ if (isDisjunction()) {
+ return asDisjunction().add(other);
+ }
+ if (other.isDisjunction()) {
+ return other.asDisjunction().add(this);
+ }
+ assert isArgumentConstraint() || isConjunction();
+ assert other.isArgumentConstraint() || other.isConjunction();
+ return new SimpleInliningConstraintDisjunction(ImmutableList.of(this, other));
+ }
+
+ public abstract SimpleInliningConstraint rewrittenWithUnboxedArguments(
+ IntList unboxedArgumentIndices);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintAnalysis.java
new file mode 100644
index 0000000..1a7e997
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintAnalysis.java
@@ -0,0 +1,190 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.inlining;
+
+import static com.android.tools.r8.ir.code.Opcodes.GOTO;
+import static com.android.tools.r8.ir.code.Opcodes.IF;
+import static com.android.tools.r8.ir.code.Opcodes.RETURN;
+import static com.android.tools.r8.ir.code.Opcodes.THROW;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.If;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.Sets;
+import java.util.Set;
+
+/**
+ * Analysis that given a method computes a constraint which is satisfied by a concrete call site
+ * only if the method becomes simple after inlining into the concrete call site.
+ *
+ * <p>Examples of simple inlining constraints are:
+ *
+ * <ul>
+ * <li>Always simple,
+ * <li>Never simple,
+ * <li>Simple if argument i is {true, false, null, not-null},
+ * <li>Simple if argument i is true and argument j is false, or if argument i is false.
+ * </ul>
+ */
+public class SimpleInliningConstraintAnalysis {
+
+ private final SimpleInliningConstraintFactory factory;
+ private final ProgramMethod method;
+ private final InternalOptions options;
+ private final int simpleInliningConstraintThreshold;
+
+ private final Set<BasicBlock> seen = Sets.newIdentityHashSet();
+
+ public SimpleInliningConstraintAnalysis(
+ AppView<AppInfoWithLiveness> appView, ProgramMethod method) {
+ this.factory = appView.simpleInliningConstraintFactory();
+ this.method = method;
+ this.options = appView.options();
+ this.simpleInliningConstraintThreshold = appView.options().simpleInliningConstraintThreshold;
+ }
+
+ public SimpleInliningConstraint analyzeCode(IRCode code) {
+ if (method.getReference().getArity() == 0) {
+ // The method does not have any parameters, so there is no need to analyze the method.
+ return NeverSimpleInliningConstraint.getInstance();
+ }
+
+ if (options.debug) {
+ // Inlining is not enabled in debug mode.
+ return NeverSimpleInliningConstraint.getInstance();
+ }
+
+ // Run a bounded depth-first traversal to collect the path constraints that lead to early
+ // returns.
+ InstructionIterator instructionIterator =
+ code.entryBlock().iterator(code.getNumberOfArguments());
+ return analyzeInstructionsInBlock(code.entryBlock(), 0, instructionIterator);
+ }
+
+ private SimpleInliningConstraint analyzeInstructionsInBlock(BasicBlock block, int depth) {
+ return analyzeInstructionsInBlock(block, depth, block.iterator());
+ }
+
+ private SimpleInliningConstraint analyzeInstructionsInBlock(
+ BasicBlock block, int instructionDepth, InstructionIterator instructionIterator) {
+ // If we reach a block that has already been seen, give up.
+ if (!seen.add(block)) {
+ return NeverSimpleInliningConstraint.getInstance();
+ }
+
+ // Move the instruction iterator forward to the block's jump instruction, while incrementing the
+ // instruction depth of the depth-first traversal.
+ Instruction instruction = instructionIterator.next();
+ while (!instruction.isJumpInstruction()) {
+ assert !instruction.isArgument();
+ assert !instruction.isDebugInstruction();
+ if (!instruction.isAssume()) {
+ instructionDepth += 1;
+ }
+ instruction = instructionIterator.next();
+ }
+
+ // If we have exceeded the threshold, then all paths from this instruction will not lead to any
+ // early exits, so return 'never'.
+ if (instructionDepth > simpleInliningConstraintThreshold) {
+ return NeverSimpleInliningConstraint.getInstance();
+ }
+
+ // Analyze the jump instruction.
+ // TODO(b/132600418): Extend to switch and throw instructions.
+ switch (instruction.opcode()) {
+ case IF:
+ If ifInstruction = instruction.asIf();
+ if (ifInstruction.isZeroTest()) {
+ Value lhs = ifInstruction.lhs().getAliasedValue();
+ if (lhs.isArgument() && !lhs.isThis()) {
+ int argumentIndex = lhs.getDefinition().asArgument().getIndex();
+ DexType argumentType = method.getDefinition().getArgumentType(argumentIndex);
+ int currentDepth = instructionDepth;
+
+ // Compute the constraint for which paths through the true target are guaranteed to exit
+ // early.
+ SimpleInliningConstraint trueTargetConstraint =
+ computeConstraintFromIfZeroTest(
+ argumentIndex, argumentType, ifInstruction.getType())
+ // Only recurse into the true target if the constraint from the if-instruction
+ // is not 'never'.
+ .lazyMeet(
+ () ->
+ analyzeInstructionsInBlock(
+ ifInstruction.getTrueTarget(), currentDepth));
+
+ // Compute the constraint for which paths through the false target are guaranteed to
+ // exit early.
+ SimpleInliningConstraint fallthroughTargetConstraint =
+ computeConstraintFromIfZeroTest(
+ argumentIndex, argumentType, ifInstruction.getType().inverted())
+ // Only recurse into the false target if the constraint from the if-instruction
+ // is not 'never'.
+ .lazyMeet(
+ () ->
+ analyzeInstructionsInBlock(
+ ifInstruction.fallthroughBlock(), currentDepth));
+
+ // Paths going through this basic block are guaranteed to exit early if the true target
+ // is guaranteed to exit early or the false target is.
+ return trueTargetConstraint.join(fallthroughTargetConstraint);
+ }
+ }
+ break;
+
+ case GOTO:
+ return analyzeInstructionsInBlock(instruction.asGoto().getTarget(), instructionDepth);
+
+ case RETURN:
+ return AlwaysSimpleInliningConstraint.getInstance();
+
+ case THROW:
+ return block.hasCatchHandlers()
+ ? NeverSimpleInliningConstraint.getInstance()
+ : AlwaysSimpleInliningConstraint.getInstance();
+
+ default:
+ break;
+ }
+
+ // Give up.
+ return NeverSimpleInliningConstraint.getInstance();
+ }
+
+ private SimpleInliningConstraint computeConstraintFromIfZeroTest(
+ int argumentIndex, DexType argumentType, If.Type type) {
+ switch (type) {
+ case EQ:
+ if (argumentType.isReferenceType()) {
+ return factory.createNullConstraint(argumentIndex);
+ }
+ if (argumentType.isBooleanType()) {
+ return factory.createBooleanFalseConstraint(argumentIndex);
+ }
+ return NeverSimpleInliningConstraint.getInstance();
+
+ case NE:
+ if (argumentType.isReferenceType()) {
+ return factory.createNotNullConstraint(argumentIndex);
+ }
+ if (argumentType.isBooleanType()) {
+ return factory.createBooleanTrueConstraint(argumentIndex);
+ }
+ return NeverSimpleInliningConstraint.getInstance();
+
+ default:
+ return NeverSimpleInliningConstraint.getInstance();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintConjunction.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintConjunction.java
new file mode 100644
index 0000000..02a8536
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintConjunction.java
@@ -0,0 +1,80 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.inlining;
+
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.utils.ListUtils;
+import com.google.common.collect.ImmutableList;
+import it.unimi.dsi.fastutil.ints.IntList;
+import java.util.List;
+
+public class SimpleInliningConstraintConjunction extends SimpleInliningConstraint {
+
+ private final List<SimpleInliningConstraint> constraints;
+
+ public SimpleInliningConstraintConjunction(List<SimpleInliningConstraint> constraints) {
+ assert constraints.size() > 1;
+ assert constraints.stream().noneMatch(SimpleInliningConstraint::isAlways);
+ assert constraints.stream().noneMatch(SimpleInliningConstraint::isConjunction);
+ assert constraints.stream().noneMatch(SimpleInliningConstraint::isNever);
+ this.constraints = constraints;
+ }
+
+ SimpleInliningConstraint add(SimpleInliningConstraint constraint) {
+ assert !constraint.isAlways();
+ assert !constraint.isNever();
+ if (constraint.isConjunction()) {
+ return addAll(constraint.asConjunction());
+ }
+ assert constraint.isArgumentConstraint() || constraint.isDisjunction();
+ return new SimpleInliningConstraintConjunction(
+ ImmutableList.<SimpleInliningConstraint>builder()
+ .addAll(constraints)
+ .add(constraint)
+ .build());
+ }
+
+ public SimpleInliningConstraintConjunction addAll(
+ SimpleInliningConstraintConjunction conjunction) {
+ return new SimpleInliningConstraintConjunction(
+ ImmutableList.<SimpleInliningConstraint>builder()
+ .addAll(constraints)
+ .addAll(conjunction.constraints)
+ .build());
+ }
+
+ @Override
+ public boolean isConjunction() {
+ return true;
+ }
+
+ @Override
+ public SimpleInliningConstraintConjunction asConjunction() {
+ return this;
+ }
+
+ @Override
+ public boolean isSatisfied(InvokeMethod invoke) {
+ for (SimpleInliningConstraint constraint : constraints) {
+ if (!constraint.isSatisfied(invoke)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public SimpleInliningConstraint rewrittenWithUnboxedArguments(IntList unboxedArgumentIndices) {
+ List<SimpleInliningConstraint> rewrittenConstraints =
+ ListUtils.mapOrElse(
+ constraints,
+ constraint -> constraint.rewrittenWithUnboxedArguments(unboxedArgumentIndices),
+ null);
+ if (rewrittenConstraints != null) {
+ return new SimpleInliningConstraintConjunction(rewrittenConstraints);
+ }
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintDisjunction.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintDisjunction.java
new file mode 100644
index 0000000..c069f37
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintDisjunction.java
@@ -0,0 +1,80 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.inlining;
+
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.utils.ListUtils;
+import com.google.common.collect.ImmutableList;
+import it.unimi.dsi.fastutil.ints.IntList;
+import java.util.List;
+
+public class SimpleInliningConstraintDisjunction extends SimpleInliningConstraint {
+
+ private final List<SimpleInliningConstraint> constraints;
+
+ public SimpleInliningConstraintDisjunction(List<SimpleInliningConstraint> constraints) {
+ assert constraints.size() > 1;
+ assert constraints.stream().noneMatch(SimpleInliningConstraint::isAlways);
+ assert constraints.stream().noneMatch(SimpleInliningConstraint::isDisjunction);
+ assert constraints.stream().noneMatch(SimpleInliningConstraint::isNever);
+ this.constraints = constraints;
+ }
+
+ SimpleInliningConstraint add(SimpleInliningConstraint constraint) {
+ assert !constraint.isAlways();
+ assert !constraint.isNever();
+ if (constraint.isDisjunction()) {
+ return addAll(constraint.asDisjunction());
+ }
+ assert constraint.isArgumentConstraint() || constraint.isConjunction();
+ return new SimpleInliningConstraintDisjunction(
+ ImmutableList.<SimpleInliningConstraint>builder()
+ .addAll(constraints)
+ .add(constraint)
+ .build());
+ }
+
+ public SimpleInliningConstraintDisjunction addAll(
+ SimpleInliningConstraintDisjunction disjunction) {
+ return new SimpleInliningConstraintDisjunction(
+ ImmutableList.<SimpleInliningConstraint>builder()
+ .addAll(constraints)
+ .addAll(disjunction.constraints)
+ .build());
+ }
+
+ @Override
+ public boolean isDisjunction() {
+ return true;
+ }
+
+ @Override
+ public SimpleInliningConstraintDisjunction asDisjunction() {
+ return this;
+ }
+
+ @Override
+ public boolean isSatisfied(InvokeMethod invoke) {
+ for (SimpleInliningConstraint constraint : constraints) {
+ if (constraint.isSatisfied(invoke)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public SimpleInliningConstraint rewrittenWithUnboxedArguments(IntList unboxedArgumentIndices) {
+ List<SimpleInliningConstraint> rewrittenConstraints =
+ ListUtils.mapOrElse(
+ constraints,
+ constraint -> constraint.rewrittenWithUnboxedArguments(unboxedArgumentIndices),
+ null);
+ if (rewrittenConstraints != null) {
+ return new SimpleInliningConstraintDisjunction(rewrittenConstraints);
+ }
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintFactory.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintFactory.java
new file mode 100644
index 0000000..99603e8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintFactory.java
@@ -0,0 +1,86 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.inlining;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Supplier;
+
+public class SimpleInliningConstraintFactory {
+
+ // Immutable argument constraints for low argument indices to avoid overhead of ConcurrentHashMap.
+ private final BooleanFalseSimpleInliningConstraint[] lowBooleanFalseConstraints =
+ new BooleanFalseSimpleInliningConstraint[5];
+ private final BooleanTrueSimpleInliningConstraint[] lowBooleanTrueConstraints =
+ new BooleanTrueSimpleInliningConstraint[5];
+ private final NotNullSimpleInliningConstraint[] lowNotNullConstraints =
+ new NotNullSimpleInliningConstraint[5];
+ private final NullSimpleInliningConstraint[] lowNullConstraints =
+ new NullSimpleInliningConstraint[5];
+
+ // Argument constraints for high argument indices.
+ private final Map<Integer, BooleanFalseSimpleInliningConstraint> highBooleanFalseConstraints =
+ new ConcurrentHashMap<>();
+ private final Map<Integer, BooleanTrueSimpleInliningConstraint> highBooleanTrueConstraints =
+ new ConcurrentHashMap<>();
+ private final Map<Integer, NotNullSimpleInliningConstraint> highNotNullConstraints =
+ new ConcurrentHashMap<>();
+ private final Map<Integer, NullSimpleInliningConstraint> highNullConstraints =
+ new ConcurrentHashMap<>();
+
+ public SimpleInliningConstraintFactory() {
+ for (int i = 0; i < lowBooleanFalseConstraints.length; i++) {
+ lowBooleanFalseConstraints[i] = BooleanFalseSimpleInliningConstraint.create(i, this);
+ }
+ for (int i = 0; i < lowBooleanTrueConstraints.length; i++) {
+ lowBooleanTrueConstraints[i] = BooleanTrueSimpleInliningConstraint.create(i, this);
+ }
+ for (int i = 0; i < lowNotNullConstraints.length; i++) {
+ lowNotNullConstraints[i] = NotNullSimpleInliningConstraint.create(i, this);
+ }
+ for (int i = 0; i < lowNullConstraints.length; i++) {
+ lowNullConstraints[i] = NullSimpleInliningConstraint.create(i, this);
+ }
+ }
+
+ public BooleanFalseSimpleInliningConstraint createBooleanFalseConstraint(int argumentIndex) {
+ return createArgumentConstraint(
+ argumentIndex,
+ lowBooleanFalseConstraints,
+ highBooleanFalseConstraints,
+ () -> BooleanFalseSimpleInliningConstraint.create(argumentIndex, this));
+ }
+
+ public BooleanTrueSimpleInliningConstraint createBooleanTrueConstraint(int argumentIndex) {
+ return createArgumentConstraint(
+ argumentIndex,
+ lowBooleanTrueConstraints,
+ highBooleanTrueConstraints,
+ () -> BooleanTrueSimpleInliningConstraint.create(argumentIndex, this));
+ }
+
+ public NotNullSimpleInliningConstraint createNotNullConstraint(int argumentIndex) {
+ return createArgumentConstraint(
+ argumentIndex,
+ lowNotNullConstraints,
+ highNotNullConstraints,
+ () -> NotNullSimpleInliningConstraint.create(argumentIndex, this));
+ }
+
+ public NullSimpleInliningConstraint createNullConstraint(int argumentIndex) {
+ return createArgumentConstraint(
+ argumentIndex,
+ lowNullConstraints,
+ highNullConstraints,
+ () -> NullSimpleInliningConstraint.create(argumentIndex, this));
+ }
+
+ private <T extends SimpleInliningArgumentConstraint> T createArgumentConstraint(
+ int argumentIndex, T[] lowConstraints, Map<Integer, T> highConstraints, Supplier<T> fn) {
+ return argumentIndex < lowConstraints.length
+ ? lowConstraints[argumentIndex]
+ : highConstraints.computeIfAbsent(argumentIndex, key -> fn.get());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
index 7dca039..2a6be81 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
@@ -4,9 +4,13 @@
package com.android.tools.r8.ir.analysis.proto;
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+
import com.android.tools.r8.code.CfOrDexInstruction;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.proto.schema.ProtoEnqueuerExtension;
@@ -22,7 +26,9 @@
private final ProtoReferences references;
public ProtoEnqueuerUseRegistry(
- AppView<?> appView, ProgramMethod currentMethod, Enqueuer enqueuer) {
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ ProgramMethod currentMethod,
+ Enqueuer enqueuer) {
super(appView, currentMethod, enqueuer);
this.references = appView.protoShrinker().references;
}
@@ -50,17 +56,29 @@
/**
* Unlike {@link DefaultEnqueuerUseRegistry#registerStaticFieldRead(DexField)}, this method does
- * not trace any static-get instructions in every implementation of dynamicMethod().
+ * not trace any static-get instructions in every implementation of dynamicMethod() that accesses
+ * an 'INSTANCE' or a 'DEFAULT_INSTANCE' field.
*
* <p>The static-get instructions that remain after the proto schema has been optimized will be
* traced manually by {@link ProtoEnqueuerExtension#tracePendingInstructionsInDynamicMethods}.
*/
@Override
public void registerStaticFieldRead(DexField field) {
- if (references.isDynamicMethod(getContextMethod())) {
+ if (references.isDynamicMethod(getContextMethod())
+ && isStaticFieldReadForProtoSchemaDefinition(field)) {
enqueuer.addDeadProtoTypeCandidate(field.holder);
return;
}
super.registerStaticFieldRead(field);
}
+
+ private boolean isStaticFieldReadForProtoSchemaDefinition(DexField field) {
+ if (field == references.getDefaultInstanceField(getContextHolder())) {
+ return true;
+ }
+ DexProgramClass holder = asProgramClassOrNull(appView.definitionFor(field.getHolderType()));
+ return holder != null
+ && holder.getInterfaces().contains(references.enumVerifierType)
+ && field == references.getEnumVerifierInstanceField(holder);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
index 84aabe8..b25f946 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
@@ -21,6 +21,7 @@
public final DexType enumLiteType;
public final DexType enumLiteMapType;
+ public final DexType enumVerifierType;
public final DexType extendableMessageType;
public final DexType extensionDescriptorType;
public final DexType extensionRegistryLiteType;
@@ -42,6 +43,7 @@
public final MethodToInvokeMembers methodToInvokeMembers;
public final DexString defaultInstanceFieldName;
+ public final DexString instanceFieldName;
public final DexString internalValueMapFieldName;
public final DexString dynamicMethodName;
public final DexString findLiteExtensionByNumberName;
@@ -63,6 +65,7 @@
// Types.
enumLiteType = factory.createType("Lcom/google/protobuf/Internal$EnumLite;");
enumLiteMapType = factory.createType("Lcom/google/protobuf/Internal$EnumLiteMap;");
+ enumVerifierType = factory.createType("Lcom/google/protobuf/Internal$EnumVerifier;");
extendableMessageType =
factory.createType("Lcom/google/protobuf/GeneratedMessageLite$ExtendableMessage;");
extensionDescriptorType =
@@ -85,6 +88,7 @@
// Names.
defaultInstanceFieldName = factory.createString("DEFAULT_INSTANCE");
+ instanceFieldName = factory.createString("INSTANCE");
internalValueMapFieldName = factory.createString("internalValueMap");
dynamicMethodName = factory.createString("dynamicMethod");
findLiteExtensionByNumberName = factory.createString("findLiteExtensionByNumber");
@@ -129,6 +133,10 @@
return dexItemFactory.createField(holder.type, holder.type, defaultInstanceFieldName);
}
+ public DexField getEnumVerifierInstanceField(DexProgramClass holder) {
+ return dexItemFactory.createField(holder.type, enumVerifierType, instanceFieldName);
+ }
+
public boolean isAbstractGeneratedMessageLiteBuilder(DexProgramClass clazz) {
return clazz.type == generatedMessageLiteBuilderType
|| clazz.type == generatedMessageLiteExtendableBuilderType;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
index d84295c..41ae033 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldResolutionResult;
+import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.analysis.EnqueuerAnalysis;
@@ -128,7 +129,7 @@
* ProtoMessageInfo} object, and create a mapping from the holder to it.
*/
@Override
- public void processNewlyLiveMethod(ProgramMethod method) {
+ public void processNewlyLiveMethod(ProgramMethod method, ProgramDefinition context) {
if (references.isFindLiteExtensionByNumberMethod(method.getReference())) {
findLiteExtensionByNumberMethods.add(method);
return;
@@ -381,7 +382,7 @@
// written such that we cannot optimize any field reads or writes.
enqueuer.registerReflectiveFieldAccess(valueStorage.getReference(), dynamicMethod);
worklist.enqueueMarkReachableFieldAction(
- valueStorage, KeepReason.reflectiveUseIn(dynamicMethod));
+ valueStorage, dynamicMethod, KeepReason.reflectiveUseIn(dynamicMethod));
valueStorageIsLive = true;
} else {
valueStorageIsLive = false;
@@ -446,8 +447,7 @@
// dynamicMethod().
if (enqueuer.registerReflectiveFieldWrite(newlyLiveField.getReference(), dynamicMethod)) {
worklist.enqueueMarkReachableFieldAction(
- newlyLiveField,
- KeepReason.reflectiveUseIn(dynamicMethod));
+ newlyLiveField, dynamicMethod, KeepReason.reflectiveUseIn(dynamicMethod));
}
}
}
@@ -567,7 +567,7 @@
if (enqueuer.registerReflectiveFieldWrite(oneOfField.getReference(), dynamicMethod)) {
worklist.enqueueMarkReachableFieldAction(
- oneOfField, KeepReason.reflectiveUseIn(dynamicMethod));
+ oneOfField, dynamicMethod, KeepReason.reflectiveUseIn(dynamicMethod));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index c9377f7..e81d765 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -1749,7 +1749,7 @@
// one new phi to merge the two exception values, and all other phis don't need
// to be changed.
for (BasicBlock catchSuccessor : catchSuccessors) {
- catchSuccessor.splitCriticalExceptionEdges(code, blockIterator::add, options);
+ catchSuccessor.splitCriticalExceptionEdges(code, blockIterator, options);
}
}
@@ -1767,7 +1767,7 @@
* and Goto.
*/
public void splitCriticalExceptionEdges(
- IRCode code, Consumer<BasicBlock> onNewBlock, InternalOptions options) {
+ IRCode code, ListIterator<BasicBlock> blockIterator, InternalOptions options) {
List<BasicBlock> predecessors = getMutablePredecessors();
boolean hasMoveException = entry().isMoveException();
TypeElement exceptionTypeLattice = null;
@@ -1783,7 +1783,7 @@
getInstructions().remove(0);
}
// Create new predecessor blocks.
- List<BasicBlock> newPredecessors = new ArrayList<>();
+ List<BasicBlock> newPredecessors = new ArrayList<>(predecessors.size());
List<Value> values = new ArrayList<>(predecessors.size());
for (BasicBlock predecessor : predecessors) {
if (!predecessor.hasCatchSuccessor(this)) {
@@ -1808,7 +1808,7 @@
newBlock.getMutableSuccessors().add(this);
newBlock.getMutablePredecessors().add(predecessor);
predecessor.replaceSuccessor(this, newBlock);
- onNewBlock.accept(newBlock);
+ blockIterator.add(newBlock);
assert newBlock.getNumber() >= 0 : "Number must be assigned by `onNewBlock`";
}
// Replace the blocks predecessors with the new ones.
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
index 54354f6..ee770e8 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
@@ -123,6 +123,26 @@
metadata.record(instruction);
}
+ @Override
+ public void addThrowingInstructionToPossiblyThrowingBlock(
+ IRCode code,
+ ListIterator<BasicBlock> blockIterator,
+ Instruction instruction,
+ InternalOptions options) {
+ if (block.hasCatchHandlers()) {
+ BasicBlock splitBlock = split(code, blockIterator, false);
+ splitBlock.listIterator(code).add(instruction);
+ assert !block.hasCatchHandlers();
+ assert splitBlock.hasCatchHandlers();
+ block.copyCatchHandlers(code, blockIterator, splitBlock, options);
+ while (IteratorUtils.peekPrevious(blockIterator) != splitBlock) {
+ blockIterator.previous();
+ }
+ } else {
+ add(instruction);
+ }
+ }
+
/**
* Replaces the last instruction returned by {@link #next} or {@link #previous} with the specified
* instruction.
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index 8e7236c..01cf2ba 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.ir.code.Phi.RegisterReadType;
import com.android.tools.r8.ir.conversion.IRBuilder;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.CfgPrinter;
import com.android.tools.r8.utils.DequeUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -1064,6 +1065,11 @@
return nextInstructionNumber;
}
+ public int getNumberOfArguments() {
+ return context().getReference().getArity()
+ + BooleanUtils.intValue(!context().getDefinition().isStatic());
+ }
+
public List<Value> collectArguments() {
return collectArguments(false);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
index d2b9ffb..a96760f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
@@ -161,6 +161,16 @@
}
@Override
+ public void addThrowingInstructionToPossiblyThrowingBlock(
+ IRCode code,
+ ListIterator<BasicBlock> blockIterator,
+ Instruction instruction,
+ InternalOptions options) {
+ instructionIterator.addThrowingInstructionToPossiblyThrowingBlock(
+ code, blockIterator, instruction, options);
+ }
+
+ @Override
public void remove() {
instructionIterator.remove();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
index 1acdb98..c4cceea 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
@@ -20,6 +20,12 @@
public interface InstructionListIterator
extends InstructionIterator, ListIterator<Instruction>, PreviousUntilIterator<Instruction> {
+ void addThrowingInstructionToPossiblyThrowingBlock(
+ IRCode code,
+ ListIterator<BasicBlock> blockIterator,
+ Instruction instruction,
+ InternalOptions options);
+
default void addBefore(Instruction instruction) {
previous();
add(instruction);
diff --git a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
index e6009dd..0fc2768 100644
--- a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
@@ -131,6 +131,16 @@
}
@Override
+ public void addThrowingInstructionToPossiblyThrowingBlock(
+ IRCode code,
+ ListIterator<BasicBlock> blockIterator,
+ Instruction instruction,
+ InternalOptions options) {
+ currentBlockIterator.addThrowingInstructionToPossiblyThrowingBlock(
+ code, blockIterator, instruction, options);
+ }
+
+ @Override
public void removeOrReplaceByDebugLocalRead() {
currentBlockIterator.removeOrReplaceByDebugLocalRead();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index b842c63..0f0d6e9 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -30,6 +30,7 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.MethodPosition;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.LongInterval;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.SetUtils;
@@ -778,6 +779,11 @@
return isConstant() && getConstInstruction().isConstNumber();
}
+ public boolean isConstBoolean(boolean value) {
+ return isConstNumber()
+ && definition.asConstNumber().getRawValue() == BooleanUtils.longValue(value);
+ }
+
public boolean isConstZero() {
return isConstNumber() && definition.asConstNumber().isZero();
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index 76e3b0f..b6609b8 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -55,6 +55,7 @@
import com.android.tools.r8.graph.RewrittenPrototypeDescription.RewrittenTypeInfo;
import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
import com.android.tools.r8.ir.analysis.type.DestructivePhiTypeUpdater;
+import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.SingleNumberValue;
import com.android.tools.r8.ir.code.Assume;
@@ -64,6 +65,7 @@
import com.android.tools.r8.ir.code.ConstClass;
import com.android.tools.r8.ir.code.ConstInstruction;
import com.android.tools.r8.ir.code.ConstMethodHandle;
+import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.InitClass;
import com.android.tools.r8.ir.code.InstanceGet;
@@ -92,6 +94,7 @@
import com.android.tools.r8.ir.optimize.enums.EnumUnboxer;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.optimize.MemberRebindingAnalysis;
+import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.HashSet;
@@ -105,14 +108,15 @@
public class LensCodeRewriter {
private final AppView<? extends AppInfoWithClassHierarchy> appView;
-
private final EnumUnboxer enumUnboxer;
private final LensCodeRewriterUtils helper;
+ private final InternalOptions options;
LensCodeRewriter(AppView<? extends AppInfoWithClassHierarchy> appView, EnumUnboxer enumUnboxer) {
this.appView = appView;
this.enumUnboxer = enumUnboxer;
this.helper = new LensCodeRewriterUtils(appView);
+ this.options = appView.options();
}
private Value makeOutValue(Instruction insn, IRCode code) {
@@ -125,6 +129,15 @@
return null;
}
+ private Value makeOutValue(FieldInstruction insn, IRCode code, DexField rewrittenField) {
+ if (insn.hasOutValue()) {
+ Nullability nullability = insn.getOutType().nullability();
+ TypeElement newType = TypeElement.fromDexType(rewrittenField.getType(), nullability, appView);
+ return code.createValue(newType, insn.getLocalInfo());
+ }
+ return null;
+ }
+
/** Replace type appearances, invoke targets and field accesses with actual definitions. */
public void rewrite(IRCode code, ProgramMethod method) {
Set<Phi> affectedPhis =
@@ -137,7 +150,7 @@
boolean mayHaveUnreachableBlocks = false;
while (blocks.hasNext()) {
BasicBlock block = blocks.next();
- if (block.hasCatchHandlers() && appView.options().enableVerticalClassMerging) {
+ if (block.hasCatchHandlers() && options.enableVerticalClassMerging) {
boolean anyGuardsRenamed = block.renameGuardsInCatchHandlers(graphLens);
if (anyGuardsRenamed) {
mayHaveUnreachableBlocks |= unlinkDeadCatchHandlers(block);
@@ -312,9 +325,7 @@
parameter.getTypeElement(appView, type), null));
assert !instruction.instructionTypeCanThrow();
instruction.setPosition(
- appView.options().debug
- ? invoke.getPosition()
- : Position.none());
+ options.debug ? invoke.getPosition() : Position.none());
iterator.add(instruction);
iterator.next();
return instruction.outValue();
@@ -389,11 +400,11 @@
graphLens.lookupGetFieldForMethod(rewrittenField, method.getReference());
Value newOutValue = null;
if (replacementMethod != null) {
- newOutValue = makeOutValue(instanceGet, code);
+ newOutValue = makeOutValue(instanceGet, code, rewrittenField);
iterator.replaceCurrentInstruction(
new InvokeStatic(replacementMethod, newOutValue, instanceGet.inValues()));
} else if (rewrittenField != field) {
- newOutValue = makeOutValue(instanceGet, code);
+ newOutValue = makeOutValue(instanceGet, code, rewrittenField);
iterator.replaceCurrentInstruction(
new InstanceGet(newOutValue, instanceGet.object(), rewrittenField));
}
@@ -402,15 +413,17 @@
TypeElement castType =
TypeElement.fromDexType(
lookup.getCastType(), newOutValue.getType().nullability(), appView);
+ Value castOutValue = code.createValue(castType);
+ newOutValue.replaceUsers(castOutValue);
CheckCast checkCast =
CheckCast.builder()
.setCastType(lookup.getCastType())
- .setFreshOutValue(code, castType)
.setObject(newOutValue)
+ .setOutValue(castOutValue)
.setPosition(instanceGet)
.build();
- iterator.add(checkCast);
- newOutValue.replaceUsers(checkCast.outValue());
+ iterator.addThrowingInstructionToPossiblyThrowingBlock(
+ code, blocks, checkCast, options);
affectedPhis.addAll(checkCast.outValue().uniquePhiUsers());
} else if (newOutValue.getType() != instanceGet.getOutType()) {
affectedPhis.addAll(newOutValue.uniquePhiUsers());
@@ -452,11 +465,11 @@
graphLens.lookupGetFieldForMethod(rewrittenField, method.getReference());
Value newOutValue = null;
if (replacementMethod != null) {
- newOutValue = makeOutValue(staticGet, code);
+ newOutValue = makeOutValue(staticGet, code, rewrittenField);
iterator.replaceCurrentInstruction(
new InvokeStatic(replacementMethod, newOutValue, staticGet.inValues()));
} else if (rewrittenField != field) {
- newOutValue = makeOutValue(staticGet, code);
+ newOutValue = makeOutValue(staticGet, code, rewrittenField);
iterator.replaceCurrentInstruction(new StaticGet(newOutValue, rewrittenField));
}
if (newOutValue != null) {
@@ -464,15 +477,17 @@
TypeElement castType =
TypeElement.fromDexType(
lookup.getCastType(), newOutValue.getType().nullability(), appView);
+ Value castOutValue = code.createValue(castType);
+ newOutValue.replaceUsers(castOutValue);
CheckCast checkCast =
CheckCast.builder()
.setCastType(lookup.getCastType())
- .setFreshOutValue(code, castType)
.setObject(newOutValue)
+ .setOutValue(castOutValue)
.setPosition(staticGet)
.build();
- iterator.add(checkCast);
- newOutValue.replaceUsers(checkCast.outValue());
+ iterator.addThrowingInstructionToPossiblyThrowingBlock(
+ code, blocks, checkCast, options);
affectedPhis.addAll(checkCast.outValue().uniquePhiUsers());
} else if (newOutValue.getType() != staticGet.getOutType()) {
affectedPhis.addAll(newOutValue.uniquePhiUsers());
@@ -554,8 +569,7 @@
MoveException moveException = current.asMoveException();
new InstructionReplacer(code, current, iterator, affectedPhis)
.replaceInstructionIfTypeChanged(
- moveException.getExceptionType(),
- (t, v) -> new MoveException(v, t, appView.options()));
+ moveException.getExceptionType(), (t, v) -> new MoveException(v, t, options));
}
break;
@@ -695,7 +709,7 @@
iterator.previous();
Value rewrittenDefaultValue =
iterator.insertConstNumberInstruction(
- code, appView.options(), 0, defaultValueLatticeElement(newType));
+ code, options, 0, defaultValueLatticeElement(newType));
iterator.next();
return rewrittenDefaultValue;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
index 44f5990..b1b14fb 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
@@ -7,6 +7,8 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -71,5 +73,7 @@
void setNonNullParamOnNormalExits(DexEncodedMethod method, BitSet facts);
+ void setSimpleInliningConstraint(ProgramMethod method, SimpleInliningConstraint constraint);
+
void classInitializerMayBePostponed(DexEncodedMethod method);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index 6e7d98b..4534c9b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
+import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.InstancePut;
@@ -203,7 +204,9 @@
return false;
}
assert reason != Reason.FORCE
- || !inlineeRefersToClassesNotInMainDex(method.getHolderType(), singleTarget);
+ || !inlineeRefersToClassesNotInMainDex(method.getHolderType(), singleTarget)
+ : MainDexDirectReferenceTracer.getFirstReferenceOutsideFromCode(
+ appView.appInfo(), singleTarget, inliner.mainDexClasses.getRoots());
return true;
}
@@ -223,7 +226,11 @@
if (code.estimatedSizeForInliningAtMost(instructionLimit)) {
return true;
}
- return false;
+ // Even if the inlinee is big it may become simple after inlining. We therefore check if the
+ // inlinee's simple inlining constraint is satisfied by the invoke.
+ SimpleInliningConstraint simpleInliningConstraint =
+ target.getDefinition().getOptimizationInfo().getSimpleInliningConstraint();
+ return simpleInliningConstraint.isSatisfied(invoke);
}
private int computeInstructionLimit(InvokeMethod invoke, ProgramMethod candidate) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index 615e5ae..39ce729 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -360,7 +360,9 @@
}
AbstractValue abstractValue;
- if (appView.appInfo().isFieldWrittenByFieldPutInstruction(target)) {
+ if (field.getType().isAlwaysNull(appView)) {
+ abstractValue = appView.abstractValueFactory().createSingleNumberValue(0);
+ } else if (appView.appInfo().isFieldWrittenByFieldPutInstruction(target)) {
abstractValue = target.getOptimizationInfo().getAbstractValue();
if (abstractValue.isUnknown() && !target.isStatic()) {
AbstractValue abstractReceiverValue =
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
index c681eaa..d60ba25 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
@@ -14,8 +14,6 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.FieldAccessInfo;
-import com.android.tools.r8.graph.FieldAccessInfoCollection;
import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
import com.android.tools.r8.graph.RewrittenPrototypeDescription;
import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
@@ -97,18 +95,11 @@
public UninstantiatedTypeOptimization strenghtenOptimizationInfo() {
OptimizationFeedback feedback = OptimizationFeedbackSimple.getInstance();
- FieldAccessInfoCollection<?> fieldAccessInfoCollection =
- appView.appInfo().getFieldAccessInfoCollection();
AbstractValue nullValue = appView.abstractValueFactory().createSingleNumberValue(0);
for (DexProgramClass clazz : appView.appInfo().classes()) {
clazz.forEachField(
field -> {
if (field.type().isAlwaysNull(appView)) {
- FieldAccessInfo fieldAccessInfo = fieldAccessInfoCollection.get(field.field);
- if (fieldAccessInfo != null) {
- // Clear all writes since each write must write `null` to the field.
- fieldAccessInfo.asMutable().clearWrites();
- }
feedback.recordFieldHasAbstractValue(field, appView, nullValue);
}
});
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index e080808..143d776 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -237,8 +237,11 @@
for (Instruction user : currentUsers) {
if (user.isAssume() || user.isCheckCast()) {
if (user.isCheckCast()) {
+ CheckCast checkCast = user.asCheckCast();
+ // TODO(b/175863158): Allow unsafe casts by rewriting into throw new ClassCastException.
boolean isCheckCastUnsafe =
- !appView.appInfo().isSubtype(eligibleClass.type, user.asCheckCast().getType());
+ !checkCast.getType().isClassType()
+ || !appView.appInfo().isSubtype(eligibleClass.type, checkCast.getType());
if (isCheckCastUnsafe) {
return user; // Not eligible.
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index 1ce0df3..166022e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -22,7 +22,6 @@
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.FieldResolutionResult;
import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ProgramPackageCollection;
import com.android.tools.r8.graph.ResolutionResult;
@@ -62,6 +61,7 @@
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback.OptimizationInfoFixer;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
+import com.android.tools.r8.ir.optimize.info.UpdatableMethodOptimizationInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.utils.BooleanUtils;
@@ -394,7 +394,7 @@
enumClassesToUnbox, enumsToUnboxWithPackageRequirement, appBuilder)
.build();
enumUnboxerRewriter = new EnumUnboxingRewriter(appView, enumDataMap, relocator);
- NestedGraphLens enumUnboxingLens =
+ EnumUnboxingLens enumUnboxingLens =
new EnumUnboxingTreeFixer(appView, enumsToUnbox, relocator, enumUnboxerRewriter)
.fixupTypeReferences();
enumUnboxerRewriter.setEnumUnboxingLens(enumUnboxingLens);
@@ -430,8 +430,9 @@
public void fixup(DexEncodedMethod method) {
MethodOptimizationInfo optimizationInfo = method.getOptimizationInfo();
if (optimizationInfo.isUpdatableMethodOptimizationInfo()) {
- optimizationInfo
- .asUpdatableMethodOptimizationInfo()
+ UpdatableMethodOptimizationInfo updatableOptimizationInfo =
+ optimizationInfo.asUpdatableMethodOptimizationInfo();
+ updatableOptimizationInfo
.fixupClassTypeReferences(appView.graphLens()::lookupType, appView)
.fixupAbstractReturnValue(appView, appView.graphLens())
.fixupInstanceInitializerInfo(appView, appView.graphLens());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
index f230dda..c64e51e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
@@ -142,9 +142,7 @@
public EnumUnboxingLens build(
DexItemFactory dexItemFactory, GraphLens previousLens, Set<DexType> unboxedEnums) {
- if (typeMap.isEmpty() && newFieldSignatures.isEmpty() && originalMethodSignatures.isEmpty()) {
- return null;
- }
+ assert !typeMap.isEmpty();
return new EnumUnboxingLens(
typeMap,
originalMethodSignatures.getInverseOneToOneMap().getForwardMap(),
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index 92ce5a1..f2f70ca 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -16,9 +16,11 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue;
-import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.OptionalBool;
import com.google.common.collect.Sets;
+import it.unimi.dsi.fastutil.ints.IntArrayList;
+import it.unimi.dsi.fastutil.ints.IntList;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
@@ -47,7 +49,7 @@
this.enumUnboxerRewriter = enumUnboxerRewriter;
}
- GraphLens.NestedGraphLens fixupTypeReferences() {
+ EnumUnboxingLens fixupTypeReferences() {
assert enumUnboxerRewriter != null;
// Fix all methods and fields using enums to unbox.
for (DexProgramClass clazz : appView.appInfo().classes()) {
@@ -118,11 +120,14 @@
}
private DexEncodedMethod fixupEncodedMethod(DexEncodedMethod method) {
- DexProto newProto = fixupProto(method.proto());
+ DexProto oldProto = method.proto();
+ DexProto newProto = fixupProto(oldProto);
if (newProto == method.proto()) {
return method;
}
assert !method.isClassInitializer();
+ assert !method.isLibraryMethodOverride().isTrue()
+ : "Enum unboxing is changing the signature of a library override in a non unboxed class.";
// We add the $enumunboxing$ suffix to make sure we do not create a library override.
String newMethodName =
method.getName().toString() + (method.isNonPrivateVirtualMethod() ? "$enumunboxing$" : "");
@@ -131,17 +136,28 @@
int numberOfExtraNullParameters = newMethod.getArity() - method.method.getArity();
boolean isStatic = method.isStatic();
lensBuilder.move(method.method, newMethod, isStatic, isStatic, numberOfExtraNullParameters);
- DexEncodedMethod newEncodedMethod =
- method.toTypeSubstitutedMethod(
- newMethod,
- builder ->
- builder
- .setCompilationState(method.getCompilationState())
- .setIsLibraryMethodOverrideIf(
- method.isNonPrivateVirtualMethod(), OptionalBool.FALSE));
- assert !method.isLibraryMethodOverride().isTrue()
- : "Enum unboxing is changing the signature of a library override in a non unboxed class.";
- return newEncodedMethod;
+ return method.toTypeSubstitutedMethod(
+ newMethod,
+ builder ->
+ builder
+ .setCompilationState(method.getCompilationState())
+ .setIsLibraryMethodOverrideIf(
+ method.isNonPrivateVirtualMethod(), OptionalBool.FALSE)
+ .fixupOptimizationInfo(
+ optimizationInfo -> {
+ IntList unboxedArgumentIndices = new IntArrayList();
+ int offset = BooleanUtils.intValue(method.isInstance());
+ for (int i = 0; i < method.getReference().getArity(); i++) {
+ if (oldProto.getParameter(i).isReferenceType()
+ && newProto.getParameter(i).isPrimitiveType()) {
+ unboxedArgumentIndices.add(i + offset);
+ }
+ }
+ optimizationInfo.setSimpleInliningConstraint(
+ optimizationInfo
+ .getSimpleInliningConstraint()
+ .rewrittenWithUnboxedArguments(unboxedArgumentIndices));
+ }));
}
private DexMethod ensureUniqueMethod(DexEncodedMethod encodedMethod, DexMethod newMethod) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
index b2b10ae..a9854ee 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
@@ -5,6 +5,8 @@
package com.android.tools.r8.ir.optimize.info;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.inlining.NeverSimpleInliningConstraint;
+import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -21,11 +23,11 @@
public class DefaultMethodOptimizationInfo extends MethodOptimizationInfo {
- public static final MethodOptimizationInfo DEFAULT_INSTANCE = new DefaultMethodOptimizationInfo();
+ public static final DefaultMethodOptimizationInfo DEFAULT_INSTANCE =
+ new DefaultMethodOptimizationInfo();
static Set<DexType> UNKNOWN_INITIALIZED_CLASSES_ON_NORMAL_EXIT = ImmutableSet.of();
static int UNKNOWN_RETURNED_ARGUMENT = -1;
- static boolean UNKNOWN_NEVER_RETURNS_NULL = false;
static boolean UNKNOWN_NEVER_RETURNS_NORMALLY = false;
static AbstractValue UNKNOWN_ABSTRACT_RETURN_VALUE = UnknownValue.getInstance();
static TypeElement UNKNOWN_TYPE = null;
@@ -42,6 +44,10 @@
private DefaultMethodOptimizationInfo() {}
+ public static DefaultMethodOptimizationInfo getInstance() {
+ return DEFAULT_INSTANCE;
+ }
+
@Override
public boolean isDefaultMethodOptimizationInfo() {
return true;
@@ -150,6 +156,11 @@
}
@Override
+ public SimpleInliningConstraint getSimpleInliningConstraint() {
+ return NeverSimpleInliningConstraint.getInstance();
+ }
+
+ @Override
public boolean isInitializerEnablingJavaVmAssertions() {
return UNKNOWN_INITIALIZER_ENABLING_JAVA_ASSERTIONS;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
index cf7c4ee..7f5d173 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.optimize.info;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -77,6 +78,8 @@
public abstract AbstractValue getAbstractReturnValue();
+ public abstract SimpleInliningConstraint getSimpleInliningConstraint();
+
public abstract boolean forceInline();
public abstract boolean neverInline();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index 4e3fb01..b5dfee6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -60,6 +60,7 @@
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
import com.android.tools.r8.ir.analysis.DeterminismAnalysis;
import com.android.tools.r8.ir.analysis.InitializedClassesOnNormalExitAnalysis;
+import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraintAnalysis;
import com.android.tools.r8.ir.analysis.sideeffect.ClassInitializerSideEffectAnalysis;
import com.android.tools.r8.ir.analysis.sideeffect.ClassInitializerSideEffectAnalysis.ClassInitializerSideEffect;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
@@ -148,6 +149,7 @@
if (options.enableInlining) {
identifyInvokeSemanticsForInlining(definition, code, feedback, timing);
}
+ computeSimpleInliningConstraint(method, code, feedback, timing);
computeDynamicReturnType(dynamicTypeOptimization, feedback, definition, code, timing);
computeInitializedClassesOnNormalExit(feedback, definition, code, timing);
computeInstanceInitializerInfo(
@@ -967,6 +969,21 @@
return true;
}
+ private void computeSimpleInliningConstraint(
+ ProgramMethod method, IRCode code, OptimizationFeedback feedback, Timing timing) {
+ if (appView.options().enableSimpleInliningConstraints) {
+ timing.begin("Compute simple inlining constraint");
+ computeSimpleInliningConstraint(method, code, feedback);
+ timing.end();
+ }
+ }
+
+ private void computeSimpleInliningConstraint(
+ ProgramMethod method, IRCode code, OptimizationFeedback feedback) {
+ feedback.setSimpleInliningConstraint(
+ method, new SimpleInliningConstraintAnalysis(appView, method).analyzeCode(code));
+ }
+
private void computeDynamicReturnType(
DynamicTypeOptimization dynamicTypeOptimization,
OptimizationFeedback feedback,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
index 50cb793..1236ed6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
@@ -8,6 +8,8 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -60,6 +62,11 @@
return info;
}
+ private UpdatableMethodOptimizationInfo getMethodOptimizationInfoForUpdating(
+ ProgramMethod method) {
+ return getMethodOptimizationInfoForUpdating(method.getDefinition());
+ }
+
@Override
public void fixupOptimizationInfos(
AppView<?> appView, ExecutorService executorService, OptimizationInfoFixer fixer)
@@ -283,6 +290,12 @@
}
@Override
+ public synchronized void setSimpleInliningConstraint(
+ ProgramMethod method, SimpleInliningConstraint constraint) {
+ getMethodOptimizationInfoForUpdating(method).setSimpleInliningConstraint(constraint);
+ }
+
+ @Override
public synchronized void classInitializerMayBePostponed(DexEncodedMethod method) {
getMethodOptimizationInfoForUpdating(method).markClassInitializerMayBePostponed();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
index 12fda15..299f7ce 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
@@ -8,6 +8,8 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -130,8 +132,11 @@
public void setNonNullParamOrThrow(DexEncodedMethod method, BitSet facts) {}
@Override
- public void setNonNullParamOnNormalExits(DexEncodedMethod method, BitSet facts) {
- }
+ public void setNonNullParamOnNormalExits(DexEncodedMethod method, BitSet facts) {}
+
+ @Override
+ public void setSimpleInliningConstraint(
+ ProgramMethod method, SimpleInliningConstraint constraint) {}
@Override
public void classInitializerMayBePostponed(DexEncodedMethod method) {}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
index f0d7c28..ed477a6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -8,6 +8,8 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -194,6 +196,12 @@
}
@Override
+ public void setSimpleInliningConstraint(
+ ProgramMethod method, SimpleInliningConstraint constraint) {
+ // Ignored.
+ }
+
+ @Override
public void classInitializerMayBePostponed(DexEncodedMethod method) {
method.getMutableOptimizationInfo().markClassInitializerMayBePostponed();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
index 877c72d..03e83b8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
@@ -8,6 +8,8 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.ir.analysis.inlining.NeverSimpleInliningConstraint;
+import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -63,6 +65,9 @@
private BitSet nonNullParamOnNormalExits =
DefaultMethodOptimizationInfo.NO_NULL_PARAMETER_ON_NORMAL_EXITS_FACTS;
+ private SimpleInliningConstraint simpleInliningConstraint =
+ NeverSimpleInliningConstraint.getInstance();
+
// To reduce the memory footprint of UpdatableMethodOptimizationInfo, all the boolean fields are
// merged into a flag int field. The various static final FLAG fields indicate which bit is
// used by each boolean. DEFAULT_FLAGS encodes the default value for efficient instantiation and
@@ -137,6 +142,7 @@
returnsObjectWithUpperBoundType = template.returnsObjectWithUpperBoundType;
returnsObjectWithLowerBoundType = template.returnsObjectWithLowerBoundType;
inlining = template.inlining;
+ simpleInliningConstraint = template.simpleInliningConstraint;
bridgeInfo = template.bridgeInfo;
classInlinerEligibility = template.classInlinerEligibility;
instanceInitializerInfoCollection = template.instanceInitializerInfoCollection;
@@ -322,6 +328,11 @@
}
@Override
+ public SimpleInliningConstraint getSimpleInliningConstraint() {
+ return simpleInliningConstraint;
+ }
+
+ @Override
public boolean isInitializerEnablingJavaVmAssertions() {
return isFlagSet(INITIALIZER_ENABLING_JAVA_ASSERTIONS_FLAG);
}
@@ -372,6 +383,10 @@
setFlag(REACHABILITY_SENSITIVE_FLAG, reachabilitySensitive);
}
+ public void setSimpleInliningConstraint(SimpleInliningConstraint constraint) {
+ this.simpleInliningConstraint = constraint;
+ }
+
void setClassInlinerEligibility(ClassInlinerEligibilityInfo eligibility) {
this.classInlinerEligibility = eligibility;
}
@@ -486,7 +501,6 @@
@Override
public UpdatableMethodOptimizationInfo mutableCopy() {
- assert this != DefaultMethodOptimizationInfo.DEFAULT_INSTANCE;
return new UpdatableMethodOptimizationInfo(this);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java
index bb270b9..e6b5afe 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java
@@ -51,7 +51,7 @@
// program.
return Reason.SIMPLE;
}
- if (callSiteInformation.hasSingleCallSite(target)) {
+ if (isSingleCallerInliningTarget(target)) {
return Reason.SINGLE_CALLER;
}
if (isDoubleInliningTarget(target)) {
@@ -60,6 +60,20 @@
return Reason.SIMPLE;
}
+ private boolean isSingleCallerInliningTarget(ProgramMethod method) {
+ if (!callSiteInformation.hasSingleCallSite(method)) {
+ return false;
+ }
+ if (appView.appInfo().isNeverInlineDueToSingleCallerMethod(method)) {
+ return false;
+ }
+ if (appView.options().testing.validInliningReasons != null
+ && !appView.options().testing.validInliningReasons.contains(Reason.SINGLE_CALLER)) {
+ return false;
+ }
+ return true;
+ }
+
private boolean isDoubleInliningTarget(ProgramMethod candidate) {
// 10 is found from measuring.
if (callSiteInformation.hasDoubleCallSite(candidate)
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
index 189a6b0..f9d2d18 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
@@ -6,7 +6,6 @@
import static com.android.tools.r8.kotlin.KotlinClassMetadataReader.hasKotlinClassMetadataAnnotation;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO;
-import static com.android.tools.r8.utils.FunctionUtils.forEachApply;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
@@ -17,20 +16,30 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.EnclosingMethodAttribute;
+import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.analysis.EnqueuerAnalysis;
import com.android.tools.r8.shaking.Enqueuer;
+import com.android.tools.r8.shaking.Enqueuer.EnqueuerDefinitionSupplier;
import com.google.common.collect.Sets;
import java.util.Set;
public class KotlinMetadataEnqueuerExtension extends EnqueuerAnalysis {
private final AppView<?> appView;
- private final DexDefinitionSupplier definitionSupplier;
+ private final EnqueuerDefinitionSupplier enqueuerDefinitionSupplier;
+ private final Set<DexType> prunedTypes;
public KotlinMetadataEnqueuerExtension(
- AppView<?> appView, DexDefinitionSupplier definitionSupplier, Set<DexType> prunedTypes) {
+ AppView<?> appView,
+ EnqueuerDefinitionSupplier enqueuerDefinitionSupplier,
+ Set<DexType> prunedTypes) {
this.appView = appView;
- this.definitionSupplier = new KotlinMetadataDefinitionSupplier(definitionSupplier, prunedTypes);
+ this.enqueuerDefinitionSupplier = enqueuerDefinitionSupplier;
+ this.prunedTypes = prunedTypes;
+ }
+
+ private KotlinMetadataDefinitionSupplier definitionsForContext(ProgramDefinition context) {
+ return new KotlinMetadataDefinitionSupplier(context, enqueuerDefinitionSupplier, prunedTypes);
}
@Override
@@ -54,7 +63,7 @@
KotlinClassMetadataReader.getKotlinInfo(
appView.dexItemFactory().kotlin,
clazz,
- definitionSupplier.dexItemFactory(),
+ appView.dexItemFactory(),
appView.options().reporter,
onlyProcessLambdas,
method -> keepByteCodeFunctions.add(method.method)));
@@ -72,7 +81,8 @@
EnclosingMethodAttribute enclosingAttribute =
localOrAnonymousClass.getEnclosingMethodAttribute();
DexClass holder =
- definitionSupplier.definitionForHolder(enclosingAttribute.getEnclosingMethod());
+ definitionsForContext(localOrAnonymousClass)
+ .definitionForHolder(enclosingAttribute.getEnclosingMethod());
if (holder == null) {
continue;
}
@@ -91,11 +101,13 @@
// Trace through the modeled kotlin metadata.
enqueuer.forAllLiveClasses(
clazz -> {
- clazz.getKotlinInfo().trace(definitionSupplier);
- forEachApply(
- clazz.methods(), method -> method.getKotlinMemberInfo()::trace, definitionSupplier);
- forEachApply(
- clazz.fields(), field -> field.getKotlinMemberInfo()::trace, definitionSupplier);
+ clazz.getKotlinInfo().trace(definitionsForContext(clazz));
+ clazz.forEachProgramMember(
+ member ->
+ member
+ .getDefinition()
+ .getKotlinMemberInfo()
+ .trace(definitionsForContext(member)));
});
}
@@ -104,7 +116,7 @@
enqueuer.forAllLiveClasses(
clazz -> {
// Trace through class and member definitions
- assert !hasKotlinClassMetadataAnnotation(clazz, definitionSupplier)
+ assert !hasKotlinClassMetadataAnnotation(clazz, definitionsForContext(clazz))
|| !keepMetadata
|| !enqueuer.isPinned(clazz.type)
|| clazz.getKotlinInfo() != NO_KOTLIN_INFO;
@@ -112,14 +124,18 @@
return true;
}
- public static class KotlinMetadataDefinitionSupplier implements DexDefinitionSupplier {
+ public class KotlinMetadataDefinitionSupplier implements DexDefinitionSupplier {
- private final DexDefinitionSupplier baseSupplier;
+ private final ProgramDefinition context;
+ private final EnqueuerDefinitionSupplier enqueuerDefinitionSupplier;
private final Set<DexType> prunedTypes;
private KotlinMetadataDefinitionSupplier(
- DexDefinitionSupplier baseSupplier, Set<DexType> prunedTypes) {
- this.baseSupplier = baseSupplier;
+ ProgramDefinition context,
+ EnqueuerDefinitionSupplier enqueuerDefinitionSupplier,
+ Set<DexType> prunedTypes) {
+ this.context = context;
+ this.enqueuerDefinitionSupplier = enqueuerDefinitionSupplier;
this.prunedTypes = prunedTypes;
}
@@ -131,12 +147,12 @@
if (prunedTypes != null && prunedTypes.contains(type)) {
return null;
}
- return baseSupplier.definitionFor(type);
+ return enqueuerDefinitionSupplier.definitionFor(type, context);
}
@Override
public DexItemFactory dexItemFactory() {
- return baseSupplier.dexItemFactory();
+ return appView.dexItemFactory();
}
}
}
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
index bcbba53..191dc0a 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.graph.DexEncodedMember;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.EnclosingMethodAttribute;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ProgramPackage;
@@ -127,11 +126,10 @@
innerClassAttribute ->
registry.registerInnerClassAttribute(clazz, innerClassAttribute));
- // Trace the references from the enclosing method attribute.
- EnclosingMethodAttribute attr = clazz.getEnclosingMethodAttribute();
- if (attr != null) {
- registry.registerEnclosingMethodAttribute(attr);
- }
+ // Trace the references from the enclosing method and nest attributes.
+ registry.registerEnclosingMethodAttribute(clazz.getEnclosingMethodAttribute());
+ registry.registerNestHostAttribute(clazz.getNestHostClassAttribute());
+ registry.registerNestMemberClassAttributes(clazz.getNestMembersClassAttributes());
}
private void registerReferencesFromField(ProgramField field) {
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
index 9d5cec5..a6ffc40 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
@@ -19,6 +19,8 @@
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.MemberResolutionResult;
+import com.android.tools.r8.graph.NestHostClassAttribute;
+import com.android.tools.r8.graph.NestMemberClassAttribute;
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ResolutionResult;
@@ -26,6 +28,7 @@
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
+import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -233,6 +236,9 @@
}
public void registerEnclosingMethodAttribute(EnclosingMethodAttribute enclosingMethodAttribute) {
+ if (enclosingMethodAttribute == null) {
+ return;
+ }
// For references in enclosing method attributes we add an edge from the context to the
// referenced item even if the item would be accessible from another package, to make sure that
// we don't split such classes into different packages.
@@ -257,4 +263,26 @@
innerClassAttribute.forEachType(
type -> registerTypeAccess(type, clazz -> registerClassTypeAccess(clazz, alwaysTrue())));
}
+
+ public void registerNestHostAttribute(NestHostClassAttribute nestHostClassAttribute) {
+ if (nestHostClassAttribute == null) {
+ return;
+ }
+ // JVM require nest-members to be in the same package.
+ registerTypeAccess(
+ nestHostClassAttribute.getNestHost(),
+ clazz -> registerClassTypeAccess(clazz, alwaysTrue()));
+ }
+
+ public void registerNestMemberClassAttributes(
+ List<NestMemberClassAttribute> memberClassAttributes) {
+ if (memberClassAttributes == null) {
+ return;
+ }
+ // JVM require nest-members to be in the same package.
+ memberClassAttributes.forEach(
+ nestMember ->
+ registerTypeAccess(
+ nestMember.getNestMember(), clazz -> registerClassTypeAccess(clazz, alwaysTrue())));
+ }
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceUtils.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceUtils.java
index 0ca25c7..cd3e12d 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceUtils.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceUtils.java
@@ -30,8 +30,7 @@
public class RetraceUtils {
- private static final Set<String> UNKNOWN_SOURCEFILE_NAMES =
- Sets.newHashSet("", "SourceFile", "Unknown", "Unknown Source", "PG");
+ private static final Set<String> KEEP_SOURCEFILE_NAMES = Sets.newHashSet("Native Method");
public static String methodDescriptionFromRetraceMethod(
RetracedMethod methodReference, boolean appendHolder, boolean verbose) {
@@ -109,24 +108,17 @@
String minifiedClassName,
String sourceFile,
boolean hasRetraceResult) {
- boolean fileNameProbablyChanged =
- hasRetraceResult && !retracedClassName.startsWith(minifiedClassName);
- if (!UNKNOWN_SOURCEFILE_NAMES.contains(sourceFile) && !fileNameProbablyChanged) {
- // We have no new information, only rewrite filename if it is unknown.
- // PG-retrace will always rewrite the filename, but that seems a bit to harsh to do.
+ if (!hasRetraceResult || KEEP_SOURCEFILE_NAMES.contains(sourceFile)) {
return sourceFile;
}
String extension = Files.getFileExtension(sourceFile);
- if (extension.isEmpty()) {
+ String newFileName = getOuterClassSimpleName(retracedClassName);
+ if (newFileName.endsWith("Kt") && (extension.isEmpty() || extension.equals("kt"))) {
+ newFileName = newFileName.substring(0, newFileName.length() - 2);
+ extension = "kt";
+ } else if (extension.isEmpty()) {
extension = "java";
}
- if (!hasRetraceResult) {
- // We have no mapping but but file name is unknown, so the best we can do is take the
- // name of the obfuscated clazz.
- assert minifiedClassName.equals(retracedClassName);
- return getOuterClassSimpleName(minifiedClassName) + "." + extension;
- }
- String newFileName = getOuterClassSimpleName(retracedClassName);
return newFileName + "." + extension;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 632c74c..855e24c 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -75,8 +75,6 @@
implements InstantiatedSubTypeInfo {
/** Set of reachable proto types that will be dead code eliminated. */
private final Set<DexType> deadProtoTypes;
- /** Set of types that are mentioned in the program, but for which no definition exists. */
- private final Set<DexType> missingTypes;
/**
* Set of types that are mentioned in the program. We at least need an empty abstract classitem
* for these.
@@ -136,6 +134,11 @@
private final Set<DexMethod> forceInline;
/** All methods that *must* never be inlined due to a configuration directive (testing only). */
private final Set<DexMethod> neverInline;
+ /**
+ * All methods that *must* never be inlined as a result of having a single caller due to a
+ * configuration directive (testing only).
+ */
+ private final Set<DexMethod> neverInlineDueToSingleCaller;
/** Items for which to print inlining decisions for (testing only). */
private final Set<DexMethod> whyAreYouNotInlining;
/** All methods that may not have any parameters with a constant value removed. */
@@ -190,7 +193,7 @@
ClassToFeatureSplitMap classToFeatureSplitMap,
MainDexClasses mainDexClasses,
Set<DexType> deadProtoTypes,
- Set<DexType> missingTypes,
+ MissingClasses missingClasses,
Set<DexType> liveTypes,
Set<DexMethod> targetedMethods,
Set<DexMethod> failedResolutionTargets,
@@ -209,6 +212,7 @@
Set<DexMethod> alwaysInline,
Set<DexMethod> forceInline,
Set<DexMethod> neverInline,
+ Set<DexMethod> neverInlineDueToSingleCaller,
Set<DexMethod> whyAreYouNotInlining,
Set<DexMethod> keepConstantArguments,
Set<DexMethod> keepUnusedArguments,
@@ -226,9 +230,8 @@
Map<DexField, Int2ReferenceMap<DexField>> switchMaps,
Set<DexType> lockCandidates,
Map<DexType, Visibility> initClassReferences) {
- super(syntheticItems, classToFeatureSplitMap, mainDexClasses);
+ super(syntheticItems, classToFeatureSplitMap, mainDexClasses, missingClasses);
this.deadProtoTypes = deadProtoTypes;
- this.missingTypes = missingTypes;
this.liveTypes = liveTypes;
this.targetedMethods = targetedMethods;
this.failedResolutionTargets = failedResolutionTargets;
@@ -247,6 +250,7 @@
this.alwaysInline = alwaysInline;
this.forceInline = forceInline;
this.neverInline = neverInline;
+ this.neverInlineDueToSingleCaller = neverInlineDueToSingleCaller;
this.whyAreYouNotInlining = whyAreYouNotInlining;
this.keepConstantArguments = keepConstantArguments;
this.keepUnusedArguments = keepUnusedArguments;
@@ -273,7 +277,7 @@
previous.getClassToFeatureSplitMap(),
previous.getMainDexClasses(),
previous.deadProtoTypes,
- previous.missingTypes,
+ previous.getMissingClasses(),
CollectionUtils.mergeSets(previous.liveTypes, committedItems.getCommittedTypes()),
previous.targetedMethods,
previous.failedResolutionTargets,
@@ -292,6 +296,7 @@
previous.alwaysInline,
previous.forceInline,
previous.neverInline,
+ previous.neverInlineDueToSingleCaller,
previous.whyAreYouNotInlining,
previous.keepConstantArguments,
previous.keepUnusedArguments,
@@ -317,7 +322,7 @@
previous.getClassToFeatureSplitMap().withoutPrunedItems(prunedItems),
previous.getMainDexClasses().withoutPrunedItems(prunedItems),
previous.deadProtoTypes,
- previous.missingTypes,
+ previous.getMissingClasses(),
prunedItems.hasRemovedClasses()
? Sets.difference(previous.liveTypes, prunedItems.getRemovedClasses())
: previous.liveTypes,
@@ -338,6 +343,7 @@
previous.alwaysInline,
previous.forceInline,
previous.neverInline,
+ previous.neverInlineDueToSingleCaller,
previous.whyAreYouNotInlining,
previous.keepConstantArguments,
previous.keepUnusedArguments,
@@ -362,7 +368,7 @@
private void verify() {
assert keepInfo.verifyPinnedTypesAreLive(liveTypes);
assert objectAllocationInfoCollection.verifyAllocatedTypesAreLive(
- liveTypes, missingTypes, this);
+ liveTypes, getMissingClasses(), this);
}
private static KeepInfoCollection extendPinnedItems(
@@ -407,9 +413,9 @@
super(
previous.getSyntheticItems().commit(previous.app()),
previous.getClassToFeatureSplitMap(),
- previous.getMainDexClasses());
+ previous.getMainDexClasses(),
+ previous.getMissingClasses());
this.deadProtoTypes = previous.deadProtoTypes;
- this.missingTypes = previous.missingTypes;
this.liveTypes = previous.liveTypes;
this.targetedMethods = previous.targetedMethods;
this.failedResolutionTargets = previous.failedResolutionTargets;
@@ -428,6 +434,7 @@
this.alwaysInline = previous.alwaysInline;
this.forceInline = previous.forceInline;
this.neverInline = previous.neverInline;
+ this.neverInlineDueToSingleCaller = previous.neverInlineDueToSingleCaller;
this.whyAreYouNotInlining = previous.whyAreYouNotInlining;
this.keepConstantArguments = previous.keepConstantArguments;
this.keepUnusedArguments = previous.keepUnusedArguments;
@@ -458,7 +465,7 @@
DexClass definition = super.definitionFor(type);
assert definition != null
|| deadProtoTypes.contains(type)
- || missingTypes.contains(type)
+ || getMissingClasses().contains(type)
// TODO(b/150693139): Remove these exceptions once fixed.
|| InterfaceMethodRewriter.isCompanionClassType(type)
|| InterfaceMethodRewriter.hasDispatchClassSuffix(type)
@@ -565,6 +572,10 @@
return neverInline.contains(method);
}
+ public boolean isNeverInlineDueToSingleCallerMethod(ProgramMethod method) {
+ return neverInlineDueToSingleCaller.contains(method.getReference());
+ }
+
public boolean isWhyAreYouNotInliningMethod(DexMethod method) {
return whyAreYouNotInlining.contains(method);
}
@@ -680,10 +691,6 @@
return deadProtoTypes;
}
- public Set<DexType> getMissingTypes() {
- return missingTypes;
- }
-
public Int2ReferenceMap<DexField> getSwitchMap(DexField field) {
assert checkIfObsolete();
return switchMaps.get(field);
@@ -832,11 +839,31 @@
&& !keepInfo.getMethodInfo(method).isPinned();
}
- public boolean mayPropagateValueFor(DexReference reference) {
+ public boolean mayPropagateValueFor(DexMember<?, ?> reference) {
assert checkIfObsolete();
- return options().enableValuePropagation
- && !isPinned(reference)
- && !neverPropagateValue.contains(reference);
+ return reference.apply(this::mayPropagateValueFor, this::mayPropagateValueFor);
+ }
+
+ public boolean mayPropagateValueFor(DexField field) {
+ assert checkIfObsolete();
+ if (!options().enableValuePropagation || neverPropagateValue.contains(field)) {
+ return false;
+ }
+ if (isPinned(field) && !field.getType().isAlwaysNull(this)) {
+ return false;
+ }
+ return true;
+ }
+
+ public boolean mayPropagateValueFor(DexMethod method) {
+ assert checkIfObsolete();
+ if (!options().enableValuePropagation || neverPropagateValue.contains(method)) {
+ return false;
+ }
+ if (isPinned(method) && !method.getReturnType().isAlwaysNull(this)) {
+ return false;
+ }
+ return true;
}
private boolean isLibraryOrClasspathField(DexEncodedField field) {
@@ -965,7 +992,7 @@
getClassToFeatureSplitMap().rewrittenWithLens(lens),
getMainDexClasses().rewrittenWithLens(lens),
deadProtoTypes,
- missingTypes,
+ getMissingClasses().commitSyntheticItems(committedItems),
lens.rewriteTypes(liveTypes),
lens.rewriteMethods(targetedMethods),
lens.rewriteMethods(failedResolutionTargets),
@@ -984,6 +1011,7 @@
lens.rewriteMethods(alwaysInline),
lens.rewriteMethods(forceInline),
lens.rewriteMethods(neverInline),
+ lens.rewriteMethods(neverInlineDueToSingleCaller),
lens.rewriteMethods(whyAreYouNotInlining),
lens.rewriteMethods(keepConstantArguments),
lens.rewriteMethods(keepUnusedArguments),
diff --git a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
index 18ae137..f8302d7 100644
--- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.shaking;
import com.android.tools.r8.code.CfOrDexInstruction;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -19,11 +20,16 @@
public class DefaultEnqueuerUseRegistry extends UseRegistry {
+ protected final AppView<? extends AppInfoWithClassHierarchy> appView;
private final ProgramMethod context;
protected final Enqueuer enqueuer;
- public DefaultEnqueuerUseRegistry(AppView<?> appView, ProgramMethod context, Enqueuer enqueuer) {
+ public DefaultEnqueuerUseRegistry(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ ProgramMethod context,
+ Enqueuer enqueuer) {
super(appView.dexItemFactory());
+ this.appView = appView;
this.context = context;
this.enqueuer = enqueuer;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index e43e72a..72d5af2 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -27,7 +27,6 @@
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexClasspathClass;
import com.android.tools.r8.graph.DexDefinition;
-import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMember;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -205,7 +204,8 @@
private ProguardClassFilter dontWarnPatterns;
private final EnqueuerUseRegistryFactory useRegistryFactory;
private AnnotationRemover.Builder annotationRemoverBuilder;
- private final EnqueuerDefinitionSupplier enqueuerDefinitionSupplier;
+ private final EnqueuerDefinitionSupplier enqueuerDefinitionSupplier =
+ new EnqueuerDefinitionSupplier(this);
private final FieldAccessInfoCollectionImpl fieldAccessInfoCollection =
new FieldAccessInfoCollectionImpl();
@@ -261,14 +261,11 @@
private final Set<DexProgramClass> deadProtoTypeCandidates = Sets.newIdentityHashSet();
/** Set of missing types. */
- private final Set<DexType> missingTypes = Sets.newIdentityHashSet();
+ private final MissingClasses.Builder missingClassesBuilder;
/** Set of proto types that were found to be dead during the first round of tree shaking. */
private Set<DexType> initialDeadProtoTypes = Sets.newIdentityHashSet();
- /** Set of types that were found to be missing during the first round of tree shaking. */
- private Set<DexType> initialMissingTypes;
-
/** Set of types that was pruned during the first round of tree shaking. */
private Set<DexType> initialPrunedTypes;
@@ -382,10 +379,11 @@
this.subtypingInfo = subtypingInfo;
this.forceProguardCompatibility = options.forceProguardCompatibility;
this.graphReporter = new GraphReporter(appView, keptGraphConsumer);
+ this.missingClassesBuilder = appView.appInfo().getMissingClasses().builder();
this.mode = mode;
this.options = options;
this.useRegistryFactory = createUseRegistryFactory();
- this.workList = EnqueuerWorklist.createWorklist(appView);
+ this.workList = EnqueuerWorklist.createWorklist();
if (mode.isInitialOrFinalTreeShaking()) {
if (options.protoShrinking().enableGeneratedMessageLiteShrinking) {
@@ -416,8 +414,6 @@
} else {
desugaredLibraryWrapperAnalysis = null;
}
-
- enqueuerDefinitionSupplier = new EnqueuerDefinitionSupplier(appView, this);
}
private AppInfoWithClassHierarchy appInfo() {
@@ -478,11 +474,6 @@
this.initialDeadProtoTypes = initialDeadProtoTypes;
}
- public void setInitialMissingTypes(Set<DexType> initialMissingTypes) {
- assert mode.isFinalTreeShaking();
- this.initialMissingTypes = initialMissingTypes;
- }
-
public void setInitialPrunedTypes(Set<DexType> initialPrunedTypes) {
assert mode.isFinalTreeShaking();
this.initialPrunedTypes = initialPrunedTypes;
@@ -497,22 +488,21 @@
deadProtoTypeCandidates.add(clazz);
}
- private boolean isProgramClass(DexType type) {
- return getProgramClassOrNull(type) != null;
- }
-
- private void recordReference(DexReference r) {
- if (r.isDexType()) {
- recordTypeReference(r.asDexType());
- } else if (r.isDexField()) {
- recordFieldReference(r.asDexField());
- } else {
- assert r.isDexMethod();
- recordMethodReference(r.asDexMethod());
+ private void recordCompilerSynthesizedTypeReference(DexType type) {
+ DexClass clazz = appInfo().definitionFor(type);
+ if (clazz == null) {
+ ignoreMissingClass(type);
+ } else if (clazz.isNotProgramClass()) {
+ addLiveNonProgramType(clazz);
}
}
- private void recordTypeReference(DexType type) {
+ private void recordTypeReference(DexType type, ProgramDefinition context) {
+ recordTypeReference(type, context, this::reportMissingClass);
+ }
+
+ private void recordTypeReference(
+ DexType type, ProgramDefinition context, Consumer<DexType> missingClassConsumer) {
if (type == null) {
return;
}
@@ -523,47 +513,44 @@
return;
}
// Lookup the definition, ignoring the result. This populates the missing and referenced sets.
- definitionFor(type);
+ definitionFor(type, context, missingClassConsumer);
}
- private void recordMethodReference(DexMethod method) {
- recordTypeReference(method.holder);
- recordTypeReference(method.proto.returnType);
+ private void recordMethodReference(DexMethod method, ProgramDefinition context) {
+ recordTypeReference(method.holder, context);
+ recordTypeReference(method.proto.returnType, context);
for (DexType type : method.proto.parameters.values) {
- recordTypeReference(type);
+ recordTypeReference(type, context);
}
}
- private void recordFieldReference(DexField field) {
- recordTypeReference(field.holder);
- recordTypeReference(field.type);
+ private void recordFieldReference(DexField field, ProgramDefinition context) {
+ recordTypeReference(field.holder, context);
+ recordTypeReference(field.type, context);
}
- public DexEncodedMethod definitionFor(DexMethod method) {
- DexClass clazz = definitionFor(method.holder);
+ public DexEncodedMethod definitionFor(DexMethod method, ProgramDefinition context) {
+ DexClass clazz = definitionFor(method.holder, context);
if (clazz == null) {
return null;
}
return clazz.lookupMethod(method);
}
- private DexClass definitionFor(DexType type) {
- return internalDefinitionFor(type, false);
+ public DexClass definitionFor(DexType type, ProgramDefinition context) {
+ return definitionFor(type, context, this::reportMissingClass);
}
- private DexClass definitionForFromReflectiveAccess(DexType type) {
- return internalDefinitionFor(type, true);
+ private DexClass definitionFor(
+ DexType type, ProgramDefinition context, Consumer<DexType> missingClassConsumer) {
+ return internalDefinitionFor(type, context, missingClassConsumer);
}
- private DexClass internalDefinitionFor(DexType type, boolean fromReflectiveAccess) {
- DexClass clazz =
- fromReflectiveAccess
- ? appView.appInfo().definitionForWithoutExistenceAssert(type)
- : appView.appInfo().definitionFor(type);
+ private DexClass internalDefinitionFor(
+ DexType type, ProgramDefinition context, Consumer<DexType> missingClassConsumer) {
+ DexClass clazz = appInfo().definitionFor(type);
if (clazz == null) {
- if (!fromReflectiveAccess) {
- reportMissingClass(type);
- }
+ missingClassConsumer.accept(type);
return null;
}
if (clazz.isNotProgramClass()) {
@@ -630,14 +617,20 @@
worklist.addLast(definition);
}
- private DexProgramClass getProgramClassOrNull(DexType type) {
- DexClass clazz = definitionFor(type);
+ private DexProgramClass getProgramClassOrNull(DexType type, ProgramDefinition context) {
+ DexClass clazz = definitionFor(type, context);
return clazz != null && clazz.isProgramClass() ? clazz.asProgramClass() : null;
}
- private DexProgramClass getProgramClassOrNullFromReflectiveAccess(DexType type) {
+ private DexProgramClass getProgramHolderOrNull(
+ DexMember<?, ?> member, ProgramDefinition context) {
+ return getProgramClassOrNull(member.getHolderType(), context);
+ }
+
+ private DexProgramClass getProgramClassOrNullFromReflectiveAccess(
+ DexType type, ProgramDefinition context) {
// To avoid that we report reflectively accessed types as missing.
- DexClass clazz = definitionForFromReflectiveAccess(type);
+ DexClass clazz = definitionFor(type, context, this::ignoreMissingClass);
return clazz != null && clazz.isProgramClass() ? clazz.asProgramClass() : null;
}
@@ -652,7 +645,7 @@
private void warnIfClassExtendsInterfaceOrImplementsClass(DexProgramClass clazz) {
if (clazz.superType != null) {
- DexClass superClass = definitionFor(clazz.superType);
+ DexClass superClass = definitionFor(clazz.superType, clazz);
if (superClass != null && superClass.isInterface()) {
options.reporter.warning(
new StringDiagnostic(
@@ -664,7 +657,7 @@
}
}
for (DexType iface : clazz.interfaces.values) {
- DexClass ifaceClass = definitionFor(iface);
+ DexClass ifaceClass = definitionFor(iface, clazz);
if (ifaceClass != null && !ifaceClass.isInterface()) {
options.reporter.warning(
new StringDiagnostic(
@@ -677,10 +670,6 @@
}
}
- private void enqueueRootItems(Map<DexReference, Set<ProguardKeepRuleBase>> items) {
- items.entrySet().forEach(this::enqueueRootItem);
- }
-
private void enqueueRootItems(ItemsWithRules items) {
items.forEachField(this::enqueueRootField);
items.forEachMethod(this::enqueueRootMethod);
@@ -705,10 +694,14 @@
private void enqueueRootClass(DexType type, Set<ProguardKeepRuleBase> rules) {
DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(type));
if (clazz != null) {
- enqueueRootClass(clazz, rules, null);
+ enqueueRootClass(clazz, rules);
}
}
+ private void enqueueRootClass(DexProgramClass clazz, Set<ProguardKeepRuleBase> rules) {
+ enqueueRootClass(clazz, rules, null);
+ }
+
private void enqueueRootClass(
DexProgramClass clazz, Set<ProguardKeepRuleBase> rules, DexDefinition precondition) {
keepClassWithRules(clazz, rules);
@@ -732,7 +725,7 @@
graphReporter.reportCompatKeepDefaultInitializer(defaultInitializer));
}
if (clazz.isExternalizable(appView)) {
- enqueueMarkMethodLiveAction(defaultInitializer, witness);
+ enqueueMarkMethodLiveAction(defaultInitializer, defaultInitializer, witness);
}
}
}
@@ -740,15 +733,20 @@
// TODO(b/123923324): Verify that root items are present.
private void enqueueRootField(DexField reference, Set<ProguardKeepRuleBase> rules) {
- DexProgramClass holder = getProgramClassOrNull(reference.holder);
+ DexProgramClass holder =
+ asProgramClassOrNull(appInfo().definitionFor(reference.getHolderType()));
if (holder != null) {
ProgramField field = holder.lookupProgramField(reference);
if (field != null) {
- enqueueRootField(field, rules, null);
+ enqueueRootField(field, rules);
}
}
}
+ private void enqueueRootField(ProgramField field, Set<ProguardKeepRuleBase> rules) {
+ enqueueRootField(field, rules, null);
+ }
+
private void enqueueRootField(
ProgramField field, Set<ProguardKeepRuleBase> rules, DexDefinition precondition) {
keepFieldWithRules(field, rules);
@@ -758,7 +756,8 @@
// TODO(b/123923324): Verify that root items are present.
private void enqueueRootMethod(DexMethod reference, Set<ProguardKeepRuleBase> rules) {
- DexProgramClass holder = getProgramClassOrNull(reference.holder);
+ DexProgramClass holder =
+ asProgramClassOrNull(appInfo().definitionFor(reference.getHolderType()));
if (holder != null) {
ProgramMethod method = holder.lookupProgramMethod(reference);
if (method != null) {
@@ -767,6 +766,10 @@
}
}
+ private void enqueueRootMethod(ProgramMethod method, Set<ProguardKeepRuleBase> rules) {
+ enqueueRootMethod(method, rules, null);
+ }
+
private void enqueueRootMethod(
ProgramMethod method, Set<ProguardKeepRuleBase> rules, DexDefinition precondition) {
keepMethodWithRules(method, rules);
@@ -774,29 +777,18 @@
method, graphReporter.reportKeepMethod(precondition, rules, method.getDefinition()));
}
- private void enqueueRootItem(DexDefinition item, Set<ProguardKeepRuleBase> rules) {
+ private void enqueueRootItem(ProgramDefinition item, Set<ProguardKeepRuleBase> rules) {
internalEnqueueRootItem(item, rules, null);
}
private void internalEnqueueRootItem(
- DexDefinition item, Set<ProguardKeepRuleBase> rules, DexDefinition precondition) {
- if (item.isDexClass()) {
- DexProgramClass clazz = item.asDexClass().asProgramClass();
- if (clazz != null) {
- enqueueRootClass(clazz, rules, precondition);
- }
- } else if (item.isDexEncodedField()) {
- DexEncodedField field = item.asDexEncodedField();
- DexProgramClass holder = getProgramClassOrNull(field.getHolderType());
- if (holder != null) {
- enqueueRootField(new ProgramField(holder, field), rules, precondition);
- }
- } else if (item.isDexEncodedMethod()) {
- DexEncodedMethod method = item.asDexEncodedMethod();
- DexProgramClass holder = getProgramClassOrNull(method.getHolderType());
- if (holder != null) {
- enqueueRootMethod(new ProgramMethod(holder, method), rules, precondition);
- }
+ ProgramDefinition item, Set<ProguardKeepRuleBase> rules, DexDefinition precondition) {
+ if (item.isProgramClass()) {
+ enqueueRootClass(item.asProgramClass(), rules, precondition);
+ } else if (item.isProgramField()) {
+ enqueueRootField(item.asProgramField(), rules, precondition);
+ } else if (item.isProgramMethod()) {
+ enqueueRootMethod(item.asProgramMethod(), rules, precondition);
} else {
throw new IllegalArgumentException(item.toString());
}
@@ -808,22 +800,23 @@
// Climb up the class hierarchy. Break out if the definition is not found, or hit the library
// classes which are kept by definition, or encounter the first non-serializable class.
while (clazz.isSerializable(appView)) {
- DexProgramClass superClass = getProgramClassOrNull(clazz.superType);
+ DexProgramClass superClass = getProgramClassOrNull(clazz.superType, clazz);
if (superClass == null) {
return;
}
clazz = superClass;
}
if (clazz.hasDefaultInitializer()) {
- enqueueMarkMethodLiveAction(clazz.getProgramDefaultInitializer(), reason);
+ enqueueMarkMethodLiveAction(clazz.getProgramDefaultInitializer(), clazz, reason);
}
}
// Utility to avoid adding to the worklist if already live.
- private boolean enqueueMarkMethodLiveAction(ProgramMethod method, KeepReason reason) {
+ private boolean enqueueMarkMethodLiveAction(
+ ProgramMethod method, ProgramDefinition context, KeepReason reason) {
if (liveMethods.add(method, reason)) {
assert !method.getDefinition().getOptimizationInfo().forceInline();
- workList.enqueueMarkMethodLiveAction(method, reason);
+ workList.enqueueMarkMethodLiveAction(method, context);
return true;
}
return false;
@@ -847,7 +840,7 @@
BiPredicate<DexMethod, ProgramMethod> registration, DexMethod method, ProgramMethod context) {
DexType baseHolder = method.holder.toBaseType(appView.dexItemFactory());
if (baseHolder.isClassType()) {
- markTypeAsLive(baseHolder, clazz -> graphReporter.reportClassReferencedFrom(clazz, context));
+ markTypeAsLive(baseHolder, context);
return registration.test(method, context);
}
return false;
@@ -879,7 +872,7 @@
DexField field, ProgramMethod context, boolean isRead, boolean isReflective) {
FieldAccessInfoImpl info = fieldAccessInfoCollection.get(field);
if (info == null) {
- DexEncodedField encodedField = resolveField(field).getResolvedField();
+ DexEncodedField encodedField = resolveField(field, context).getResolvedField();
// If the field does not exist, then record this in the mapping, such that we don't have to
// resolve the field the next time.
@@ -913,7 +906,7 @@
void traceCallSite(DexCallSite callSite, ProgramMethod context) {
DexProgramClass bootstrapClass =
- getProgramClassOrNull(callSite.bootstrapMethod.asMethod().holder);
+ getProgramHolderOrNull(callSite.bootstrapMethod.asMethod(), context);
if (bootstrapClass != null) {
bootstrapMethods.add(callSite.bootstrapMethod.asMethod());
}
@@ -996,7 +989,7 @@
// potential locks on T[].class and S[].class exists.
DexType baseType = type.toBaseType(appView.dexItemFactory());
if (baseType.isClassType()) {
- DexProgramClass baseClass = getProgramClassOrNull(baseType);
+ DexProgramClass baseClass = getProgramClassOrNull(baseType, currentMethod);
if (baseClass != null && isConstClassMaybeUsedAsLock(currentMethod, iterator)) {
lockCandidates.add(baseType);
}
@@ -1046,7 +1039,7 @@
}
DexType baseType = type.toBaseType(appView.dexItemFactory());
if (baseType.isClassType()) {
- DexProgramClass baseClass = getProgramClassOrNull(baseType);
+ DexProgramClass baseClass = getProgramClassOrNull(baseType, currentMethod);
if (baseClass != null) {
// Don't require any constructor, see b/112386012.
markClassAsInstantiatedWithCompatRule(
@@ -1060,7 +1053,7 @@
Visibility oldMinimumRequiredVisibility = initClassReferences.get(type);
if (oldMinimumRequiredVisibility == null) {
- DexProgramClass clazz = getProgramClassOrNull(type);
+ DexProgramClass clazz = getProgramClassOrNull(type, currentMethod);
if (clazz == null) {
assert false;
return;
@@ -1069,7 +1062,7 @@
initClassReferences.put(
type, computeMinimumRequiredVisibilityForInitClassField(type, currentMethod.getHolder()));
- markTypeAsLive(type, classReferencedFromReporter(currentMethod));
+ markTypeAsLive(type, currentMethod);
markDirectAndIndirectClassInitializersAsLive(clazz);
return;
}
@@ -1123,7 +1116,7 @@
// as instantiated.
if (methodHandle.isMethodHandle() && use != MethodHandleUse.ARGUMENT_TO_LAMBDA_METAFACTORY) {
DexType type = methodHandle.asMethod().holder;
- DexProgramClass clazz = getProgramClassOrNull(type);
+ DexProgramClass clazz = getProgramClassOrNull(type, currentMethod);
if (clazz != null) {
KeepReason reason = KeepReason.methodHandleReferencedIn(currentMethod);
if (clazz.isAnnotation()) {
@@ -1139,7 +1132,7 @@
}
void traceTypeReference(DexType type, ProgramMethod currentMethod) {
- markTypeAsLive(type, classReferencedFromReporter(currentMethod));
+ markTypeAsLive(type, currentMethod);
}
void traceInstanceOf(DexType type, ProgramMethod currentMethod) {
@@ -1169,7 +1162,7 @@
/** Returns true if a deferred action was registered. */
private boolean registerDeferredActionForDeadProtoBuilder(
DexType type, ProgramMethod currentMethod, Action action) {
- DexProgramClass clazz = getProgramClassOrNull(type);
+ DexProgramClass clazz = getProgramClassOrNull(type, currentMethod);
if (clazz != null) {
return appView.withGeneratedMessageLiteBuilderShrinker(
shrinker ->
@@ -1189,7 +1182,6 @@
if (registerBackportInvoke(invokedMethod, context)) {
return;
}
-
if (!registerMethodWithTargetAndContext(
methodAccessInfoCollection::registerInvokeDirectInContext, invokedMethod, context)) {
return;
@@ -1197,7 +1189,7 @@
if (Log.ENABLED) {
Log.verbose(getClass(), "Register invokeDirect `%s`.", invokedMethod);
}
- handleInvokeOfDirectTarget(invokedMethod, reason);
+ handleInvokeOfDirectTarget(invokedMethod, context, reason);
invokeAnalyses.forEach(analysis -> analysis.traceInvokeDirect(invokedMethod, context));
}
@@ -1274,7 +1266,7 @@
if (Log.ENABLED) {
Log.verbose(getClass(), "Register invokeStatic `%s`.", invokedMethod);
}
- handleInvokeOfStaticTarget(invokedMethod, reason);
+ handleInvokeOfStaticTarget(invokedMethod, context, reason);
invokeAnalyses.forEach(analysis -> analysis.traceInvokeStatic(invokedMethod, context));
}
@@ -1356,7 +1348,7 @@
ProgramMethod context,
InstantiationReason instantiationReason,
KeepReason keepReason) {
- DexProgramClass clazz = getProgramClassOrNull(type);
+ DexProgramClass clazz = getProgramClassOrNull(type, context);
if (clazz != null) {
if (clazz.isAnnotation() || clazz.isInterface()) {
markTypeAsLive(clazz, graphReporter.registerClass(clazz, keepReason));
@@ -1383,7 +1375,7 @@
// Must mark the field as targeted even if it does not exist.
markFieldAsTargeted(fieldReference, currentMethod);
- FieldResolutionResult resolutionResult = resolveField(fieldReference);
+ FieldResolutionResult resolutionResult = resolveField(fieldReference, currentMethod);
if (resolutionResult.isFailedOrUnknownResolution()) {
noClassMerging.add(fieldReference.getHolderType());
return;
@@ -1408,13 +1400,12 @@
// the field as live, if the holder is an interface.
if (appView.options().enableUnusedInterfaceRemoval) {
if (field.getReference() != fieldReference) {
- markTypeAsLive(
- field.getHolder(),
- graphReporter.reportClassReferencedFrom(field.getHolder(), currentMethod));
+ markTypeAsLive(field.getHolder(), currentMethod);
}
}
- workList.enqueueMarkReachableFieldAction(field, KeepReason.fieldReferencedIn(currentMethod));
+ workList.enqueueMarkReachableFieldAction(
+ field, currentMethod, KeepReason.fieldReferencedIn(currentMethod));
}
void traceInstanceFieldWrite(DexField field, ProgramMethod currentMethod) {
@@ -1434,7 +1425,7 @@
// Must mark the field as targeted even if it does not exist.
markFieldAsTargeted(fieldReference, currentMethod);
- FieldResolutionResult resolutionResult = resolveField(fieldReference);
+ FieldResolutionResult resolutionResult = resolveField(fieldReference, currentMethod);
if (resolutionResult.isFailedOrUnknownResolution()) {
noClassMerging.add(fieldReference.getHolderType());
return;
@@ -1459,14 +1450,12 @@
// the field as live, if the holder is an interface.
if (appView.options().enableUnusedInterfaceRemoval) {
if (field.getReference() != fieldReference) {
- markTypeAsLive(
- field.getHolder(),
- graphReporter.reportClassReferencedFrom(field.getHolder(), currentMethod));
+ markTypeAsLive(field.getHolder(), currentMethod);
}
}
KeepReason reason = KeepReason.fieldReferencedIn(currentMethod);
- workList.enqueueMarkReachableFieldAction(field, reason);
+ workList.enqueueMarkReachableFieldAction(field, currentMethod, reason);
}
void traceStaticFieldRead(DexField field, ProgramMethod currentMethod) {
@@ -1483,7 +1472,7 @@
return;
}
- FieldResolutionResult resolutionResult = resolveField(fieldReference);
+ FieldResolutionResult resolutionResult = resolveField(fieldReference, currentMethod);
if (resolutionResult.isFailedOrUnknownResolution()) {
// Must mark the field as targeted even if it does not exist.
markFieldAsTargeted(fieldReference, currentMethod);
@@ -1527,7 +1516,7 @@
markFieldAsTargeted(fieldReference, currentMethod);
}
- markStaticFieldAsLive(field, KeepReason.fieldReferencedIn(currentMethod));
+ markStaticFieldAsLive(field, currentMethod);
}
void traceStaticFieldWrite(DexField field, ProgramMethod currentMethod) {
@@ -1544,7 +1533,7 @@
return;
}
- FieldResolutionResult resolutionResult = resolveField(fieldReference);
+ FieldResolutionResult resolutionResult = resolveField(fieldReference, currentMethod);
if (resolutionResult.isFailedOrUnknownResolution()) {
// Must mark the field as targeted even if it does not exist.
markFieldAsTargeted(fieldReference, currentMethod);
@@ -1586,12 +1575,7 @@
markFieldAsTargeted(fieldReference, currentMethod);
}
- markStaticFieldAsLive(field, KeepReason.fieldReferencedIn(currentMethod));
- }
-
- private Function<DexProgramClass, KeepReasonWitness> classReferencedFromReporter(
- ProgramMethod currentMethod) {
- return clazz -> graphReporter.reportClassReferencedFrom(clazz, currentMethod);
+ markStaticFieldAsLive(field, currentMethod);
}
private DexMethod getInvokeSuperTarget(DexMethod method, ProgramMethod currentMethod) {
@@ -1624,98 +1608,96 @@
return true;
}
- private void markTypeAsLive(DexType type, KeepReason reason) {
+ private void markTypeAsLive(DexType type, ProgramDefinition context) {
if (type.isArrayType()) {
- markTypeAsLive(type.toBaseType(appView.dexItemFactory()), reason);
+ markTypeAsLive(type.toBaseType(appView.dexItemFactory()), context);
return;
}
if (!type.isClassType()) {
// Ignore primitive types.
return;
}
- DexProgramClass clazz = getProgramClassOrNull(type);
+ DexProgramClass clazz = getProgramClassOrNull(type, context);
+ if (clazz == null) {
+ return;
+ }
+ markTypeAsLive(clazz, context);
+ }
+
+ private void markTypeAsLive(DexType type, ProgramDefinition context, KeepReason reason) {
+ if (type.isArrayType()) {
+ markTypeAsLive(type.toBaseType(appView.dexItemFactory()), context, reason);
+ return;
+ }
+ if (!type.isClassType()) {
+ // Ignore primitive types.
+ return;
+ }
+ DexProgramClass clazz = getProgramClassOrNull(type, context);
if (clazz == null) {
return;
}
markTypeAsLive(clazz, reason);
}
+ private void markTypeAsLive(DexProgramClass clazz, ProgramDefinition context) {
+ markTypeAsLive(clazz, graphReporter.reportClassReferencedFrom(clazz, context));
+ }
+
private void markTypeAsLive(DexProgramClass clazz, KeepReason reason) {
assert clazz != null;
- markTypeAsLive(clazz, graphReporter.registerClass(clazz, reason));
- }
-
- private void markTypeAsLive(DexType type, Function<DexProgramClass, KeepReasonWitness> reason) {
- if (type.isArrayType()) {
- markTypeAsLive(type.toBaseType(appView.dexItemFactory()), reason);
- return;
- }
- if (!type.isClassType()) {
- // Ignore primitive types.
- return;
- }
- DexProgramClass holder = getProgramClassOrNull(type);
- if (holder == null) {
- return;
- }
- markTypeAsLive(
- holder,
- scopedMethodsForLiveTypes.computeIfAbsent(type, ignore -> new ScopedDexMethodSet()),
- reason.apply(holder));
- }
-
- void markTypeAsLive(DexProgramClass clazz, KeepReasonWitness witness) {
markTypeAsLive(
clazz,
- scopedMethodsForLiveTypes.computeIfAbsent(clazz.type, ignore -> new ScopedDexMethodSet()),
- witness);
+ scopedMethodsForLiveTypes.computeIfAbsent(
+ clazz.getType(), ignore -> new ScopedDexMethodSet()),
+ graphReporter.registerClass(clazz, reason));
}
private void markTypeAsLive(
- DexProgramClass holder, ScopedDexMethodSet seen, KeepReasonWitness witness) {
- if (!liveTypes.add(holder, witness)) {
+ DexProgramClass clazz, ScopedDexMethodSet seen, KeepReasonWitness witness) {
+ if (!liveTypes.add(clazz, witness)) {
return;
}
// Mark types in inner-class attributes referenced.
- for (InnerClassAttribute innerClassAttribute : holder.getInnerClasses()) {
- recordTypeReference(innerClassAttribute.getInner());
- recordTypeReference(innerClassAttribute.getOuter());
+ for (InnerClassAttribute innerClassAttribute : clazz.getInnerClasses()) {
+ recordTypeReference(innerClassAttribute.getInner(), clazz, this::ignoreMissingClass);
+ recordTypeReference(innerClassAttribute.getOuter(), clazz, this::ignoreMissingClass);
}
- EnclosingMethodAttribute enclosingMethodAttribute = holder.getEnclosingMethodAttribute();
+ EnclosingMethodAttribute enclosingMethodAttribute = clazz.getEnclosingMethodAttribute();
if (enclosingMethodAttribute != null) {
DexMethod enclosingMethod = enclosingMethodAttribute.getEnclosingMethod();
if (enclosingMethod != null) {
- recordMethodReference(enclosingMethod);
+ recordMethodReference(enclosingMethod, clazz);
} else {
- recordTypeReference(enclosingMethodAttribute.getEnclosingClass());
+ recordTypeReference(enclosingMethodAttribute.getEnclosingClass(), clazz);
}
}
if (Log.ENABLED) {
- Log.verbose(getClass(), "Type `%s` has become live.", holder.type);
+ Log.verbose(getClass(), "Type `%s` has become live.", clazz.type);
}
- KeepReason reason = KeepReason.reachableFromLiveType(holder.type);
+ KeepReason reason = KeepReason.reachableFromLiveType(clazz.type);
- for (DexType iface : holder.interfaces.values) {
- markInterfaceTypeAsLiveViaInheritanceClause(iface, holder);
+ for (DexType iface : clazz.interfaces.values) {
+ markInterfaceTypeAsLiveViaInheritanceClause(iface, clazz);
}
- if (holder.superType != null) {
+ if (clazz.superType != null) {
ScopedDexMethodSet seenForSuper =
scopedMethodsForLiveTypes.computeIfAbsent(
- holder.superType, ignore -> new ScopedDexMethodSet());
+ clazz.superType, ignore -> new ScopedDexMethodSet());
seen.setParent(seenForSuper);
- markTypeAsLive(holder.superType, reason);
+ markTypeAsLive(clazz.superType, clazz);
}
// Warn if the class extends an interface or implements a class
- warnIfClassExtendsInterfaceOrImplementsClass(holder);
+ warnIfClassExtendsInterfaceOrImplementsClass(clazz);
// If this is an interface that has just become live, then report previously seen but unreported
// implemented-by edges.
- transitionUnusedInterfaceToLive(holder);
+ transitionUnusedInterfaceToLive(clazz);
// We cannot remove virtual methods defined earlier in the type hierarchy if it is widening
// access and is defined in an interface:
@@ -1732,30 +1714,30 @@
// error because their exists an existing implementation (here it is Object.clone()). This is
// only a problem in the DEX VM. We have to make this check no matter the output because
// CF libraries can be used by Android apps. See b/136698023 for more information.
- ensureMethodsContinueToWidenAccess(holder, seen, reason);
+ ensureMethodsContinueToWidenAccess(clazz, seen, reason);
- if (holder.isSerializable(appView)) {
- enqueueFirstNonSerializableClassInitializer(holder, reason);
+ if (clazz.isSerializable(appView)) {
+ enqueueFirstNonSerializableClassInitializer(clazz, reason);
}
- processAnnotations(holder, holder);
+ processAnnotations(clazz, clazz);
// If this type has deferred annotations, we have to process those now, too.
- if (holder.isAnnotation()) {
- Set<DexAnnotation> annotations = deferredAnnotations.remove(holder.type);
+ if (clazz.isAnnotation()) {
+ Set<DexAnnotation> annotations = deferredAnnotations.remove(clazz.type);
if (annotations != null && !annotations.isEmpty()) {
- assert annotations.stream().allMatch(a -> a.annotation.type == holder.type);
- annotations.forEach(annotation -> processAnnotation(holder, holder, annotation));
+ assert annotations.stream().allMatch(a -> a.annotation.type == clazz.type);
+ annotations.forEach(annotation -> processAnnotation(clazz, clazz, annotation));
}
}
rootSet.forEachDependentInstanceConstructor(
- holder, appView, this::enqueueHolderWithDependentInstanceConstructor);
- rootSet.forEachDependentStaticMember(holder, appView, this::enqueueDependentMember);
+ clazz, appView, this::enqueueHolderWithDependentInstanceConstructor);
+ rootSet.forEachDependentStaticMember(clazz, appView, this::enqueueDependentMember);
compatEnqueueHolderIfDependentNonStaticMember(
- holder, rootSet.getDependentKeepClassCompatRule(holder.getType()));
+ clazz, rootSet.getDependentKeepClassCompatRule(clazz.getType()));
- analyses.forEach(analysis -> analysis.processNewlyLiveClass(holder, workList));
+ analyses.forEach(analysis -> analysis.processNewlyLiveClass(clazz, workList));
}
private void ensureMethodsContinueToWidenAccess(DexClass clazz) {
@@ -1777,7 +1759,7 @@
private void markInterfaceTypeAsLiveViaInheritanceClause(
DexType type, DexProgramClass implementer) {
- DexProgramClass clazz = getProgramClassOrNull(type);
+ DexProgramClass clazz = getProgramClassOrNull(type, implementer);
if (clazz == null) {
return;
}
@@ -1785,7 +1767,7 @@
if (!appView.options().enableUnusedInterfaceRemoval
|| rootSet.noUnusedInterfaceRemoval.contains(type)
|| mode.isTracingMainDex()) {
- markTypeAsLive(clazz, graphReporter.reportClassReferencedFrom(clazz, implementer));
+ markTypeAsLive(clazz, implementer);
} else {
if (liveTypes.contains(clazz)) {
// The interface is already live, so make sure to report this implements-edge.
@@ -1807,7 +1789,7 @@
unusedInterfaceTypes.computeIfAbsent(current, ignore -> Sets.newIdentityHashSet());
if (implementors.add(implementer)) {
for (DexType iface : current.interfaces.values) {
- DexProgramClass definition = getProgramClassOrNull(iface);
+ DexProgramClass definition = getProgramClassOrNull(iface, current);
if (definition != null) {
worklist.addIfNotSeen(definition);
}
@@ -1820,7 +1802,7 @@
private void enqueueDependentMember(
DexDefinition precondition,
- DexEncodedMember<?, ?> consequent,
+ ProgramMember<?, ?> consequent,
Set<ProguardKeepRuleBase> reasons) {
internalEnqueueRootItem(consequent, reasons, precondition);
}
@@ -1831,50 +1813,49 @@
enqueueKeepRuleInstantiatedType(holder, reasons, instanceInitializer.getDefinition());
}
- private void processAnnotations(DexProgramClass holder, DexDefinition annotatedItem) {
- processAnnotations(holder, annotatedItem, annotatedItem.annotations());
+ private void processAnnotations(DexProgramClass holder, ProgramDefinition annotatedItem) {
+ processAnnotations(holder, annotatedItem, annotatedItem.getDefinition().annotations());
}
private void processAnnotations(
- DexProgramClass holder, DexDefinition annotatedItem, DexAnnotationSet annotations) {
+ DexProgramClass holder, ProgramDefinition annotatedItem, DexAnnotationSet annotations) {
processAnnotations(holder, annotatedItem, annotations.annotations);
}
private void processAnnotations(
- DexProgramClass holder, DexDefinition annotatedItem, DexAnnotation[] annotations) {
+ DexProgramClass holder, ProgramDefinition annotatedItem, DexAnnotation[] annotations) {
for (DexAnnotation annotation : annotations) {
processAnnotation(holder, annotatedItem, annotation);
}
}
private void processAnnotation(
- DexProgramClass holder, DexDefinition annotatedItem, DexAnnotation annotation) {
- assert annotatedItem == holder
- || annotatedItem.asDexEncodedMember().getReference().holder == holder.type;
+ DexProgramClass holder, ProgramDefinition annotatedItem, DexAnnotation annotation) {
+ assert annotatedItem == holder || annotatedItem.asProgramMember().getHolder() == holder;
assert !holder.isDexClass() || holder.asDexClass().isProgramClass();
DexType type = annotation.annotation.type;
- recordTypeReference(type);
+ recordTypeReference(type, holder);
DexClass clazz = appView.definitionFor(type);
boolean annotationTypeIsLibraryClass = clazz == null || clazz.isNotProgramClass();
boolean isLive = annotationTypeIsLibraryClass || liveTypes.contains(clazz.asProgramClass());
- if (!shouldKeepAnnotation(appView, annotatedItem, annotation, isLive)) {
+ if (!shouldKeepAnnotation(appView, annotatedItem.getDefinition(), annotation, isLive)) {
// Remember this annotation for later.
if (!annotationTypeIsLibraryClass) {
deferredAnnotations.computeIfAbsent(type, ignore -> new HashSet<>()).add(annotation);
}
return;
}
- KeepReason reason = KeepReason.annotatedOn(annotatedItem);
+ KeepReason reason = KeepReason.annotatedOn(annotatedItem.getDefinition());
graphReporter.registerAnnotation(annotation, reason);
AnnotationReferenceMarker referenceMarker =
- new AnnotationReferenceMarker(annotation.annotation.type, appView.dexItemFactory(), reason);
+ new AnnotationReferenceMarker(
+ annotation.annotation.type, holder, appView.dexItemFactory(), reason);
annotation.annotation.collectIndexedItems(referenceMarker);
}
- private FieldResolutionResult resolveField(DexField field) {
+ private FieldResolutionResult resolveField(DexField field, ProgramDefinition context) {
// Record the references in case they are not program types.
- recordTypeReference(field.holder);
- recordTypeReference(field.type);
+ recordFieldReference(field, context);
FieldResolutionResult resolutionResult = appInfo.resolveField(field);
if (resolutionResult.isFailedOrUnknownResolution()) {
reportMissingField(field);
@@ -1882,31 +1863,33 @@
return resolutionResult;
}
- private SingleResolutionResult resolveMethod(DexMethod method, KeepReason reason) {
+ private SingleResolutionResult resolveMethod(
+ DexMethod method, ProgramDefinition context, KeepReason reason) {
// Record the references in case they are not program types.
- recordMethodReference(method);
+ recordMethodReference(method, context);
ResolutionResult resolutionResult = appInfo.unsafeResolveMethodDueToDexFormat(method);
if (resolutionResult.isFailedResolution()) {
reportMissingMethod(method);
- markFailedResolutionTargets(method, resolutionResult.asFailedResolution(), reason);
+ markFailedResolutionTargets(method, resolutionResult.asFailedResolution(), context, reason);
}
return resolutionResult.asSingleResolution();
}
private SingleResolutionResult resolveMethod(
- DexMethod method, KeepReason reason, boolean interfaceInvoke) {
+ DexMethod method, ProgramDefinition context, KeepReason reason, boolean interfaceInvoke) {
// Record the references in case they are not program types.
- recordMethodReference(method);
+ recordMethodReference(method, context);
ResolutionResult resolutionResult = appInfo.resolveMethod(method, interfaceInvoke);
if (resolutionResult.isFailedResolution()) {
reportMissingMethod(method);
- markFailedResolutionTargets(method, resolutionResult.asFailedResolution(), reason);
+ markFailedResolutionTargets(method, resolutionResult.asFailedResolution(), context, reason);
}
return resolutionResult.asSingleResolution();
}
- private void handleInvokeOfStaticTarget(DexMethod reference, KeepReason reason) {
- SingleResolutionResult resolution = resolveMethod(reference, reason);
+ private void handleInvokeOfStaticTarget(
+ DexMethod reference, ProgramDefinition context, KeepReason reason) {
+ SingleResolutionResult resolution = resolveMethod(reference, context, reason);
if (resolution == null || resolution.getResolvedHolder().isNotProgramClass()) {
return;
}
@@ -1948,7 +1931,7 @@
// Mark all class initializers in all super types as live.
for (DexType superType : current.allImmediateSupertypes()) {
- DexProgramClass superClass = getProgramClassOrNull(superType);
+ DexProgramClass superClass = getProgramClassOrNull(superType, current);
if (superClass != null) {
worklist.addIfNotSeen(superClass);
}
@@ -2021,15 +2004,17 @@
}
// Package protected due to entry point from worklist.
- void markNonStaticDirectMethodAsReachable(DexMethod method, KeepReason reason) {
- handleInvokeOfDirectTarget(method, reason);
+ void markNonStaticDirectMethodAsReachable(
+ DexMethod method, ProgramDefinition context, KeepReason reason) {
+ handleInvokeOfDirectTarget(method, context, reason);
}
- private void handleInvokeOfDirectTarget(DexMethod reference, KeepReason reason) {
+ private void handleInvokeOfDirectTarget(
+ DexMethod reference, ProgramDefinition context, KeepReason reason) {
DexType holder = reference.holder;
- DexProgramClass clazz = getProgramClassOrNull(holder);
+ DexProgramClass clazz = getProgramClassOrNull(holder, context);
if (clazz == null) {
- recordMethodReference(reference);
+ recordMethodReference(reference, context);
return;
}
// TODO(zerny): Is it ok that we lookup in both the direct and virtual pool here?
@@ -2057,30 +2042,30 @@
// Method). In a class, that would lead to a verification error.
if (encodedMethod.isNonPrivateVirtualMethod()
&& virtualMethodsTargetedByInvokeDirect.add(encodedMethod.method)) {
- enqueueMarkMethodLiveAction(method, reason);
+ enqueueMarkMethodLiveAction(method, context, reason);
}
}
- private void ensureFromLibraryOrThrow(DexType type, DexClass context) {
+ private void ensureFromLibraryOrThrow(DexType type, DexLibraryClass context) {
if (mode.isTracingMainDex()) {
// b/72312389: android.jar contains parts of JUnit and most developers include JUnit in
// their programs. This leads to library classes extending program classes. When tracing
// main dex lists we allow this.
return;
}
- DexProgramClass holder = getProgramClassOrNull(type);
- if (holder == null) {
+ DexProgramClass clazz = asProgramClassOrNull(appInfo().definitionFor(type));
+ if (clazz == null) {
return;
}
if (forceProguardCompatibility) {
// To ensure that the program works correctly we have to pin all super types and members
// in the tree.
KeepReason keepReason = KeepReason.reachableFromLiveType(context.type);
- keepClassAndAllMembers(holder, keepReason);
+ keepClassAndAllMembers(clazz, keepReason);
appInfo.forEachSuperType(
- holder,
- (dexType, ignored) -> {
- DexProgramClass superClass = getProgramClassOrNull(dexType);
+ clazz,
+ (superType, subclass, ignored) -> {
+ DexProgramClass superClass = asProgramClassOrNull(appInfo().definitionFor(superType));
if (superClass != null) {
keepClassAndAllMembers(superClass, keepReason);
}
@@ -2096,7 +2081,7 @@
new StringDiagnostic(
"Library class "
+ context.type.toSourceString()
- + (holder.isInterface() ? " implements " : " extends ")
+ + (clazz.isInterface() ? " implements " : " extends ")
+ "program class "
+ type.toSourceString());
if (forceProguardCompatibility) {
@@ -2132,16 +2117,19 @@
});
}
+ private void ignoreMissingClass(DexType clazz) {
+ missingClassesBuilder.ignoreNewMissingClass(clazz);
+ }
+
private void reportMissingClass(DexType clazz) {
assert !mode.isFinalTreeShaking()
+ || missingClassesBuilder.wasAlreadyMissing(clazz)
|| appView.dexItemFactory().isPossiblyCompilerSynthesizedType(clazz)
|| initialDeadProtoTypes.contains(clazz)
- || initialMissingTypes.contains(clazz)
+ // TODO(b/157107464): See if we can clean this up.
+ || (initialPrunedTypes != null && initialPrunedTypes.contains(clazz))
: "Unexpected missing class `" + clazz.toSourceString() + "`";
- boolean newReport = missingTypes.add(clazz);
- if (Log.ENABLED && newReport) {
- Log.verbose(Enqueuer.class, "Class `%s` is missing.", clazz);
- }
+ missingClassesBuilder.addNewMissingClass(clazz);
}
private void reportMissingMethod(DexMethod method) {
@@ -2164,9 +2152,9 @@
return;
}
markReferencedTypesAsLive(method);
- processAnnotations(holder, definition);
+ processAnnotations(holder, method);
definition.parameterAnnotationsList.forEachAnnotation(
- annotation -> processAnnotation(holder, definition, annotation));
+ annotation -> processAnnotation(holder, method, annotation));
if (Log.ENABLED) {
Log.verbose(getClass(), "Method `%s` is targeted.", method);
@@ -2255,7 +2243,7 @@
}
private void checkLambdaInterface(DexType itf, ProgramMethod context) {
- DexClass clazz = definitionFor(itf);
+ DexClass clazz = definitionFor(itf, context);
if (clazz == null) {
StringDiagnostic message =
new StringDiagnostic(
@@ -2277,7 +2265,7 @@
private void transitionMethodsForInstantiatedLambda(LambdaDescriptor lambda) {
transitionMethodsForInstantiatedObject(
InstantiatedObject.of(lambda),
- definitionFor(appInfo.dexItemFactory().objectType),
+ appInfo().definitionFor(appInfo.dexItemFactory().objectType),
lambda.interfaces);
}
@@ -2310,7 +2298,7 @@
markLibraryAndClasspathMethodOverridesAsLive(instantiation, clazz);
}
worklist.addIfNotSeen(Arrays.asList(clazz.interfaces.values));
- clazz = clazz.superType != null ? definitionFor(clazz.superType) : null;
+ clazz = clazz.superType != null ? appInfo().definitionFor(clazz.superType) : null;
}
// The targets for methods on the type and its supertype that are reachable are now marked.
// In a second step, we look at interfaces. We order the search this way such that a
@@ -2318,7 +2306,7 @@
// resolution/dispatch.
while (worklist.hasNext()) {
DexType type = worklist.next();
- DexClass iface = definitionFor(type);
+ DexClass iface = appInfo().definitionFor(type);
if (iface == null) {
continue;
}
@@ -2454,14 +2442,10 @@
private void markOverridesAsLibraryMethodOverrides(
DexProgramClass instantiatedClass, DexMethod libraryMethodOverride) {
- WorkList<DexType> worklist = WorkList.newIdentityWorkList();
- worklist.addIfNotSeen(instantiatedClass.type);
+ WorkList<DexProgramClass> worklist = WorkList.newIdentityWorkList();
+ worklist.addIfNotSeen(instantiatedClass);
while (worklist.hasNext()) {
- DexType type = worklist.next();
- DexProgramClass clazz = getProgramClassOrNull(type);
- if (clazz == null) {
- continue;
- }
+ DexProgramClass clazz = worklist.next();
DexEncodedMethod override = clazz.lookupVirtualMethod(libraryMethodOverride);
if (override != null) {
if (override.isLibraryMethodOverride().isTrue()) {
@@ -2469,7 +2453,13 @@
}
override.setLibraryMethodOverride(OptionalBool.TRUE);
}
- clazz.forEachImmediateSupertype(worklist::addIfNotSeen);
+ clazz.forEachImmediateSupertype(
+ superType -> {
+ DexProgramClass superclass = getProgramClassOrNull(superType, clazz);
+ if (superclass != null) {
+ worklist.addIfNotSeen(superclass);
+ }
+ });
}
}
@@ -2483,9 +2473,11 @@
if (reachableFields != null) {
// TODO(b/120959039): Should the reason this field is reachable come from the set?
KeepReason reason = KeepReason.reachableFromLiveType(clazz.type);
- reachableFields.forEach(field -> markInstanceFieldAsLive(field, reason));
+ for (ProgramField field : reachableFields) {
+ markInstanceFieldAsLive(field, clazz, reason);
+ }
}
- clazz = getProgramClassOrNull(clazz.superType);
+ clazz = getProgramClassOrNull(clazz.superType, clazz);
} while (clazz != null && !objectAllocationInfoCollection.isInstantiatedDirectly(clazz));
}
@@ -2518,7 +2510,7 @@
Set<DexProgramClass> implementedBy = unusedInterfaceTypes.remove(clazz);
if (implementedBy != null) {
for (DexProgramClass implementer : implementedBy) {
- markTypeAsLive(clazz, graphReporter.reportClassReferencedFrom(clazz, implementer));
+ markTypeAsLive(clazz, implementer);
}
}
} else {
@@ -2527,16 +2519,19 @@
}
private void markFieldAsTargeted(DexField field, ProgramMethod context) {
- markTypeAsLive(field.type, clazz -> graphReporter.reportClassReferencedFrom(clazz, context));
- markTypeAsLive(field.holder, clazz -> graphReporter.reportClassReferencedFrom(clazz, context));
+ markTypeAsLive(field.type, context);
+ markTypeAsLive(field.holder, context);
}
- private void markStaticFieldAsLive(ProgramField field, KeepReason reason) {
+ private void markStaticFieldAsLive(ProgramField field, ProgramMethod context) {
+ markStaticFieldAsLive(field, context, KeepReason.fieldReferencedIn(context));
+ }
+
+ private void markStaticFieldAsLive(
+ ProgramField field, ProgramDefinition context, KeepReason reason) {
// Mark the type live here, so that the class exists at runtime.
- markTypeAsLive(
- field.getHolder(), graphReporter.reportClassReferencedFrom(field.getHolder(), field));
- markTypeAsLive(
- field.getReference().type, clazz -> graphReporter.reportClassReferencedFrom(clazz, field));
+ markTypeAsLive(field.getHolder(), field);
+ markTypeAsLive(field.getReference().type, field);
markDirectAndIndirectClassInitializersAsLive(field.getHolder());
@@ -2552,7 +2547,7 @@
Log.verbose(getClass(), "Adding instance field `%s` to live set (static context).", field);
}
}
- processAnnotations(field.getHolder(), field.getDefinition());
+ processAnnotations(field.getHolder(), field);
liveFields.add(field, reason);
// Add all dependent members to the workqueue.
@@ -2561,16 +2556,17 @@
checkMemberForSoftPinning(field);
// Notify analyses.
- analyses.forEach(analysis -> analysis.processNewlyLiveField(field));
+ analyses.forEach(analysis -> analysis.processNewlyLiveField(field, context));
}
- private void markInstanceFieldAsLive(ProgramField field, KeepReason reason) {
- markTypeAsLive(field.getHolder(), graphReporter.registerClass(field.getHolder(), reason));
- markTypeAsLive(field.getReference().type, reason);
+ private void markInstanceFieldAsLive(
+ ProgramField field, ProgramDefinition context, KeepReason reason) {
+ markTypeAsLive(field.getHolder(), field);
+ markTypeAsLive(field.getType(), field);
if (Log.ENABLED) {
Log.verbose(getClass(), "Adding instance field `%s` to live set.", field);
}
- processAnnotations(field.getHolder(), field.getDefinition());
+ processAnnotations(field.getHolder(), field);
liveFields.add(field, reason);
// Add all dependent members to the workqueue.
@@ -2579,11 +2575,11 @@
checkMemberForSoftPinning(field);
// Notify analyses.
- analyses.forEach(analysis -> analysis.processNewlyLiveField(field));
+ analyses.forEach(analysis -> analysis.processNewlyLiveField(field, context));
}
private void markDirectStaticOrConstructorMethodAsLive(ProgramMethod method, KeepReason reason) {
- if (!enqueueMarkMethodLiveAction(method, reason)) {
+ if (!enqueueMarkMethodLiveAction(method, method, reason)) {
// Already marked live.
return;
}
@@ -2601,7 +2597,7 @@
assert !method.getDefinition().isAbstract()
|| reason.isDueToKeepRule()
|| reason.isDueToReflectiveUse();
- if (enqueueMarkMethodLiveAction(method, reason)) {
+ if (enqueueMarkMethodLiveAction(method, method, reason)) {
if (Log.ENABLED) {
Log.verbose(getClass(), "Adding virtual method `%s` to live set.", method);
}
@@ -2678,26 +2674,25 @@
}
// Package protected due to entry point from worklist.
- void markInstanceFieldAsReachable(ProgramField field, KeepReason reason) {
+ void markInstanceFieldAsReachable(
+ ProgramField field, ProgramDefinition context, KeepReason reason) {
if (Log.ENABLED) {
Log.verbose(getClass(), "Marking instance field `%s` as reachable.", field);
}
- markTypeAsLive(
- field.getHolder(), graphReporter.reportClassReferencedFrom(field.getHolder(), field));
- markTypeAsLive(
- field.getReference().type, clazz -> graphReporter.reportClassReferencedFrom(clazz, field));
-
// We might have a instance field access that is dispatched to a static field. In such case,
// we have to keep the static field, so that the dispatch fails at runtime in the same way that
// it did before. We have to keep the field even if the receiver has no live inhabitants, as
// field resolution happens before the receiver is inspected.
if (field.getDefinition().isStatic()) {
- markStaticFieldAsLive(field, reason);
+ markStaticFieldAsLive(field, context, reason);
} else if (objectAllocationInfoCollection.isInstantiatedDirectlyOrHasInstantiatedSubtype(
field.getHolder())) {
- markInstanceFieldAsLive(field, reason);
+ markInstanceFieldAsLive(field, context, reason);
} else {
+ markTypeAsLive(field.getHolder(), field);
+ markTypeAsLive(field.getReference().type, field);
+
// Add the field to the reachable set if the type later becomes instantiated.
reachableInstanceFields
.computeIfAbsent(field.getHolder(), ignore -> ProgramFieldSet.create())
@@ -2706,28 +2701,28 @@
}
private void markVirtualMethodAsReachable(
- DexMethod method, boolean interfaceInvoke, ProgramMethod contextOrNull, KeepReason reason) {
+ DexMethod method, boolean interfaceInvoke, ProgramDefinition context, KeepReason reason) {
if (method.holder.isArrayType()) {
// This is an array type, so the actual class will be generated at runtime. We treat this
// like an invoke on a direct subtype of java.lang.Object that has no further subtypes.
// As it has no subtypes, it cannot affect liveness of the program we are processing.
// Ergo, we can ignore it. We need to make sure that the element type is available, though.
- markTypeAsLive(method.holder, reason);
+ markTypeAsLive(method.holder, context, reason);
return;
}
// Note that all virtual methods derived from library methods are kept regardless of being
// reachable, so the following only needs to consider reachable targets in the program.
// TODO(b/70160030): Revise this to support tree shaking library methods on non-escaping types.
- DexProgramClass holder = getProgramClassOrNull(method.holder);
+ DexProgramClass holder = getProgramClassOrNull(method.holder, context);
if (holder == null) {
// TODO(b/139464956): clean this.
// Ensure that the full proto of the targeted method is referenced.
- recordMethodReference(method);
+ recordMethodReference(method, context);
return;
}
- SingleResolutionResult resolution = resolveMethod(method, reason, interfaceInvoke);
+ SingleResolutionResult resolution = resolveMethod(method, context, reason, interfaceInvoke);
if (resolution == null) {
return;
}
@@ -2756,20 +2751,12 @@
DexEncodedMethod resolvedMethod = resolution.getResolvedMethod();
markMethodAsTargeted(new ProgramMethod(resolvedHolder, resolvedMethod), reason);
- DexProgramClass contextHolder = contextOrNull != null ? contextOrNull.getHolder() : null;
- if (contextOrNull != null
- && resolution.isAccessibleForVirtualDispatchFrom(contextHolder, appInfo).isFalse()) {
+ DexProgramClass contextHolder = context.getContextClass();
+ if (resolution.isAccessibleForVirtualDispatchFrom(contextHolder, appInfo).isFalse()) {
// Not accessible from this context, so this call will cause a runtime exception.
return;
}
- // If the resolved method is not a virtual target, eg, is static, dispatch will fail too.
- if (!resolvedMethod.isVirtualMethod()) {
- // This can only happen when context is null, otherwise the access check above will fail.
- assert contextOrNull == null;
- return;
- }
-
// The method resolved and is accessible, so currently live overrides become live.
reachableVirtualTargets.computeIfAbsent(holder, k -> Sets.newIdentityHashSet()).add(method);
@@ -2812,16 +2799,20 @@
LookupLambdaTarget target, Function<ProgramMethod, KeepReasonWitness> reason) {
ProgramMethod implementationMethod = target.getImplementationMethod().asProgramMethod();
if (implementationMethod != null) {
- enqueueMarkMethodLiveAction(implementationMethod, reason.apply(implementationMethod));
+ enqueueMarkMethodLiveAction(
+ implementationMethod, implementationMethod, reason.apply(implementationMethod));
}
}
private void markFailedResolutionTargets(
- DexMethod symbolicMethod, FailedResolutionResult failedResolution, KeepReason reason) {
+ DexMethod symbolicMethod,
+ FailedResolutionResult failedResolution,
+ ProgramDefinition context,
+ KeepReason reason) {
failedResolutionTargets.add(symbolicMethod);
failedResolution.forEachFailureDependency(
method -> {
- DexProgramClass clazz = getProgramClassOrNull(method.getHolderType());
+ DexProgramClass clazz = getProgramClassOrNull(method.getHolderType(), context);
if (clazz != null) {
failedResolutionTargets.add(method.method);
markMethodAsTargeted(new ProgramMethod(clazz, method), reason);
@@ -2855,7 +2846,7 @@
// Package protected due to entry point from worklist.
void markSuperMethodAsReachable(DexMethod reference, ProgramMethod from) {
KeepReason reason = KeepReason.targetedBySuperFrom(from);
- SingleResolutionResult resolution = resolveMethod(reference, reason);
+ SingleResolutionResult resolution = resolveMethod(reference, from, reason);
if (resolution == null) {
return;
}
@@ -2873,7 +2864,7 @@
return;
}
- DexProgramClass clazz = getProgramClassOrNull(target.getHolderType());
+ DexProgramClass clazz = getProgramClassOrNull(target.getHolderType(), from);
if (clazz == null) {
return;
}
@@ -2955,14 +2946,10 @@
assert appView.options().getProguardConfiguration().getKeepAllRule() != null;
ImmutableSet<ProguardKeepRuleBase> keepAllSet =
ImmutableSet.of(appView.options().getProguardConfiguration().getKeepAllRule());
- for (DexProgramClass dexProgramClass : appView.appInfo().classes()) {
- for (DexEncodedMethod method : dexProgramClass.methods()) {
- this.enqueueRootItem(method, keepAllSet);
- }
- for (DexEncodedField field : dexProgramClass.fields()) {
- this.enqueueRootItem(field, keepAllSet);
- }
- this.enqueueRootItem(dexProgramClass, keepAllSet);
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ enqueueRootClass(clazz, keepAllSet);
+ clazz.forEachProgramMethod(method -> enqueueRootMethod(method, keepAllSet));
+ clazz.forEachProgramField(field -> enqueueRootField(field, keepAllSet));
}
}
trace(executorService, timing);
@@ -3113,7 +3100,7 @@
for (ProgramMethod liveMethod : liveMethods.values()) {
assert !enqueuer.targetedMethods.contains(liveMethod.getDefinition());
enqueuer.markMethodAsTargeted(liveMethod, fakeReason);
- enqueuer.enqueueMarkMethodLiveAction(liveMethod, fakeReason);
+ enqueuer.enqueueMarkMethodLiveAction(liveMethod, liveMethod, fakeReason);
}
enqueuer.liveNonProgramTypes.addAll(syntheticClasspathClasses.values());
}
@@ -3277,7 +3264,9 @@
rootSet.pruneDeadItems(appView, this);
// Ensure references from all hard coded factory items.
- appView.dexItemFactory().forEachPossiblyCompilerSynthesizedType(this::recordTypeReference);
+ appView
+ .dexItemFactory()
+ .forEachPossiblyCompilerSynthesizedType(this::recordCompilerSynthesizedTypeReference);
// Rebuild a new app only containing referenced types.
Set<DexLibraryClass> libraryClasses = Sets.newIdentityHashSet();
@@ -3301,7 +3290,6 @@
// Verify the references on the pruned application after type synthesis.
assert verifyReferences(app);
- assert verifyMissingTypes();
AppInfoWithLiveness appInfoWithLiveness =
new AppInfoWithLiveness(
@@ -3309,9 +3297,9 @@
appInfo.getClassToFeatureSplitMap(),
appInfo.getMainDexClasses(),
deadProtoTypes,
- mode.isFinalTreeShaking()
- ? Sets.union(initialMissingTypes, missingTypes)
- : missingTypes,
+ appView.testing().enableExperimentalMissingClassesReporting
+ ? missingClassesBuilder.reportMissingClasses(options)
+ : missingClassesBuilder.ignoreMissingClasses(),
SetUtils.mapIdentityHashSet(liveTypes.getItems(), DexProgramClass::getType),
Enqueuer.toDescriptorSet(targetedMethods.getItems()),
Collections.unmodifiableSet(failedResolutionTargets),
@@ -3331,6 +3319,7 @@
rootSet.alwaysInline,
rootSet.forceInline,
rootSet.neverInline,
+ rootSet.neverInlineDueToSingleCaller,
rootSet.whyAreYouNotInlining,
rootSet.keepConstantArguments,
rootSet.keepUnusedArguments,
@@ -3360,8 +3349,9 @@
.getKnownLambdaClasses()
.forEach(
(type, lambda) -> {
- DexProgramClass synthesizedClass = getProgramClassOrNull(type);
+ DexProgramClass synthesizedClass = lambda.getOrCreateLambdaClass();
assert synthesizedClass != null;
+ assert synthesizedClass == appInfo().definitionForWithoutExistenceAssert(type);
assert liveTypes.contains(synthesizedClass);
if (synthesizedClass == null) {
return;
@@ -3387,22 +3377,6 @@
}
}
- private boolean verifyMissingTypes() {
- if (initialMissingTypes == null) {
- assert !mode.isFinalTreeShaking();
- return true;
- }
- missingTypes.forEach(
- missingType -> {
- assert initialMissingTypes.contains(missingType)
- // TODO(b/157107464): See if we can clean this up.
- || initialPrunedTypes.contains(missingType)
- || missingType.isD8R8SynthesizedClassType()
- : missingType;
- });
- return true;
- }
-
private boolean verifyReferences(DexApplication app) {
WorkList<DexClass> worklist = WorkList.newIdentityWorkList();
for (DexProgramClass clazz : liveTypes.getItems()) {
@@ -3425,9 +3399,11 @@
}
DexClass clazz = app.definitionFor(type);
if (clazz == null) {
- assert missingTypes.contains(type) : "Expected type to be in missing types': " + type;
+ assert missingClassesBuilder.contains(type)
+ : "Expected type to be in missing types': " + type;
} else {
- assert !missingTypes.contains(type) : "Type with definition also in missing types: " + type;
+ assert !missingClassesBuilder.contains(type)
+ : "Type with definition also in missing types: " + type;
// Eager assert while the context is still present.
assert clazz.isProgramClass() || liveNonProgramTypes.contains(clazz)
: "Expected type to be in live non-program types: " + clazz;
@@ -3653,7 +3629,8 @@
consequentRootSet.dependentKeepClassCompatRule.forEach(
(precondition, compatRules) -> {
assert precondition.isDexType();
- DexProgramClass preconditionHolder = getProgramClassOrNull(precondition.asDexType());
+ DexProgramClass preconditionHolder =
+ asProgramClassOrNull(appInfo().definitionFor(precondition.asDexType()));
compatEnqueueHolderIfDependentNonStaticMember(preconditionHolder, compatRules);
});
}
@@ -3662,11 +3639,12 @@
private boolean isLiveProgramReference(DexReference reference) {
if (reference.isDexType()) {
DexProgramClass clazz =
- DexProgramClass.asProgramClassOrNull(definitionFor(reference.asDexType()));
+ DexProgramClass.asProgramClassOrNull(appInfo().definitionFor(reference.asDexType()));
return clazz != null && isTypeLive(clazz);
}
DexMember<?, ?> member = reference.asDexMember();
- DexProgramClass holder = DexProgramClass.asProgramClassOrNull(definitionFor(member.holder));
+ DexProgramClass holder =
+ DexProgramClass.asProgramClassOrNull(appInfo().definitionFor(member.holder));
ProgramMember<?, ?> programMember = member.lookupOnProgramClass(holder);
return programMember != null && isMemberLive(programMember.getDefinition());
}
@@ -3706,9 +3684,10 @@
markVirtualMethodAsReachable(
singleTargetMethod.method,
singleTargetHolder.isInterface(),
- null,
+ singleTarget,
graphReporter.fakeReportShouldNotBeUsed());
- enqueueMarkMethodLiveAction(singleTarget, graphReporter.fakeReportShouldNotBeUsed());
+ enqueueMarkMethodLiveAction(
+ singleTarget, singleTarget, graphReporter.fakeReportShouldNotBeUsed());
}
}
action.getAction().accept(builder);
@@ -3730,7 +3709,7 @@
// A virtual method. Mark it as reachable so that subclasses, if instantiated, keep
// their overrides. However, we don't mark it live, as a keep rule might not imply that
// the corresponding class is live.
- markVirtualMethodAsReachable(reference, holder.isInterface(), null, reason);
+ markVirtualMethodAsReachable(reference, holder.isInterface(), target, reason);
if (holder.isInterface()) {
// Reachability for default methods is based on live subtypes in general. For keep rules,
// we need special handling as we essentially might have live subtypes that are outside of
@@ -3742,7 +3721,8 @@
} else {
DexEncodedMethod implementation = definition.getDefaultInterfaceMethodImplementation();
if (implementation != null) {
- DexProgramClass companion = getProgramClassOrNull(implementation.getHolderType());
+ DexProgramClass companion =
+ asProgramClassOrNull(appInfo().definitionFor(implementation.getHolderType()));
markTypeAsLive(companion, graphReporter.reportCompanionClass(holder, companion));
markVirtualMethodAsLive(
new ProgramMethod(companion, implementation),
@@ -3759,9 +3739,9 @@
// Package protected due to entry point from worklist.
void markFieldAsKept(ProgramField field, KeepReason reason) {
if (field.getDefinition().isStatic()) {
- markStaticFieldAsLive(field, reason);
+ markStaticFieldAsLive(field, field, reason);
} else {
- markInstanceFieldAsReachable(field, reason);
+ markInstanceFieldAsReachable(field, field, reason);
}
}
@@ -3832,7 +3812,7 @@
}
// Package protected due to entry point from worklist.
- void markMethodAsLive(ProgramMethod method, KeepReason reason) {
+ void markMethodAsLive(ProgramMethod method, ProgramDefinition context) {
DexProgramClass holder = method.getHolder();
DexEncodedMethod definition = method.getDefinition();
@@ -3853,9 +3833,9 @@
}
}
markParameterAndReturnTypesAsLive(method);
- processAnnotations(holder, definition);
+ processAnnotations(holder, method);
definition.parameterAnnotationsList.forEachAnnotation(
- annotation -> processAnnotation(holder, definition, annotation));
+ annotation -> processAnnotation(holder, method, annotation));
method.registerCodeReferences(useRegistryFactory.create(appView, method, this));
// Add all dependent members to the workqueue.
@@ -3864,7 +3844,7 @@
checkMemberForSoftPinning(method);
// Notify analyses.
- analyses.forEach(analysis -> analysis.processNewlyLiveMethod(method));
+ analyses.forEach(analysis -> analysis.processNewlyLiveMethod(method, context));
}
private void checkMemberForSoftPinning(ProgramMember<?, ?> member) {
@@ -3884,25 +3864,23 @@
}
private void markReferencedTypesAsLive(ProgramMethod method) {
- markTypeAsLive(
- method.getHolderType(), clazz -> graphReporter.reportClassReferencedFrom(clazz, method));
+ markTypeAsLive(method.getHolderType(), method);
markParameterAndReturnTypesAsLive(method);
}
private void markParameterAndReturnTypesAsLive(ProgramMethod method) {
for (DexType parameterType : method.getDefinition().parameters().values) {
- markTypeAsLive(
- parameterType, clazz -> graphReporter.reportClassReferencedFrom(clazz, method));
+ markTypeAsLive(parameterType, method);
}
- markTypeAsLive(
- method.getDefinition().returnType(),
- clazz -> graphReporter.reportClassReferencedFrom(clazz, method));
+ markTypeAsLive(method.getDefinition().returnType(), method);
}
private void markClassAsInstantiatedWithReason(DexProgramClass clazz, KeepReason reason) {
workList.enqueueMarkInstantiatedAction(clazz, null, InstantiationReason.REFLECTION, reason);
if (clazz.hasDefaultInitializer()) {
- workList.enqueueMarkReachableDirectAction(clazz.getDefaultInitializer().method, reason);
+ ProgramMethod defaultInitializer = clazz.getProgramDefaultInitializer();
+ workList.enqueueMarkReachableDirectAction(
+ defaultInitializer.getReference(), defaultInitializer, reason);
}
}
@@ -3918,13 +3896,14 @@
ProgramMethod defaultInitializer = clazz.getProgramDefaultInitializer();
workList.enqueueMarkReachableDirectAction(
defaultInitializer.getReference(),
+ defaultInitializer,
graphReporter.reportCompatKeepDefaultInitializer(defaultInitializer));
}
}
}
private void markMethodAsLiveWithCompatRule(ProgramMethod method) {
- enqueueMarkMethodLiveAction(method, graphReporter.reportCompatKeepMethod(method));
+ enqueueMarkMethodLiveAction(method, method, graphReporter.reportCompatKeepMethod(method));
}
private void handleReflectiveBehavior(ProgramMethod method) {
@@ -3976,7 +3955,8 @@
assert identifierLookupResult.isTypeResult();
IdentifierNameStringTypeLookupResult identifierTypeLookupResult =
identifierLookupResult.asTypeResult();
- DexProgramClass clazz = getProgramClassOrNullFromReflectiveAccess(referencedItem.asDexType());
+ DexProgramClass clazz =
+ getProgramClassOrNullFromReflectiveAccess(referencedItem.asDexType(), method);
if (clazz == null) {
return;
}
@@ -4001,7 +3981,7 @@
}
} else if (referencedItem.isDexField()) {
DexField field = referencedItem.asDexField();
- DexProgramClass clazz = getProgramClassOrNull(field.holder);
+ DexProgramClass clazz = getProgramClassOrNullFromReflectiveAccess(field.holder, method);
if (clazz == null) {
return;
}
@@ -4029,7 +4009,8 @@
} else {
assert referencedItem.isDexMethod();
DexMethod targetedMethodReference = referencedItem.asDexMethod();
- DexProgramClass clazz = getProgramClassOrNull(targetedMethodReference.holder);
+ DexProgramClass clazz =
+ getProgramClassOrNullFromReflectiveAccess(targetedMethodReference.holder, method);
if (clazz == null) {
return;
}
@@ -4064,7 +4045,7 @@
return;
}
- DexProgramClass clazz = getProgramClassOrNullFromReflectiveAccess(instantiatedType);
+ DexProgramClass clazz = getProgramClassOrNullFromReflectiveAccess(instantiatedType, method);
if (clazz == null) {
return;
}
@@ -4108,7 +4089,7 @@
return;
}
- DexProgramClass clazz = getProgramClassOrNullFromReflectiveAccess(instantiatedType);
+ DexProgramClass clazz = getProgramClassOrNullFromReflectiveAccess(instantiatedType, method);
if (clazz == null) {
return;
}
@@ -4206,7 +4187,7 @@
continue;
}
- DexProgramClass clazz = getProgramClassOrNull(type);
+ DexProgramClass clazz = getProgramClassOrNull(type, method);
if (clazz != null && clazz.isInterface()) {
// Add this interface to the set of pinned items to ensure that we do not merge the
// interface into its unique subtype, if any.
@@ -4220,7 +4201,7 @@
clazz.forEachProgramVirtualMethod(
virtualMethod -> {
keepInfo.pinMethod(virtualMethod);
- markVirtualMethodAsReachable(virtualMethod.getReference(), true, null, reason);
+ markVirtualMethodAsReachable(virtualMethod.getReference(), true, clazz, reason);
});
}
}
@@ -4233,8 +4214,8 @@
// call this method.
if (invoke.inValues().get(0).isConstClass()) {
DexType type = invoke.inValues().get(0).definition.asConstClass().getValue();
- DexProgramClass clazz = getProgramClassOrNull(type);
- if (clazz != null && clazz.accessFlags.isEnum()) {
+ DexProgramClass clazz = getProgramClassOrNull(type, method);
+ if (clazz != null && clazz.isEnum()) {
markEnumValuesAsReachable(clazz, KeepReason.invokedFrom(method));
}
}
@@ -4264,16 +4245,17 @@
return;
}
- handleServiceInstantiation(serviceType, KeepReason.reflectiveUseIn(method));
+ handleServiceInstantiation(serviceType, method, KeepReason.reflectiveUseIn(method));
} else {
KeepReason reason = KeepReason.reflectiveUseIn(method);
for (DexType serviceType : appView.appServices().allServiceTypes()) {
- handleServiceInstantiation(serviceType, reason);
+ handleServiceInstantiation(serviceType, method, reason);
}
}
}
- private void handleServiceInstantiation(DexType serviceType, KeepReason reason) {
+ private void handleServiceInstantiation(
+ DexType serviceType, ProgramMethod context, KeepReason reason) {
List<DexType> serviceImplementationTypes =
appView.appServices().serviceImplementationsFor(serviceType);
for (DexType serviceImplementationType : serviceImplementationTypes) {
@@ -4282,7 +4264,8 @@
continue;
}
- DexProgramClass serviceImplementationClass = getProgramClassOrNull(serviceImplementationType);
+ DexProgramClass serviceImplementationClass =
+ getProgramClassOrNull(serviceImplementationType, context);
if (serviceImplementationClass != null && serviceImplementationClass.isProgramClass()) {
markClassAsInstantiatedWithReason(serviceImplementationClass, reason);
}
@@ -4403,12 +4386,17 @@
private class AnnotationReferenceMarker implements IndexedItemCollection {
private final DexItem annotationHolder;
+ private final ProgramDefinition context;
private final DexItemFactory dexItemFactory;
private final KeepReason reason;
private AnnotationReferenceMarker(
- DexItem annotationHolder, DexItemFactory dexItemFactory, KeepReason reason) {
+ DexItem annotationHolder,
+ ProgramDefinition context,
+ DexItemFactory dexItemFactory,
+ KeepReason reason) {
this.annotationHolder = annotationHolder;
+ this.context = context;
this.dexItemFactory = dexItemFactory;
this.reason = reason;
}
@@ -4420,8 +4408,8 @@
@Override
public boolean addField(DexField fieldReference) {
- recordFieldReference(fieldReference);
- DexProgramClass holder = getProgramClassOrNull(fieldReference.holder);
+ recordFieldReference(fieldReference, context);
+ DexProgramClass holder = getProgramHolderOrNull(fieldReference, context);
if (holder == null) {
return false;
}
@@ -4440,7 +4428,7 @@
: fieldAccessInfoCollection.extend(
fieldReference, new FieldAccessInfoImpl(fieldReference));
fieldAccessInfo.setReadFromAnnotation();
- markStaticFieldAsLive(field, KeepReason.referencedInAnnotation(annotationHolder));
+ markStaticFieldAsLive(field, context, KeepReason.referencedInAnnotation(annotationHolder));
// When an annotation has a field of an enum type the JVM will use the values() method on
// that enum class if the field is referenced.
if (options.isGeneratingClassFiles() && field.getHolder().isEnum()) {
@@ -4449,7 +4437,8 @@
}
} else {
// There is no dispatch on annotations, so only keep what is directly referenced.
- markInstanceFieldAsReachable(field, KeepReason.referencedInAnnotation(annotationHolder));
+ markInstanceFieldAsReachable(
+ field, context, KeepReason.referencedInAnnotation(annotationHolder));
}
return false;
}
@@ -4457,8 +4446,8 @@
@Override
public boolean addMethod(DexMethod method) {
// Record the references in case they are not program types.
- recordMethodReference(method);
- DexProgramClass holder = getProgramClassOrNull(method.holder);
+ recordMethodReference(method, context);
+ DexProgramClass holder = getProgramHolderOrNull(method, context);
if (holder == null) {
return false;
}
@@ -4507,30 +4496,22 @@
// Annotations can also contain the void type, which is not a class type, so filter it out
// here.
if (type != dexItemFactory.voidType) {
- markTypeAsLive(type, reason);
+ markTypeAsLive(type, context, reason);
}
return false;
}
}
- public static class EnqueuerDefinitionSupplier implements DexDefinitionSupplier {
+ public static class EnqueuerDefinitionSupplier {
private final Enqueuer enqueuer;
- private final AppView<?> appView;
- private EnqueuerDefinitionSupplier(AppView<?> appView, Enqueuer enqueuer) {
- this.appView = appView;
+ EnqueuerDefinitionSupplier(Enqueuer enqueuer) {
this.enqueuer = enqueuer;
}
- @Override
- public DexClass definitionFor(DexType type) {
- return enqueuer.definitionFor(type);
- }
-
- @Override
- public DexItemFactory dexItemFactory() {
- return appView.dexItemFactory();
+ public DexClass definitionFor(DexType type, ProgramDefinition context) {
+ return enqueuer.definitionFor(type, context);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java
index 5ff9c97..34a8750 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java
@@ -15,7 +15,8 @@
public class EnqueuerFactory {
public static Enqueuer createForInitialTreeShaking(
- AppView<? extends AppInfoWithClassHierarchy> appView, SubtypingInfo subtypingInfo) {
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ SubtypingInfo subtypingInfo) {
return new Enqueuer(appView, subtypingInfo, null, Mode.INITIAL_TREE_SHAKING);
}
@@ -23,19 +24,18 @@
AppView<? extends AppInfoWithClassHierarchy> appView,
SubtypingInfo subtypingInfo,
GraphConsumer keptGraphConsumer,
- Set<DexType> initialMissingTypes,
Set<DexType> initialPrunedTypes) {
Enqueuer enqueuer =
new Enqueuer(appView, subtypingInfo, keptGraphConsumer, Mode.FINAL_TREE_SHAKING);
appView.withProtoShrinker(
shrinker -> enqueuer.setInitialDeadProtoTypes(shrinker.getDeadProtoTypes()));
- enqueuer.setInitialMissingTypes(initialMissingTypes);
enqueuer.setInitialPrunedTypes(initialPrunedTypes);
return enqueuer;
}
public static Enqueuer createForMainDexTracing(
- AppView<? extends AppInfoWithClassHierarchy> appView, SubtypingInfo subtypingInfo) {
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ SubtypingInfo subtypingInfo) {
return createForMainDexTracing(appView, subtypingInfo, null);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerUseRegistryFactory.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerUseRegistryFactory.java
index 86acd84..c15f8d7 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerUseRegistryFactory.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerUseRegistryFactory.java
@@ -4,11 +4,15 @@
package com.android.tools.r8.shaking;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.UseRegistry;
public interface EnqueuerUseRegistryFactory {
- UseRegistry create(AppView<?> appView, ProgramMethod currentMethod, Enqueuer enqueuer);
+ UseRegistry create(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ ProgramMethod currentMethod,
+ Enqueuer enqueuer);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
index c368c21..0be900c 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
@@ -4,11 +4,11 @@
package com.android.tools.r8.shaking;
-import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.shaking.GraphReporter.KeepReasonWitness;
@@ -22,23 +22,27 @@
}
static class MarkReachableDirectAction extends EnqueuerAction {
- final DexMethod target;
- final KeepReason reason;
+ private final DexMethod target;
+ // TODO(b/175854431): Avoid pushing context on worklist.
+ private final ProgramDefinition context;
+ private final KeepReason reason;
- MarkReachableDirectAction(DexMethod target, KeepReason reason) {
+ MarkReachableDirectAction(DexMethod target, ProgramDefinition context, KeepReason reason) {
this.target = target;
+ this.context = context;
this.reason = reason;
}
@Override
public void run(Enqueuer enqueuer) {
- enqueuer.markNonStaticDirectMethodAsReachable(target, reason);
+ enqueuer.markNonStaticDirectMethodAsReachable(target, context, reason);
}
}
static class MarkReachableSuperAction extends EnqueuerAction {
- final DexMethod target;
- final ProgramMethod context;
+ private final DexMethod target;
+ // TODO(b/175854431): Avoid pushing context on worklist.
+ private final ProgramMethod context;
public MarkReachableSuperAction(DexMethod target, ProgramMethod context) {
this.target = target;
@@ -52,26 +56,30 @@
}
static class MarkReachableFieldAction extends EnqueuerAction {
- final ProgramField field;
- final KeepReason reason;
+ private final ProgramField field;
+ // TODO(b/175854431): Avoid pushing context on worklist.
+ private final ProgramDefinition context;
+ private final KeepReason reason;
- public MarkReachableFieldAction(ProgramField field, KeepReason reason) {
+ public MarkReachableFieldAction(
+ ProgramField field, ProgramDefinition context, KeepReason reason) {
this.field = field;
+ this.context = context;
this.reason = reason;
}
@Override
public void run(Enqueuer enqueuer) {
- enqueuer.markInstanceFieldAsReachable(field, reason);
+ enqueuer.markInstanceFieldAsReachable(field, context, reason);
}
}
static class MarkInstantiatedAction extends EnqueuerAction {
-
- final DexProgramClass target;
- final ProgramMethod context;
- final InstantiationReason instantiationReason;
- final KeepReason keepReason;
+ private final DexProgramClass target;
+ // TODO(b/175854431): Avoid pushing context on worklist.
+ private final ProgramMethod context;
+ private final InstantiationReason instantiationReason;
+ private final KeepReason keepReason;
public MarkInstantiatedAction(
DexProgramClass target,
@@ -91,8 +99,8 @@
}
static class MarkAnnotationInstantiatedAction extends EnqueuerAction {
- final DexProgramClass target;
- final KeepReasonWitness reason;
+ private final DexProgramClass target;
+ private final KeepReasonWitness reason;
public MarkAnnotationInstantiatedAction(DexProgramClass target, KeepReasonWitness reason) {
this.target = target;
@@ -106,8 +114,8 @@
}
static class MarkInterfaceInstantiatedAction extends EnqueuerAction {
- final DexProgramClass target;
- final KeepReasonWitness reason;
+ private final DexProgramClass target;
+ private final KeepReasonWitness reason;
public MarkInterfaceInstantiatedAction(DexProgramClass target, KeepReasonWitness reason) {
this.target = target;
@@ -121,23 +129,24 @@
}
static class MarkMethodLiveAction extends EnqueuerAction {
- final ProgramMethod method;
- final KeepReason reason;
+ private final ProgramMethod method;
+ // TODO(b/175854431): Avoid pushing context on worklist.
+ private final ProgramDefinition context;
- public MarkMethodLiveAction(ProgramMethod method, KeepReason reason) {
+ public MarkMethodLiveAction(ProgramMethod method, ProgramDefinition context) {
this.method = method;
- this.reason = reason;
+ this.context = context;
}
@Override
public void run(Enqueuer enqueuer) {
- enqueuer.markMethodAsLive(method, reason);
+ enqueuer.markMethodAsLive(method, context);
}
}
static class MarkMethodKeptAction extends EnqueuerAction {
- final ProgramMethod target;
- final KeepReason reason;
+ private final ProgramMethod target;
+ private final KeepReason reason;
public MarkMethodKeptAction(ProgramMethod target, KeepReason reason) {
this.target = target;
@@ -151,8 +160,8 @@
}
static class MarkFieldKeptAction extends EnqueuerAction {
- final ProgramField field;
- final KeepReasonWitness witness;
+ private final ProgramField field;
+ private final KeepReasonWitness witness;
public MarkFieldKeptAction(ProgramField field, KeepReasonWitness witness) {
this.field = field;
@@ -166,8 +175,9 @@
}
static class TraceConstClassAction extends EnqueuerAction {
- final DexType type;
- final ProgramMethod context;
+ private final DexType type;
+ // TODO(b/175854431): Avoid pushing context on worklist.
+ private final ProgramMethod context;
TraceConstClassAction(DexType type, ProgramMethod context) {
this.type = type;
@@ -181,8 +191,9 @@
}
static class TraceInvokeDirectAction extends EnqueuerAction {
- final DexMethod invokedMethod;
- final ProgramMethod context;
+ private final DexMethod invokedMethod;
+ // TODO(b/175854431): Avoid pushing context on worklist.
+ private final ProgramMethod context;
TraceInvokeDirectAction(DexMethod invokedMethod, ProgramMethod context) {
this.invokedMethod = invokedMethod;
@@ -196,8 +207,9 @@
}
static class TraceNewInstanceAction extends EnqueuerAction {
- final DexType type;
- final ProgramMethod context;
+ private final DexType type;
+ // TODO(b/175854431): Avoid pushing context on worklist.
+ private final ProgramMethod context;
TraceNewInstanceAction(DexType type, ProgramMethod context) {
this.type = type;
@@ -211,8 +223,9 @@
}
static class TraceStaticFieldReadAction extends EnqueuerAction {
- final DexField field;
- final ProgramMethod context;
+ private final DexField field;
+ // TODO(b/175854431): Avoid pushing context on worklist.
+ private final ProgramMethod context;
TraceStaticFieldReadAction(DexField field, ProgramMethod context) {
this.field = field;
@@ -225,15 +238,12 @@
}
}
- private final AppView<?> appView;
private final Queue<EnqueuerAction> queue = new ArrayDeque<>();
- private EnqueuerWorklist(AppView<?> appView) {
- this.appView = appView;
- }
+ private EnqueuerWorklist() {}
- public static EnqueuerWorklist createWorklist(AppView<?> appView) {
- return new EnqueuerWorklist(appView);
+ public static EnqueuerWorklist createWorklist() {
+ return new EnqueuerWorklist();
}
public boolean isEmpty() {
@@ -244,16 +254,18 @@
return queue.poll();
}
- void enqueueMarkReachableDirectAction(DexMethod method, KeepReason reason) {
- queue.add(new MarkReachableDirectAction(method, reason));
+ void enqueueMarkReachableDirectAction(
+ DexMethod method, ProgramDefinition context, KeepReason reason) {
+ queue.add(new MarkReachableDirectAction(method, context, reason));
}
void enqueueMarkReachableSuperAction(DexMethod method, ProgramMethod from) {
queue.add(new MarkReachableSuperAction(method, from));
}
- public void enqueueMarkReachableFieldAction(ProgramField field, KeepReason reason) {
- queue.add(new MarkReachableFieldAction(field, reason));
+ public void enqueueMarkReachableFieldAction(
+ ProgramField field, ProgramDefinition context, KeepReason reason) {
+ queue.add(new MarkReachableFieldAction(field, context, reason));
}
// TODO(b/142378367): Context is the containing method that is cause of the instantiation.
@@ -280,8 +292,8 @@
queue.add(new MarkInterfaceInstantiatedAction(clazz, reason));
}
- void enqueueMarkMethodLiveAction(ProgramMethod method, KeepReason reason) {
- queue.add(new MarkMethodLiveAction(method, reason));
+ void enqueueMarkMethodLiveAction(ProgramMethod method, ProgramDefinition context) {
+ queue.add(new MarkMethodLiveAction(method, context));
}
void enqueueMarkMethodKeptAction(ProgramMethod method, KeepReason reason) {
diff --git a/src/main/java/com/android/tools/r8/shaking/FieldAccessInfoCollectionModifier.java b/src/main/java/com/android/tools/r8/shaking/FieldAccessInfoCollectionModifier.java
index 4e9d977..f656a56 100644
--- a/src/main/java/com/android/tools/r8/shaking/FieldAccessInfoCollectionModifier.java
+++ b/src/main/java/com/android/tools/r8/shaking/FieldAccessInfoCollectionModifier.java
@@ -6,91 +6,57 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.FieldAccessInfoCollectionImpl;
import com.android.tools.r8.graph.FieldAccessInfoImpl;
import com.android.tools.r8.graph.ProgramMethod;
-import java.util.ArrayList;
-import java.util.Collection;
+import com.android.tools.r8.utils.collections.ProgramMethodSet;
import java.util.IdentityHashMap;
-import java.util.List;
import java.util.Map;
-import java.util.function.BiConsumer;
-import java.util.function.Function;
public class FieldAccessInfoCollectionModifier {
static class FieldReferences {
- final List<DexMethod> writeContexts = new ArrayList<>();
- final List<DexMethod> readContexts = new ArrayList<>();
-
- void fixUpMethods(List<DexMethod> methods, Function<DexMethod, DexMethod> fixUpMethod) {
- for (int i = 0; i < methods.size(); i++) {
- DexMethod method = methods.get(i);
- DexMethod newMethod = fixUpMethod.apply(method);
- if (method != newMethod) {
- methods.set(i, newMethod);
- }
- }
- }
-
- void fixUp(Function<DexMethod, DexMethod> fixUpMethod) {
- fixUpMethods(writeContexts, fixUpMethod);
- fixUpMethods(readContexts, fixUpMethod);
- }
+ private final ProgramMethodSet writeContexts = ProgramMethodSet.create();
+ private final ProgramMethodSet readContexts = ProgramMethodSet.create();
}
- final Map<DexField, FieldReferences> newFieldAccesses;
+ private final Map<DexField, FieldReferences> newFieldAccesses;
FieldAccessInfoCollectionModifier(Map<DexField, FieldReferences> newFieldAccesses) {
this.newFieldAccesses = newFieldAccesses;
}
- void forEachFieldAccess(
- AppView<?> appView,
- Collection<DexMethod> methods,
- DexField field,
- BiConsumer<DexField, ProgramMethod> record) {
- for (DexMethod method : methods) {
- ProgramMethod programMethod =
- appView.definitionFor(method.holder).asProgramClass().lookupProgramMethod(method);
- record.accept(field, programMethod);
- }
- }
-
public void modify(AppView<AppInfoWithLiveness> appView) {
FieldAccessInfoCollectionImpl impl = appView.appInfo().getMutableFieldAccessInfoCollection();
newFieldAccesses.forEach(
(field, info) -> {
FieldAccessInfoImpl fieldAccessInfo = new FieldAccessInfoImpl(field);
- forEachFieldAccess(appView, info.readContexts, field, fieldAccessInfo::recordRead);
- forEachFieldAccess(appView, info.writeContexts, field, fieldAccessInfo::recordWrite);
+ info.readContexts.forEach(context -> fieldAccessInfo.recordRead(field, context));
+ info.writeContexts.forEach(context -> fieldAccessInfo.recordWrite(field, context));
impl.extend(field, fieldAccessInfo);
});
}
public static class Builder {
- final Map<DexField, FieldReferences> newFieldAccesses = new IdentityHashMap<>();
+
+ private final Map<DexField, FieldReferences> newFieldAccesses = new IdentityHashMap<>();
public Builder() {}
- public FieldAccessInfoCollectionModifier build(Function<DexMethod, DexMethod> fixupMethod) {
- for (FieldReferences fieldReference : newFieldAccesses.values()) {
- fieldReference.fixUp(fixupMethod);
- }
- return new FieldAccessInfoCollectionModifier(newFieldAccesses);
- }
-
- FieldReferences getFieldReferences(DexField field) {
+ private FieldReferences getFieldReferences(DexField field) {
return newFieldAccesses.computeIfAbsent(field, ignore -> new FieldReferences());
}
- public void fieldReadByMethod(DexField field, DexMethod method) {
- getFieldReferences(field).readContexts.add(method);
+ public void recordFieldReadInContext(DexField field, ProgramMethod context) {
+ getFieldReferences(field).readContexts.add(context);
}
- public void fieldWrittenByMethod(DexField field, DexMethod method) {
- getFieldReferences(field).writeContexts.add(method);
+ public void recordFieldWrittenInContext(DexField field, ProgramMethod context) {
+ getFieldReferences(field).writeContexts.add(context);
+ }
+
+ public FieldAccessInfoCollectionModifier build() {
+ return new FieldAccessInfoCollectionModifier(newFieldAccesses);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
index d78c808..86b0d08 100644
--- a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
+++ b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
@@ -26,6 +26,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
@@ -202,6 +203,18 @@
}
public KeepReasonWitness reportClassReferencedFrom(
+ DexProgramClass clazz, ProgramDefinition context) {
+ if (context.isProgramClass()) {
+ return reportClassReferencedFrom(clazz, context.asProgramClass());
+ } else if (context.isProgramField()) {
+ return reportClassReferencedFrom(clazz, context.asProgramField());
+ } else {
+ assert context.isProgramMethod();
+ return reportClassReferencedFrom(clazz, context.asProgramMethod());
+ }
+ }
+
+ public KeepReasonWitness reportClassReferencedFrom(
DexProgramClass clazz, DexProgramClass implementer) {
if (keptGraphConsumer != null) {
ClassGraphNode source = getClassGraphNode(implementer.type);
diff --git a/src/main/java/com/android/tools/r8/shaking/InlineRule.java b/src/main/java/com/android/tools/r8/shaking/InlineRule.java
index 41b6b33..32412d7 100644
--- a/src/main/java/com/android/tools/r8/shaking/InlineRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/InlineRule.java
@@ -18,7 +18,10 @@
};
public enum Type {
- ALWAYS, FORCE, NEVER
+ ALWAYS,
+ FORCE,
+ NEVER,
+ NEVER_SINGLE_CALLER
}
public static class Builder extends ProguardConfigurationRule.Builder<InlineRule, Builder> {
@@ -127,6 +130,8 @@
return "forceinline";
case NEVER:
return "neverinline";
+ case NEVER_SINGLE_CALLER:
+ return "neversinglecaller";
}
throw new Unreachable("Unknown inline type " + type);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java b/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java
index 91f9bbc..6f7cade 100644
--- a/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java
+++ b/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java
@@ -22,7 +22,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.UseRegistry;
-import com.android.tools.r8.utils.BooleanBox;
+import com.android.tools.r8.utils.Box;
import java.util.Set;
import java.util.function.Consumer;
@@ -68,9 +68,12 @@
public static boolean hasReferencesOutsideFromCode(
AppInfoWithClassHierarchy appInfo, ProgramMethod method, Set<DexType> classes) {
+ return getFirstReferenceOutsideFromCode(appInfo, method, classes) != null;
+ }
- BooleanBox result = new BooleanBox();
-
+ public static DexProgramClass getFirstReferenceOutsideFromCode(
+ AppInfoWithClassHierarchy appInfo, ProgramMethod method, Set<DexType> classes) {
+ Box<DexProgramClass> result = new Box<>();
new MainDexDirectReferenceTracer(
appInfo,
type -> {
@@ -78,12 +81,11 @@
if (baseType.isClassType() && !classes.contains(baseType)) {
DexClass cls = appInfo.definitionFor(baseType);
if (cls != null && cls.isProgramClass()) {
- result.set(true);
+ result.set(cls.asProgramClass());
}
}
})
.runOnCode(method);
-
return result.get();
}
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexTracingResult.java b/src/main/java/com/android/tools/r8/shaking/MainDexTracingResult.java
index 6383473..3af323c 100644
--- a/src/main/java/com/android/tools/r8/shaking/MainDexTracingResult.java
+++ b/src/main/java/com/android/tools/r8/shaking/MainDexTracingResult.java
@@ -6,8 +6,10 @@
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.utils.SetUtils;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.util.Collection;
@@ -23,11 +25,29 @@
public static class Builder {
public final AppInfo appInfo;
- public final Set<DexType> roots = Sets.newIdentityHashSet();
- public final Set<DexType> dependencies = Sets.newIdentityHashSet();
+ public final Set<DexType> roots;
+ public final Set<DexType> dependencies;
private Builder(AppInfo appInfo) {
+ this(appInfo, Sets.newIdentityHashSet(), Sets.newIdentityHashSet());
+ }
+
+ private Builder(AppInfo appInfo, MainDexTracingResult mainDexTracingResult) {
+ this(
+ appInfo,
+ SetUtils.newIdentityHashSet(mainDexTracingResult.getRoots()),
+ SetUtils.newIdentityHashSet(mainDexTracingResult.getDependencies()));
+ }
+
+ private Builder(AppInfo appInfo, Set<DexType> roots, Set<DexType> dependencies) {
this.appInfo = appInfo;
+ this.roots = roots;
+ this.dependencies = dependencies;
+ }
+
+ public Builder addRoot(DexProgramClass clazz) {
+ roots.add(clazz.getType());
+ return this;
}
public Builder addRoot(DexType type) {
@@ -141,4 +161,8 @@
public static Builder builder(AppInfo appInfo) {
return new Builder(appInfo);
}
+
+ public Builder extensionBuilder(AppInfo appInfo) {
+ return new Builder(appInfo, this);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/shaking/MissingClasses.java b/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
new file mode 100644
index 0000000..d3cd164
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
@@ -0,0 +1,138 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.synthesis.CommittedItems;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+import java.util.Collection;
+import java.util.Set;
+
+public class MissingClasses {
+
+ private final Set<DexType> missingClasses;
+
+ private MissingClasses(Set<DexType> missingClasses) {
+ this.missingClasses = missingClasses;
+ }
+
+ public Builder builder() {
+ return new Builder(missingClasses);
+ }
+
+ public static Builder builderForInitialMissingClasses() {
+ return new Builder();
+ }
+
+ public static MissingClasses empty() {
+ return new MissingClasses(Sets.newIdentityHashSet());
+ }
+
+ public MissingClasses commitSyntheticItems(CommittedItems committedItems) {
+ return builder()
+ // TODO(b/175542052): Synthetic types should not be reported as missing in the first place.
+ .removeAlreadyMissingClasses(committedItems.getLegacySyntheticTypes())
+ .ignoreMissingClasses();
+ }
+
+ public boolean contains(DexType type) {
+ return missingClasses.contains(type);
+ }
+
+ public static class Builder {
+
+ private final Set<DexType> alreadyMissingClasses;
+ private final Set<DexType> newMissingClasses = Sets.newIdentityHashSet();
+
+ // Set of missing types that are not to be reported as missing. This does not hide reports
+ // if the same type is in newMissingClasses in which case it is reported regardless.
+ private final Set<DexType> newIgnoredMissingClasses = Sets.newIdentityHashSet();
+
+ private Builder() {
+ this(Sets.newIdentityHashSet());
+ }
+
+ private Builder(Set<DexType> alreadyMissingClasses) {
+ this.alreadyMissingClasses = alreadyMissingClasses;
+ }
+
+ public void addNewMissingClass(DexType type) {
+ newMissingClasses.add(type);
+ }
+
+ public Builder addNewMissingClasses(Collection<DexType> types) {
+ newMissingClasses.addAll(types);
+ return this;
+ }
+
+ public void ignoreNewMissingClass(DexType type) {
+ newIgnoredMissingClasses.add(type);
+ }
+
+ public boolean contains(DexType type) {
+ return alreadyMissingClasses.contains(type) || newMissingClasses.contains(type);
+ }
+
+ Builder removeAlreadyMissingClasses(Iterable<DexType> types) {
+ for (DexType type : types) {
+ alreadyMissingClasses.remove(type);
+ }
+ return this;
+ }
+
+ @Deprecated
+ public MissingClasses ignoreMissingClasses() {
+ return build();
+ }
+
+ public MissingClasses reportMissingClasses(InternalOptions options) {
+ Set<DexType> newMissingClassesWithoutDontWarn =
+ options.getProguardConfiguration().getDontWarnPatterns().getNonMatches(newMissingClasses);
+ newMissingClassesWithoutDontWarn.removeAll(
+ getAllowedMissingClasses(options.dexItemFactory()));
+ if (!newMissingClassesWithoutDontWarn.isEmpty()) {
+ MissingClassesDiagnostic diagnostic =
+ new MissingClassesDiagnostic.Builder()
+ .addMissingClasses(newMissingClassesWithoutDontWarn)
+ .setFatal(!options.ignoreMissingClasses)
+ .build();
+ if (options.ignoreMissingClasses) {
+ options.reporter.warning(diagnostic);
+ } else {
+ throw options.reporter.fatalError(diagnostic);
+ }
+ }
+ return build();
+ }
+
+ private static Collection<DexType> getAllowedMissingClasses(DexItemFactory dexItemFactory) {
+ return ImmutableList.of(dexItemFactory.annotationThrows);
+ }
+
+ /** Intentionally private, use {@link Builder#reportMissingClasses(InternalOptions)}. */
+ private MissingClasses build() {
+ // Extend the newMissingClasses set with all other missing classes.
+ //
+ // We also add newIgnoredMissingClasses to newMissingClasses to be able to assert that we have
+ // a closed world after the first round of tree shaking: we should never lookup a class that
+ // was not live or missing during the first round of tree shaking.
+ // See also AppInfoWithLiveness.definitionFor().
+ //
+ // Note: At this point, all missing classes in newMissingClasses have already been reported.
+ // Thus adding newIgnoredMissingClasses to newMissingClasses will not lead to reports for the
+ // classes in newIgnoredMissingClasses.
+ newMissingClasses.addAll(alreadyMissingClasses);
+ newMissingClasses.addAll(newIgnoredMissingClasses);
+ return new MissingClasses(newMissingClasses);
+ }
+
+ public boolean wasAlreadyMissing(DexType type) {
+ return alreadyMissingClasses.contains(type);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/MissingClassesDiagnostic.java b/src/main/java/com/android/tools/r8/shaking/MissingClassesDiagnostic.java
new file mode 100644
index 0000000..5965cc5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/MissingClassesDiagnostic.java
@@ -0,0 +1,106 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.google.common.collect.ImmutableSortedSet;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.SortedSet;
+
+@Keep
+public class MissingClassesDiagnostic implements Diagnostic {
+
+ private final boolean fatal;
+ private final SortedSet<ClassReference> missingClasses;
+
+ private MissingClassesDiagnostic(boolean fatal, SortedSet<ClassReference> missingClasses) {
+ assert !missingClasses.isEmpty();
+ this.fatal = fatal;
+ this.missingClasses = missingClasses;
+ }
+
+ public Set<ClassReference> getMissingClasses() {
+ return missingClasses;
+ }
+
+ /** A missing class(es) failure can generally not be attributed to a single origin. */
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+
+ /** A missing class(es) failure can generally not be attributed to a single position. */
+ @Override
+ public Position getPosition() {
+ return Position.UNKNOWN;
+ }
+
+ // TODO(b/175755807): Extend diagnostic message with contextual information.
+ @Override
+ public String getDiagnosticMessage() {
+ return fatal ? getFatalDiagnosticMessage() : getNonFatalDiagnosticMessage();
+ }
+
+ private String getFatalDiagnosticMessage() {
+ if (missingClasses.size() == 1) {
+ return "Compilation can't be completed because the class "
+ + missingClasses.iterator().next().getTypeName()
+ + " is missing.";
+ }
+ StringBuilder builder =
+ new StringBuilder("Compilation can't be completed because the following ")
+ .append(missingClasses.size())
+ .append(" classes are missing:");
+ for (ClassReference missingClass : missingClasses) {
+ builder.append(System.lineSeparator()).append("- ").append(missingClass.getTypeName());
+ }
+ return builder.toString();
+ }
+
+ private String getNonFatalDiagnosticMessage() {
+ StringBuilder builder = new StringBuilder();
+ Iterator<ClassReference> missingClassesIterator = missingClasses.iterator();
+ while (missingClassesIterator.hasNext()) {
+ ClassReference missingClass = missingClassesIterator.next();
+ builder.append("Missing class ").append(missingClass.getTypeName());
+ if (missingClassesIterator.hasNext()) {
+ builder.append(System.lineSeparator());
+ }
+ }
+ return builder.toString();
+ }
+
+ public static class Builder {
+
+ private boolean fatal;
+ private ImmutableSortedSet.Builder<ClassReference> missingClassesBuilder =
+ ImmutableSortedSet.orderedBy(Comparator.comparing(ClassReference::getDescriptor));
+
+ public MissingClassesDiagnostic.Builder addMissingClasses(Collection<DexType> missingClasses) {
+ for (DexType missingClass : missingClasses) {
+ missingClassesBuilder.add(Reference.classFromDescriptor(missingClass.toDescriptorString()));
+ }
+ return this;
+ }
+
+ public MissingClassesDiagnostic.Builder setFatal(boolean fatal) {
+ this.fatal = fatal;
+ return this;
+ }
+
+ public MissingClassesDiagnostic build() {
+ return new MissingClassesDiagnostic(fatal, missingClassesBuilder.build());
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassFilter.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassFilter.java
index c023594..d63374c 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassFilter.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassFilter.java
@@ -4,9 +4,13 @@
package com.android.tools.r8.shaking;
+import static com.android.tools.r8.utils.PredicateUtils.not;
+
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.shaking.ProguardTypeMatcher.MatchSpecificType;
+import com.android.tools.r8.utils.TraversalContinuation;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
import java.util.Set;
public class ProguardClassFilter {
@@ -55,6 +59,33 @@
return false;
}
+ public Set<DexType> getNonMatches(Set<DexType> types) {
+ Set<DexType> nonMatches = Sets.newIdentityHashSet();
+ for (DexType type : types) {
+ TraversalContinuation traversalContinuation = TraversalContinuation.CONTINUE;
+ for (ProguardClassNameList pattern : patterns) {
+ traversalContinuation =
+ pattern.traverseTypeMatchers(
+ matcher -> {
+ if (matcher.matches(type)) {
+ return TraversalContinuation.BREAK;
+ }
+ return TraversalContinuation.CONTINUE;
+ },
+ not(ProguardTypeMatcher::hasSpecificType));
+ }
+ if (traversalContinuation.shouldContinue()) {
+ nonMatches.add(type);
+ }
+ }
+ for (ProguardClassNameList pattern : patterns) {
+ pattern.forEachTypeMatcher(
+ matcher -> nonMatches.remove(matcher.getSpecificType()),
+ ProguardTypeMatcher::hasSpecificType);
+ }
+ return nonMatches;
+ }
+
public void filterOutMatches(Set<DexType> types) {
for (ProguardClassNameList pattern : patterns) {
pattern.forEachTypeMatcher(matcher -> {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
index ae80f4c..8ffaa8e 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.TraversalContinuation;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap;
@@ -15,6 +16,8 @@
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
@@ -101,6 +104,31 @@
public abstract void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer);
+ public final void forEachTypeMatcher(
+ Consumer<ProguardTypeMatcher> consumer, Predicate<ProguardTypeMatcher> predicate) {
+ forEachTypeMatcher(
+ matcher -> {
+ if (predicate.test(matcher)) {
+ consumer.accept(matcher);
+ }
+ });
+ }
+
+ public abstract TraversalContinuation traverseTypeMatchers(
+ Function<ProguardTypeMatcher, TraversalContinuation> fn);
+
+ public final TraversalContinuation traverseTypeMatchers(
+ Function<ProguardTypeMatcher, TraversalContinuation> fn,
+ Predicate<ProguardTypeMatcher> predicate) {
+ return traverseTypeMatchers(
+ matcher -> {
+ if (predicate.test(matcher)) {
+ return fn.apply(matcher);
+ }
+ return TraversalContinuation.CONTINUE;
+ });
+ }
+
private static class EmptyClassNameList extends ProguardClassNameList {
private EmptyClassNameList() {
@@ -138,6 +166,12 @@
@Override
public void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer) {
}
+
+ @Override
+ public TraversalContinuation traverseTypeMatchers(
+ Function<ProguardTypeMatcher, TraversalContinuation> fn) {
+ return TraversalContinuation.CONTINUE;
+ }
}
static class SingleClassNameList extends ProguardClassNameList {
@@ -200,6 +234,12 @@
public void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer) {
consumer.accept(className);
}
+
+ @Override
+ public TraversalContinuation traverseTypeMatchers(
+ Function<ProguardTypeMatcher, TraversalContinuation> fn) {
+ return fn.apply(className);
+ }
}
private static class PositiveClassNameList extends ProguardClassNameList {
@@ -278,6 +318,17 @@
public void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer) {
classNames.forEach(consumer);
}
+
+ @Override
+ public TraversalContinuation traverseTypeMatchers(
+ Function<ProguardTypeMatcher, TraversalContinuation> fn) {
+ for (ProguardTypeMatcher matcher : classNames) {
+ if (fn.apply(matcher).shouldBreak()) {
+ return TraversalContinuation.BREAK;
+ }
+ }
+ return TraversalContinuation.CONTINUE;
+ }
}
private static class MixedClassNameList extends ProguardClassNameList {
@@ -363,5 +414,16 @@
public void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer) {
classNames.object2BooleanEntrySet().forEach(entry -> consumer.accept(entry.getKey()));
}
+
+ @Override
+ public TraversalContinuation traverseTypeMatchers(
+ Function<ProguardTypeMatcher, TraversalContinuation> fn) {
+ for (ProguardTypeMatcher matcher : classNames.keySet()) {
+ if (fn.apply(matcher).shouldBreak()) {
+ return TraversalContinuation.BREAK;
+ }
+ }
+ return TraversalContinuation.CONTINUE;
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index f71d027..8294be0 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -469,6 +469,11 @@
configurationBuilder.addRule(rule);
return true;
}
+ if (acceptString("neversinglecallerinline")) {
+ InlineRule rule = parseInlineRule(InlineRule.Type.NEVER_SINGLE_CALLER, optionStart);
+ configurationBuilder.addRule(rule);
+ return true;
+ }
if (acceptString(NoUnusedInterfaceRemovalRule.RULE_NAME)) {
ProguardConfigurationRule rule = parseNoUnusedInterfaceRemovalRule(optionStart);
configurationBuilder.addRule(rule);
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index be431d3..7f18fff 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -28,6 +28,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.ProgramMember;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.SubtypingInfo;
@@ -91,6 +92,7 @@
private final Set<DexMethod> alwaysInline = Sets.newIdentityHashSet();
private final Set<DexMethod> forceInline = Sets.newIdentityHashSet();
private final Set<DexMethod> neverInline = Sets.newIdentityHashSet();
+ private final Set<DexMethod> neverInlineDueToSingleCaller = Sets.newIdentityHashSet();
private final Set<DexMethod> bypassClinitforInlining = Sets.newIdentityHashSet();
private final Set<DexMethod> whyAreYouNotInlining = Sets.newIdentityHashSet();
private final Set<DexMethod> keepParametersWithConstantValue = Sets.newIdentityHashSet();
@@ -347,6 +349,7 @@
alwaysInline,
forceInline,
neverInline,
+ neverInlineDueToSingleCaller,
bypassClinitforInlining,
whyAreYouNotInlining,
keepParametersWithConstantValue,
@@ -431,6 +434,7 @@
ConsequentRootSet buildConsequentRootSet() {
return new ConsequentRootSet(
neverInline,
+ neverInlineDueToSingleCaller,
neverClassInline,
noShrinking,
softPinned,
@@ -1198,15 +1202,19 @@
context.markAsUsed();
} else if (context instanceof InlineRule) {
if (item.isDexEncodedMethod()) {
+ DexMethod reference = item.asDexEncodedMethod().getReference();
switch (((InlineRule) context).getType()) {
case ALWAYS:
- alwaysInline.add(item.asDexEncodedMethod().method);
+ alwaysInline.add(reference);
break;
case FORCE:
- forceInline.add(item.asDexEncodedMethod().method);
+ forceInline.add(reference);
break;
case NEVER:
- neverInline.add(item.asDexEncodedMethod().method);
+ neverInline.add(reference);
+ break;
+ case NEVER_SINGLE_CALLER:
+ neverInlineDueToSingleCaller.add(reference);
break;
default:
throw new Unreachable();
@@ -1331,6 +1339,7 @@
abstract static class RootSetBase {
final Set<DexMethod> neverInline;
+ final Set<DexMethod> neverInlineDueToSingleCaller;
final Set<DexType> neverClassInline;
final MutableItemsWithRules noShrinking;
final MutableItemsWithRules softPinned;
@@ -1342,6 +1351,7 @@
RootSetBase(
Set<DexMethod> neverInline,
+ Set<DexMethod> neverInlineDueToSingleCaller,
Set<DexType> neverClassInline,
MutableItemsWithRules noShrinking,
MutableItemsWithRules softPinned,
@@ -1351,6 +1361,7 @@
Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule,
List<DelayedRootSetActionItem> delayedRootSetActionItems) {
this.neverInline = neverInline;
+ this.neverInlineDueToSingleCaller = neverInlineDueToSingleCaller;
this.neverClassInline = neverClassInline;
this.noShrinking = noShrinking;
this.softPinned = softPinned;
@@ -1414,14 +1425,14 @@
public void forEachDependentMember(
DexDefinition item,
AppView<?> appView,
- Consumer3<DexDefinition, DexEncodedMember<?, ?>, Set<ProguardKeepRuleBase>> fn) {
+ Consumer3<DexDefinition, ProgramMember<?, ?>, Set<ProguardKeepRuleBase>> fn) {
getDependentItems(item)
.forEachMember(
(reference, reasons) -> {
DexProgramClass holder =
asProgramClassOrNull(appView.definitionForHolder(reference));
if (holder != null) {
- DexEncodedMember<?, ?> member = holder.lookupMember(reference);
+ ProgramMember<?, ?> member = holder.lookupProgramMember(reference);
if (member != null) {
fn.accept(item, member, reasons);
}
@@ -1432,12 +1443,12 @@
public void forEachDependentNonStaticMember(
DexDefinition item,
AppView<?> appView,
- Consumer3<DexDefinition, DexEncodedMember<?, ?>, Set<ProguardKeepRuleBase>> fn) {
+ Consumer3<DexDefinition, ProgramMember<?, ?>, Set<ProguardKeepRuleBase>> fn) {
forEachDependentMember(
item,
appView,
(precondition, member, reasons) -> {
- if (!member.isStatic()) {
+ if (!member.getDefinition().isStatic()) {
fn.accept(precondition, member, reasons);
}
});
@@ -1446,12 +1457,12 @@
public void forEachDependentStaticMember(
DexDefinition item,
AppView<?> appView,
- Consumer3<DexDefinition, DexEncodedMember<?, ?>, Set<ProguardKeepRuleBase>> fn) {
+ Consumer3<DexDefinition, ProgramMember<?, ?>, Set<ProguardKeepRuleBase>> fn) {
forEachDependentMember(
item,
appView,
(precondition, member, reasons) -> {
- if (member.isStatic()) {
+ if (member.getDefinition().isStatic()) {
fn.accept(precondition, member, reasons);
}
});
@@ -1777,6 +1788,7 @@
Set<DexMethod> alwaysInline,
Set<DexMethod> forceInline,
Set<DexMethod> neverInline,
+ Set<DexMethod> neverInlineDueToSingleCaller,
Set<DexMethod> bypassClinitForInlining,
Set<DexMethod> whyAreYouNotInlining,
Set<DexMethod> keepConstantArguments,
@@ -1801,6 +1813,7 @@
List<DelayedRootSetActionItem> delayedRootSetActionItems) {
super(
neverInline,
+ neverInlineDueToSingleCaller,
neverClassInline,
noShrinking,
softPinned,
@@ -1850,6 +1863,7 @@
void addConsequentRootSet(ConsequentRootSet consequentRootSet, boolean addNoShrinking) {
neverInline.addAll(consequentRootSet.neverInline);
+ neverInlineDueToSingleCaller.addAll(consequentRootSet.neverInlineDueToSingleCaller);
neverClassInline.addAll(consequentRootSet.neverClassInline);
noObfuscation.addAll(consequentRootSet.noObfuscation);
if (addNoShrinking) {
@@ -2107,6 +2121,7 @@
ConsequentRootSet(
Set<DexMethod> neverInline,
+ Set<DexMethod> neverInlineDueToSingleCaller,
Set<DexType> neverClassInline,
MutableItemsWithRules noShrinking,
MutableItemsWithRules softPinned,
@@ -2117,6 +2132,7 @@
List<DelayedRootSetActionItem> delayedRootSetActionItems) {
super(
neverInline,
+ neverInlineDueToSingleCaller,
neverClassInline,
noShrinking,
softPinned,
diff --git a/src/main/java/com/android/tools/r8/shaking/SingleTargetLookupCache.java b/src/main/java/com/android/tools/r8/shaking/SingleTargetLookupCache.java
index a2f246e..df9803a 100644
--- a/src/main/java/com/android/tools/r8/shaking/SingleTargetLookupCache.java
+++ b/src/main/java/com/android/tools/r8/shaking/SingleTargetLookupCache.java
@@ -35,9 +35,9 @@
instance ->
appInfo.traverseSuperTypes(
instance,
- (type, ignore) -> {
- if (seen.add(type)) {
- cache.remove(type);
+ (superType, subclass, ignore) -> {
+ if (seen.add(superType)) {
+ cache.remove(superType);
return TraversalContinuation.CONTINUE;
} else {
return TraversalContinuation.BREAK;
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index 9d0b174..9155165 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -36,7 +36,6 @@
private final AppView<AppInfoWithLiveness> appView;
private final TreePrunerConfiguration configuration;
private final UnusedItemsPrinter unusedItemsPrinter;
- private final Set<DexType> missingTypes;
private final Set<DexType> prunedTypes = Sets.newIdentityHashSet();
private final Set<DexMethod> methodsToKeepForConfigurationDebugging = Sets.newIdentityHashSet();
@@ -48,7 +47,6 @@
InternalOptions options = appView.options();
this.appView = appView;
this.configuration = configuration;
- this.missingTypes = appView.appInfo().getMissingTypes();
this.unusedItemsPrinter =
options.hasUsageInformationConsumer()
? new UnusedItemsPrinter(
@@ -207,7 +205,7 @@
}
private boolean isTypeMissing(DexType type) {
- return missingTypes.contains(type);
+ return appView.appInfo().getMissingClasses().contains(type);
}
private boolean isTypeLive(DexType type) {
diff --git a/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java b/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java
index f6a6245..424fd8d 100644
--- a/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java
@@ -60,6 +60,11 @@
return committedTypes;
}
+ @Deprecated
+ public Collection<DexType> getLegacySyntheticTypes() {
+ return legacySyntheticTypes;
+ }
+
@Override
public DexClass definitionFor(DexType type, Function<DexType, DexClass> baseDefinitionFor) {
// All synthetic types are committed to the application so lookup is just the base lookup.
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 fe3388b..985090a 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -256,6 +256,8 @@
public boolean enableInliningOfInvokesWithClassInitializationSideEffects = true;
public boolean enableInliningOfInvokesWithNullableReceivers = true;
public boolean disableInliningOfLibraryMethodOverrides = true;
+ public boolean enableSimpleInliningConstraints = true;
+ public int simpleInliningConstraintThreshold = 0;
public boolean enableClassInlining = true;
public boolean enableClassStaticizer = true;
public boolean enableInitializedClassesAnalysis = true;
@@ -1362,6 +1364,10 @@
public static class TestingOptions {
+ public static void enableExperimentalMissingClassesReporting(InternalOptions options) {
+ options.testing.enableExperimentalMissingClassesReporting = true;
+ }
+
public static int NO_LIMIT = -1;
// Force writing the specified bytes as the DEX version content.
@@ -1431,6 +1437,7 @@
public boolean alwaysUsePessimisticRegisterAllocation = false;
public boolean enableCheckCastAndInstanceOfRemoval = true;
public boolean enableDeadSwitchCaseElimination = true;
+ public boolean enableExperimentalMissingClassesReporting = false;
public boolean enableInvokeSuperToInvokeVirtualRewriting = true;
public boolean enableSwitchToIfRewriting = true;
public boolean enableEnumUnboxingDebugLogs = false;
diff --git a/src/main/java/com/android/tools/r8/utils/IterableUtils.java b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
index 871a7a5..ebbef95 100644
--- a/src/main/java/com/android/tools/r8/utils/IterableUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
@@ -29,6 +29,15 @@
return Collections.unmodifiableList(list);
}
+ public static <T> T findOrDefault(Iterable<T> iterable, Predicate<T> predicate, T defaultValue) {
+ for (T element : iterable) {
+ if (predicate.test(element)) {
+ return element;
+ }
+ }
+ return defaultValue;
+ }
+
public static <T> int firstIndexMatching(Iterable<T> iterable, Predicate<T> tester) {
int i = 0;
for (T element : iterable) {
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
index d3c376f..40ae9ce 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -48,6 +48,34 @@
return result;
}
+ /**
+ * Rewrites the input list based on the given function. Returns the mapped list if any elements
+ * were rewritten, otherwise returns the original list.
+ */
+ public static <T> List<T> mapOrElse(List<T> list, Function<T, T> fn, List<T> defaultValue) {
+ ArrayList<T> result = null;
+ for (int i = 0; i < list.size(); i++) {
+ T oldElement = list.get(i);
+ T newElement = fn.apply(oldElement);
+ if (newElement == oldElement) {
+ if (result != null) {
+ result.add(oldElement);
+ }
+ } else {
+ if (result == null) {
+ result = new ArrayList<>(list.size());
+ for (int j = 0; j < i; j++) {
+ result.add(list.get(j));
+ }
+ }
+ if (newElement != null) {
+ result.add(newElement);
+ }
+ }
+ }
+ return result != null ? result : defaultValue;
+ }
+
public static <T> Optional<T> removeFirstMatch(List<T> list, Predicate<T> element) {
int index = firstIndexMatching(list, element);
if (index >= 0) {
diff --git a/src/main/java/com/android/tools/r8/utils/SetUtils.java b/src/main/java/com/android/tools/r8/utils/SetUtils.java
index 89880cc..c67225c 100644
--- a/src/main/java/com/android/tools/r8/utils/SetUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/SetUtils.java
@@ -18,6 +18,12 @@
return result;
}
+ public static <T> Set<T> newIdentityHashSet(T[] elements) {
+ Set<T> result = Sets.newIdentityHashSet();
+ Collections.addAll(result, elements);
+ return result;
+ }
+
public static <T> Set<T> newIdentityHashSet(Iterable<T> c) {
Set<T> result = Sets.newIdentityHashSet();
c.forEach(result::add);
diff --git a/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java b/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java
index cbc5c01..d8e0196 100644
--- a/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java
+++ b/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java
@@ -89,15 +89,11 @@
try {
// java.util.concurrent.Flow$Subscriber is not present in JDK8 rt.jar.
testBuilder.compileWithExpectedDiagnostics(
- diagnotics -> {
- diagnotics.assertErrorsCount(1);
- diagnotics.assertWarningsCount(1);
- diagnotics.assertInfosCount(0);
+ diagnostics -> {
+ diagnostics.assertOnlyErrors();
+ diagnostics.assertErrorsCount(1);
assertThat(
- diagnotics.getErrors().get(0).getDiagnosticMessage(),
- StringContains.containsString("java.util.concurrent.Flow$Subscriber"));
- assertThat(
- diagnotics.getWarnings().get(0).getDiagnosticMessage(),
+ diagnostics.getErrors().get(0).getDiagnosticMessage(),
StringContains.containsString("java.util.concurrent.Flow$Subscriber"));
});
} catch (CompilationFailedException e) {
diff --git a/src/test/java/com/android/tools/r8/NeverInline.java b/src/test/java/com/android/tools/r8/NeverInline.java
index ca0c2a4..cfbb09f 100644
--- a/src/test/java/com/android/tools/r8/NeverInline.java
+++ b/src/test/java/com/android/tools/r8/NeverInline.java
@@ -4,7 +4,10 @@
package com.android.tools.r8;
import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+@Retention(RetentionPolicy.CLASS)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface NeverInline {}
diff --git a/src/test/java/com/android/tools/r8/NeverSingleCallerInline.java b/src/test/java/com/android/tools/r8/NeverSingleCallerInline.java
new file mode 100644
index 0000000..8c710fb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/NeverSingleCallerInline.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.CLASS)
+@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
+public @interface NeverSingleCallerInline {}
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 34d573a..e831d72 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -57,21 +57,6 @@
private AllowedDiagnosticMessages allowedDiagnosticMessages = AllowedDiagnosticMessages.NONE;
private boolean allowUnusedProguardConfigurationRules = false;
- private boolean enableAssumeNoSideEffectsAnnotations = false;
- private boolean enableConstantArgumentAnnotations = false;
- private boolean enableInliningAnnotations = false;
- private boolean enableMemberValuePropagationAnnotations = false;
- private boolean enableNoUnusedInterfaceRemovalAnnotations = false;
- private boolean enableNoVerticalClassMergingAnnotations = false;
- private boolean enableNoHorizontalClassMergingAnnotations = false;
- private boolean enableNoStaticClassMergingAnnotations = false;
- private boolean enableNeverClassInliningAnnotations = false;
- private boolean enableNeverReprocessClassInitializerAnnotations = false;
- private boolean enableNeverReprocessMethodAnnotations = false;
- private boolean enableReprocessClassInitializerAnnotations = false;
- private boolean enableReprocessMethodAnnotations = false;
- private boolean enableSideEffectAnnotations = false;
- private boolean enableUnusedArgumentAnnotations = false;
private CollectingGraphConsumer graphConsumer = null;
private List<String> keepRules = new ArrayList<>();
private List<Path> mainDexRulesFiles = new ArrayList<>();
@@ -82,22 +67,6 @@
R8TestCompileResult internalCompile(
Builder builder, Consumer<InternalOptions> optionsConsumer, Supplier<AndroidApp> app)
throws CompilationFailedException {
- if (enableConstantArgumentAnnotations
- || enableInliningAnnotations
- || enableMemberValuePropagationAnnotations
- || enableNoUnusedInterfaceRemovalAnnotations
- || enableNoVerticalClassMergingAnnotations
- || enableNoHorizontalClassMergingAnnotations
- || enableNoStaticClassMergingAnnotations
- || enableNeverClassInliningAnnotations
- || enableNeverReprocessClassInitializerAnnotations
- || enableNeverReprocessMethodAnnotations
- || enableReprocessClassInitializerAnnotations
- || enableReprocessMethodAnnotations
- || enableSideEffectAnnotations
- || enableUnusedArgumentAnnotations) {
- ToolHelper.allowTestProguardOptions(builder);
- }
if (!keepRules.isEmpty()) {
builder.addProguardConfiguration(keepRules, Origin.unknown());
}
@@ -357,75 +326,62 @@
}
public T enableAlwaysInliningAnnotations() {
- return enableAlwaysInliningAnnotations(AlwaysInline.class.getPackage().getName());
+ return addAlwaysInliningAnnotations()
+ .enableAlwaysInliningAnnotations(AlwaysInline.class.getPackage().getName());
}
public T enableAlwaysInliningAnnotations(String annotationPackageName) {
- if (!enableInliningAnnotations) {
- enableInliningAnnotations = true;
- addInternalKeepRules(
- "-alwaysinline class * { @" + annotationPackageName + ".AlwaysInline *; }");
- }
- return self();
+ return addInternalKeepRules(
+ "-alwaysinline class * { @" + annotationPackageName + ".AlwaysInline *; }");
}
public T enableAssumeNoSideEffectsAnnotations() {
- return enableAssumeNoSideEffectsAnnotations(AssumeNoSideEffects.class.getPackage().getName());
+ return addAssumeNoSideEffectsAnnotations()
+ .enableAssumeNoSideEffectsAnnotations(AssumeNoSideEffects.class.getPackage().getName());
}
public T enableAssumeNoSideEffectsAnnotations(String annotationPackageName) {
- if (!enableAssumeNoSideEffectsAnnotations) {
- enableAssumeNoSideEffectsAnnotations = true;
- addInternalKeepRules(
- "-assumenosideeffects class * { @"
- + annotationPackageName
- + ".AssumeNoSideEffects <methods>; }");
- }
- return self();
+ return addInternalKeepRules(
+ "-assumenosideeffects class * { @"
+ + annotationPackageName
+ + ".AssumeNoSideEffects <methods>; }");
}
public T enableInliningAnnotations() {
- return enableInliningAnnotations(NeverInline.class.getPackage().getName());
+ return addInliningAnnotations()
+ .enableInliningAnnotations(NeverInline.class.getPackage().getName());
}
public T enableInliningAnnotations(String annotationPackageName) {
- if (!enableInliningAnnotations) {
- enableInliningAnnotations = true;
- addInternalKeepRules(
- "-neverinline class * { @" + annotationPackageName + ".NeverInline *; }");
- }
- return self();
+ return addInternalKeepRules(
+ "-neverinline class * { @" + annotationPackageName + ".NeverInline *; }");
}
public T enableForceInliningAnnotations() {
- return enableForceInliningAnnotations(ForceInline.class.getPackage().getName());
+ return addForceInliningAnnotations()
+ .enableForceInliningAnnotations(ForceInline.class.getPackage().getName());
}
public T enableForceInliningAnnotations(String annotationPackageName) {
- if (!enableInliningAnnotations) {
- enableInliningAnnotations = true;
- addInternalKeepRules(
- "-forceinline class * { @" + annotationPackageName + ".ForceInline *; }");
- }
- return self();
+ return addInternalKeepRules(
+ "-forceinline class * { @" + annotationPackageName + ".ForceInline *; }");
+ }
+
+ public T enableNeverSingleCallerInlineAnnotations() {
+ return addNeverSingleCallerInlineAnnotations()
+ .addInternalKeepRules(
+ "-neversinglecallerinline class * {",
+ " @com.android.tools.r8.NeverSingleCallerInline <methods>;",
+ "}");
}
public T enableNeverClassInliningAnnotations() {
- if (!enableNeverClassInliningAnnotations) {
- enableNeverClassInliningAnnotations = true;
- addInternalKeepRules("-neverclassinline @com.android.tools.r8.NeverClassInline class *");
- }
- return self();
+ return addNeverClassInliningAnnotations()
+ .addInternalKeepRules("-neverclassinline @com.android.tools.r8.NeverClassInline class *");
}
- private void addInternalMatchInterfaceRule(String name, Class matchInterface) {
- StringBuilder sb = new StringBuilder();
- sb.append("-");
- sb.append(name);
- sb.append(" @");
- sb.append(matchInterface.getTypeName());
- sb.append(" class *");
- addInternalKeepRules(sb.toString());
+ T addInternalMatchInterfaceRule(String name, Class<?> matchInterface) {
+ return addInternalKeepRules("-" + name + " @" + matchInterface.getTypeName() + " class *");
}
public T noClassInlining() {
@@ -467,42 +423,31 @@
}
public T enableNoUnusedInterfaceRemovalAnnotations() {
- if (!enableNoUnusedInterfaceRemovalAnnotations) {
- enableNoUnusedInterfaceRemovalAnnotations = true;
- addInternalMatchInterfaceRule(
- NoUnusedInterfaceRemovalRule.RULE_NAME, NoUnusedInterfaceRemoval.class);
- }
- return self();
+ return addNoUnusedInterfaceRemovalAnnotations()
+ .addInternalMatchInterfaceRule(
+ NoUnusedInterfaceRemovalRule.RULE_NAME, NoUnusedInterfaceRemoval.class);
}
public T enableNoVerticalClassMergingAnnotations() {
- if (!enableNoVerticalClassMergingAnnotations) {
- enableNoVerticalClassMergingAnnotations = true;
- addInternalMatchInterfaceRule(
- NoVerticalClassMergingRule.RULE_NAME, NoVerticalClassMerging.class);
- }
- return self();
- }
-
- public T addNoHorizontalClassMergingRule(String clazz) {
- return addKeepRules("-nohorizontalclassmerging class " + clazz);
+ return addNoVerticalClassMergingAnnotations()
+ .addInternalMatchInterfaceRule(
+ NoVerticalClassMergingRule.RULE_NAME, NoVerticalClassMerging.class);
}
public T enableNoHorizontalClassMergingAnnotations() {
- if (!enableNoHorizontalClassMergingAnnotations) {
- enableNoHorizontalClassMergingAnnotations = true;
- addInternalMatchInterfaceRule(
- NoHorizontalClassMergingRule.RULE_NAME, NoHorizontalClassMerging.class);
- }
- return self();
+ return addProgramClasses(NoHorizontalClassMerging.class)
+ .addInternalMatchInterfaceRule(
+ NoHorizontalClassMergingRule.RULE_NAME, NoHorizontalClassMerging.class);
+ }
+
+ public T addNoHorizontalClassMergingRule(String clazz) {
+ return addInternalKeepRules("-nohorizontalclassmerging class " + clazz);
}
public T enableNoStaticClassMergingAnnotations() {
- if (!enableNoStaticClassMergingAnnotations) {
- enableNoStaticClassMergingAnnotations = true;
- addInternalMatchInterfaceRule(NoStaticClassMergingRule.RULE_NAME, NoStaticClassMerging.class);
- }
- return self();
+ return addNoStaticClassMergingAnnotations()
+ .addInternalMatchInterfaceRule(
+ NoStaticClassMergingRule.RULE_NAME, NoStaticClassMerging.class);
}
public T enableMemberValuePropagationAnnotations() {
@@ -511,54 +456,40 @@
public T enableMemberValuePropagationAnnotations(boolean enable) {
if (enable) {
- if (!enableMemberValuePropagationAnnotations) {
- enableMemberValuePropagationAnnotations = true;
- addInternalKeepRules(
- "-neverpropagatevalue class * { @com.android.tools.r8.NeverPropagateValue *; }");
- }
- } else {
- assert !enableMemberValuePropagationAnnotations;
+ return addMemberValuePropagationAnnotations()
+ .addInternalKeepRules(
+ "-neverpropagatevalue class * { @com.android.tools.r8.NeverPropagateValue *; }");
}
return self();
}
public T enableReprocessClassInitializerAnnotations() {
- if (!enableReprocessClassInitializerAnnotations) {
- enableReprocessClassInitializerAnnotations = true;
- addInternalKeepRules(
- "-reprocessclassinitializer @com.android.tools.r8.ReprocessClassInitializer class *");
- }
- return self();
+ return addReprocessClassInitializerAnnotations()
+ .addInternalKeepRules(
+ "-reprocessclassinitializer @com.android.tools.r8.ReprocessClassInitializer class *");
}
public T enableNeverReprocessClassInitializerAnnotations() {
- if (!enableNeverReprocessClassInitializerAnnotations) {
- enableNeverReprocessClassInitializerAnnotations = true;
- addInternalKeepRules(
- "-neverreprocessclassinitializer @com.android.tools.r8.NeverReprocessClassInitializer"
- + " class *");
- }
- return self();
+ return addNeverReprocessClassInitializerAnnotations()
+ .addInternalKeepRules(
+ "-neverreprocessclassinitializer @com.android.tools.r8.NeverReprocessClassInitializer"
+ + " class *");
}
public T enableReprocessMethodAnnotations() {
- if (!enableReprocessMethodAnnotations) {
- enableReprocessMethodAnnotations = true;
- addInternalKeepRules(
- "-reprocessmethod class * {", " @com.android.tools.r8.ReprocessMethod <methods>;", "}");
- }
- return self();
+ return addReprocessMethodAnnotations()
+ .addInternalKeepRules(
+ "-reprocessmethod class * {",
+ " @com.android.tools.r8.ReprocessMethod <methods>;",
+ "}");
}
public T enableNeverReprocessMethodAnnotations() {
- if (!enableNeverReprocessMethodAnnotations) {
- enableNeverReprocessMethodAnnotations = true;
- addInternalKeepRules(
- "-neverreprocessmethod class * {",
- " @com.android.tools.r8.NeverReprocessMethod <methods>;",
- "}");
- }
- return self();
+ return addNeverReprocessMethodAnnotations()
+ .addInternalKeepRules(
+ "-neverreprocessmethod class * {",
+ " @com.android.tools.r8.NeverReprocessMethod <methods>;",
+ "}");
}
public T enableProtoShrinking() {
@@ -574,22 +505,15 @@
}
public T enableSideEffectAnnotations() {
- if (!enableSideEffectAnnotations) {
- enableSideEffectAnnotations = true;
- addInternalKeepRules(
- "-assumemayhavesideeffects class * {",
- " @com.android.tools.r8.AssumeMayHaveSideEffects <methods>;",
- "}");
- }
- return self();
+ return addSideEffectAnnotations()
+ .addInternalKeepRules(
+ "-assumemayhavesideeffects class * {",
+ " @com.android.tools.r8.AssumeMayHaveSideEffects <methods>;",
+ "}");
}
public T assumeAllMethodsMayHaveSideEffects() {
- if (!enableSideEffectAnnotations) {
- enableSideEffectAnnotations = true;
- addInternalKeepRules("-assumemayhavesideeffects class * { <methods>; }");
- }
- return self();
+ return addInternalKeepRules("-assumemayhavesideeffects class * { <methods>; }");
}
public T enableConstantArgumentAnnotations() {
@@ -598,13 +522,9 @@
public T enableConstantArgumentAnnotations(boolean value) {
if (value) {
- if (!enableConstantArgumentAnnotations) {
- enableConstantArgumentAnnotations = true;
- addInternalKeepRules(
- "-keepconstantarguments class * { @com.android.tools.r8.KeepConstantArguments *; }");
- }
- } else {
- assert !enableConstantArgumentAnnotations;
+ return addConstantArgumentAnnotations()
+ .addInternalKeepRules(
+ "-keepconstantarguments class * { @com.android.tools.r8.KeepConstantArguments *; }");
}
return self();
}
@@ -615,13 +535,9 @@
public T enableUnusedArgumentAnnotations(boolean value) {
if (value) {
- if (!enableUnusedArgumentAnnotations) {
- enableUnusedArgumentAnnotations = true;
- addInternalKeepRules(
- "-keepunusedarguments class * { @com.android.tools.r8.KeepUnusedArguments *; }");
- }
- } else {
- assert !enableUnusedArgumentAnnotations;
+ return addUnusedArgumentAnnotations()
+ .addInternalKeepRules(
+ "-keepunusedarguments class * { @com.android.tools.r8.KeepUnusedArguments *; }");
}
return self();
}
@@ -659,9 +575,10 @@
return self();
}
- private void addInternalKeepRules(String... rules) {
+ T addInternalKeepRules(String... rules) {
// We don't add these to the keep-rule set for other test provided rules.
builder.addProguardConfiguration(Arrays.asList(rules), Origin.unknown());
+ return enableProguardTestOptions();
}
@Override
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 6f6c949..1bcbc55 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -12,11 +12,14 @@
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.Sets;
import java.io.IOException;
+import java.lang.annotation.Annotation;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
+import java.util.Set;
public abstract class TestShrinkerBuilder<
C extends BaseCompilerCommand,
@@ -30,6 +33,9 @@
protected boolean enableOptimization = true;
protected boolean enableMinification = true;
+ private final Set<Class<? extends Annotation>> addedTestingAnnotations =
+ Sets.newIdentityHashSet();
+
TestShrinkerBuilder(TestState state, B builder, Backend backend) {
super(state, builder, backend);
}
@@ -301,6 +307,82 @@
public abstract T addApplyMapping(String proguardMap);
+ public final T addAlwaysInliningAnnotations() {
+ return addTestingAnnotation(AlwaysInline.class);
+ }
+
+ public final T addAssumeNoSideEffectsAnnotations() {
+ return addTestingAnnotation(AssumeNoSideEffects.class);
+ }
+
+ public final T addConstantArgumentAnnotations() {
+ return addTestingAnnotation(KeepConstantArguments.class);
+ }
+
+ public final T addForceInliningAnnotations() {
+ return addTestingAnnotation(ForceInline.class);
+ }
+
+ public final T addInliningAnnotations() {
+ return addTestingAnnotation(NeverInline.class);
+ }
+
+ public final T addMemberValuePropagationAnnotations() {
+ return addTestingAnnotation(NeverPropagateValue.class);
+ }
+
+ public final T addNeverClassInliningAnnotations() {
+ return addTestingAnnotation(NeverClassInline.class);
+ }
+
+ public final T addNeverReprocessClassInitializerAnnotations() {
+ return addTestingAnnotation(NeverReprocessClassInitializer.class);
+ }
+
+ public final T addNeverReprocessMethodAnnotations() {
+ return addTestingAnnotation(NeverReprocessMethod.class);
+ }
+
+ public final T addNeverSingleCallerInlineAnnotations() {
+ return addTestingAnnotation(NeverSingleCallerInline.class);
+ }
+
+ public final T addNoHorizontalClassMergingAnnotations() {
+ return addTestingAnnotation(NoHorizontalClassMerging.class);
+ }
+
+ public final T addNoStaticClassMergingAnnotations() {
+ return addTestingAnnotation(NoStaticClassMerging.class);
+ }
+
+ public final T addNoUnusedInterfaceRemovalAnnotations() {
+ return addTestingAnnotation(NoUnusedInterfaceRemoval.class);
+ }
+
+ public final T addNoVerticalClassMergingAnnotations() {
+ return addTestingAnnotation(NoVerticalClassMerging.class);
+ }
+
+ public final T addReprocessClassInitializerAnnotations() {
+ return addTestingAnnotation(ReprocessClassInitializer.class);
+ }
+
+ public final T addReprocessMethodAnnotations() {
+ return addTestingAnnotation(ReprocessMethod.class);
+ }
+
+ public final T addSideEffectAnnotations() {
+ return addTestingAnnotation(AssumeMayHaveSideEffects.class);
+ }
+
+ public final T addUnusedArgumentAnnotations() {
+ return addTestingAnnotation(KeepUnusedArguments.class);
+ }
+
+ private T addTestingAnnotation(Class<? extends Annotation> clazz) {
+ return addedTestingAnnotations.add(clazz) ? addProgramClasses(clazz) : self();
+ }
+
private static String getMethodLine(MethodReference method) {
// Should we encode modifiers in method references?
StringBuilder builder = new StringBuilder();
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/NoRelaxationForSerializableTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/NoRelaxationForSerializableTest.java
index 7521316..83a0155 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/NoRelaxationForSerializableTest.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/NoRelaxationForSerializableTest.java
@@ -125,9 +125,11 @@
assumeTrue(parameters.isCfRuntime());
testForProguard()
.addProgramClasses(CLASSES)
- .addTestingAnnotationsAsProgramClasses()
.addKeepRuleFiles(configuration)
.addKeepRules(KEEPMEMBER_RULES)
+ .addInliningAnnotations()
+ .addMemberValuePropagationAnnotations()
+ .addNoVerticalClassMergingAnnotations()
.compile()
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutput(EXPECTED_OUTPUT)
@@ -158,7 +160,9 @@
assumeTrue(parameters.isCfRuntime());
testForProguard()
.addProgramClasses(CLASSES)
- .addTestingAnnotationsAsProgramClasses()
+ .addInliningAnnotations()
+ .addMemberValuePropagationAnnotations()
+ .addNoVerticalClassMergingAnnotations()
.addKeepRuleFiles(configuration)
.compile()
.run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java b/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
index c43c289..689e080 100644
--- a/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
+++ b/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
@@ -25,7 +25,6 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.util.Calendar;
import java.util.List;
import java.util.TreeSet;
@@ -46,11 +45,12 @@
protected abstract List<Class<?>> getMethodTemplateClasses();
- public static String getHeaderString(Class<?> generationClass, String generatedPackage) {
- int year = Calendar.getInstance().get(Calendar.YEAR);
- String simpleName = generationClass.getSimpleName();
+ protected abstract int getYear();
+
+ public String getHeaderString() {
+ String simpleName = getClass().getSimpleName();
return StringUtils.lines(
- "// Copyright (c) " + year + ", the R8 project authors. Please see the AUTHORS file",
+ "// Copyright (c) " + getYear() + ", 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.",
"",
@@ -58,7 +58,7 @@
"// GENERATED FILE. DO NOT EDIT! See " + simpleName + ".java.",
"// ***********************************************************************************",
"",
- "package " + generatedPackage + ";");
+ "package " + getGeneratedClassPackageName() + ";");
}
protected Path getGeneratedFile() {
@@ -115,7 +115,7 @@
private void generateRawOutput(CfCodePrinter codePrinter, Path tempFile) throws IOException {
try (PrintStream printer = new PrintStream(Files.newOutputStream(tempFile))) {
- printer.print(getHeaderString(this.getClass(), getGeneratedClassPackageName()));
+ printer.print(getHeaderString());
printer.println("import com.android.tools.r8.graph.DexItemFactory;");
codePrinter.getImports().forEach(i -> printer.println("import " + i + ";"));
printer.println("public final class " + getGeneratedClassName() + " {\n");
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentFieldsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentFieldsTest.java
new file mode 100644
index 0000000..d322729
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentFieldsTest.java
@@ -0,0 +1,82 @@
+/*
+ * // Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+ * // for details. All rights reserved. Use of this source code is governed by a
+ * // BSD-style license that can be found in the LICENSE file.
+ */
+
+package com.android.tools.r8.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+
+public class ClassesWithDifferentFieldsTest extends HorizontalClassMergingTestBase {
+ public ClassesWithDifferentFieldsTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options ->
+ options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
+ .enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .addHorizontallyMergedClassesInspectorIf(
+ enableHorizontalClassMerging, inspector -> inspector.assertMergedInto(B.class, A.class))
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A. v: a", "B. i: 2")
+ .inspect(
+ codeInspector -> {
+ assertThat(codeInspector.clazz(A.class), isPresent());
+ assertThat(
+ codeInspector.clazz(B.class), notIf(isPresent(), enableHorizontalClassMerging));
+ });
+ }
+
+ @NeverClassInline
+ public static class A {
+ public String v;
+
+ public A(String v) {
+ this.v = v;
+ }
+
+ @NeverInline
+ public void foo() {
+ System.out.println("A. v: " + v);
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+ public Integer i;
+
+ public B(Integer i) {
+ this.i = i;
+ }
+
+ @NeverInline
+ public void foo() {
+ System.out.println("B. i: " + i);
+ }
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ new A("a").foo();
+ new B(2).foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/FieldsWithDifferentAccessFlagsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/FieldsWithDifferentAccessFlagsTest.java
new file mode 100644
index 0000000..58acdbf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/FieldsWithDifferentAccessFlagsTest.java
@@ -0,0 +1,89 @@
+/*
+ * // Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+ * // for details. All rights reserved. Use of this source code is governed by a
+ * // BSD-style license that can be found in the LICENSE file.
+ */
+
+package com.android.tools.r8.classmerging.horizontal;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import org.junit.Test;
+
+public class FieldsWithDifferentAccessFlagsTest extends HorizontalClassMergingTestBase {
+
+ public FieldsWithDifferentAccessFlagsTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options ->
+ options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
+ .addHorizontallyMergedClassesInspectorIf(
+ enableHorizontalClassMerging, HorizontallyMergedClassesInspector::assertNoClassesMerged)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A", "B", "C");
+ }
+
+ @NeverClassInline
+ public static class A {
+ public volatile String msg;
+
+ public A(String msg) {
+ this.msg = msg;
+ }
+
+ @NeverInline
+ public void foo() {
+ System.out.println(msg);
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+ public transient String msg;
+
+ public B(String msg) {
+ this.msg = msg;
+ }
+
+ @NeverInline
+ public void foo() {
+ System.out.println(msg);
+ }
+ }
+
+ @NeverClassInline
+ public static class C {
+ public String msg;
+
+ public C(String msg) {
+ this.msg = msg;
+ }
+
+ @NeverInline
+ public void foo() {
+ System.out.println(msg);
+ }
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ new A(System.currentTimeMillis() > 0 ? "A" : null).foo();
+ new B(System.currentTimeMillis() > 0 ? "B" : null).foo();
+ new C(System.currentTimeMillis() > 0 ? "C" : null).foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MinimizeFieldCastsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MinimizeFieldCastsTest.java
new file mode 100644
index 0000000..90ae326
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MinimizeFieldCastsTest.java
@@ -0,0 +1,121 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.classmerging.horizontal;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+
+public class MinimizeFieldCastsTest extends HorizontalClassMergingTestBase {
+
+ public MinimizeFieldCastsTest(TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options ->
+ options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .addHorizontallyMergedClassesInspectorIf(
+ enableHorizontalClassMerging,
+ inspector ->
+ // Two merge groups are expected since we attempt to merge classes in a way that
+ // avoids merging fields with different types unless strictly required for merging.
+ inspector.assertMergedInto(B.class, A.class).assertMergedInto(D.class, C.class))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Foo", "Foo", "Bar", "Bar");
+ }
+
+ @NeverClassInline
+ public static class A {
+ FooGreeter greeter;
+
+ A(FooGreeter greeter) {
+ this.greeter = greeter;
+ }
+
+ void greet() {
+ greeter.greet();
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+ FooGreeter greeter;
+
+ B(FooGreeter greeter) {
+ this.greeter = greeter;
+ }
+
+ void greet() {
+ greeter.greet();
+ }
+ }
+
+ @NeverClassInline
+ public static class C {
+ BarGreeter greeter;
+
+ C(BarGreeter greeter) {
+ this.greeter = greeter;
+ }
+
+ void greet() {
+ greeter.greet();
+ }
+ }
+
+ @NeverClassInline
+ public static class D {
+ BarGreeter greeter;
+
+ D(BarGreeter greeter) {
+ this.greeter = greeter;
+ }
+
+ void greet() {
+ greeter.greet();
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class FooGreeter {
+
+ @NeverInline
+ void greet() {
+ System.out.println("Foo");
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class BarGreeter {
+
+ @NeverInline
+ void greet() {
+ System.out.println("Bar");
+ }
+ }
+
+ static class Main {
+ public static void main(String[] args) {
+ new A(new FooGreeter()).greet();
+ new B(new FooGreeter()).greet();
+ new C(new BarGreeter()).greet();
+ new D(new BarGreeter()).greet();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java
index b6183a3..79aae15 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.classmerging.horizontal.NonReboundFieldAccessOnMergedClassTest.C;
import com.android.tools.r8.classmerging.horizontal.testclasses.NonReboundFieldAccessWithMergedTypeTestClasses;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PreventMergeMainDexTracingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PreventMergeMainDexTracingTest.java
index 6a31a5e..13d8a62 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PreventMergeMainDexTracingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PreventMergeMainDexTracingTest.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.classmerging.horizontal.PreventMergeMainDexListTest.Main;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.nio.file.Path;
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerConstructorCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerConstructorCollisionTest.java
index c7a7d9c..5239543 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerConstructorCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerConstructorCollisionTest.java
@@ -12,10 +12,6 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.classmerging.horizontal.TreeFixerCollisionTest.A;
-import com.android.tools.r8.classmerging.horizontal.TreeFixerCollisionTest.B;
-import com.android.tools.r8.classmerging.horizontal.TreeFixerCollisionTest.C;
-import com.android.tools.r8.classmerging.horizontal.TreeFixerCollisionTest.Main;
import org.junit.Test;
public class TreeFixerConstructorCollisionTest extends HorizontalClassMergingTestBase {
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceCollisionTest.java
index 946e113..6f3c8f8 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceCollisionTest.java
@@ -13,8 +13,6 @@
import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.classmerging.horizontal.TreeFixerInterfaceFixedCollisionTest.B;
-import com.android.tools.r8.classmerging.horizontal.TreeFixerInterfaceImplementedByParentTest.I;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceFixedCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceFixedCollisionTest.java
index 1d3290a..627838f 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceFixedCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceFixedCollisionTest.java
@@ -13,10 +13,6 @@
import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.classmerging.horizontal.TreeFixerInterfaceCollisionTest.C;
-import com.android.tools.r8.classmerging.horizontal.TreeFixerInterfaceImplementedByParentTest.B;
-import com.android.tools.r8.classmerging.horizontal.TreeFixerInterfaceImplementedByParentTest.E;
-import com.android.tools.r8.classmerging.horizontal.TreeFixerInterfaceImplementedByParentTest.I;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassTest.java
index a40b387..abd38f5 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassTest.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.classmerging.horizontal.ConstructorMergingOverlapTest.Main;
import org.junit.Test;
public class VerticallyMergedClassTest extends HorizontalClassMergingTestBase {
diff --git a/src/test/java/com/android/tools/r8/compatproguard/CompatKeepClassMemberNamesTestRunner.java b/src/test/java/com/android/tools/r8/compatproguard/CompatKeepClassMemberNamesTestRunner.java
index de0e481..034f226 100644
--- a/src/test/java/com/android/tools/r8/compatproguard/CompatKeepClassMemberNamesTestRunner.java
+++ b/src/test/java/com/android/tools/r8/compatproguard/CompatKeepClassMemberNamesTestRunner.java
@@ -11,7 +11,6 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.BaseCompilerCommand;
-import com.android.tools.r8.NeverInline;
import com.android.tools.r8.ProguardVersion;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestCompileResult;
@@ -40,8 +39,7 @@
private static Class<?> MAIN_CLASS = CompatKeepClassMemberNamesTest.class;
private static Class<?> BAR_CLASS = CompatKeepClassMemberNamesTest.Bar.class;
- private static Collection<Class<?>> CLASSES =
- ImmutableList.of(MAIN_CLASS, BAR_CLASS, NeverInline.class);
+ private static Collection<Class<?>> CLASSES = ImmutableList.of(MAIN_CLASS, BAR_CLASS);
private static String KEEP_RULE =
"class "
@@ -112,7 +110,12 @@
T extends TestShrinkerBuilder<C, B, CR, RR, T>>
void testWithoutRules(TestShrinkerBuilder<C, B, CR, RR, T> builder) throws Exception {
assertBarIsAbsent(
- builder.addProgramClasses(CLASSES).addKeepMainRule(MAIN_CLASS).noMinification().compile());
+ builder
+ .addProgramClasses(CLASSES)
+ .addKeepMainRule(MAIN_CLASS)
+ .addInliningAnnotations()
+ .noMinification()
+ .compile());
}
@Test
@@ -207,6 +210,7 @@
.addProgramClasses(CLASSES)
.addKeepMainRule(MAIN_CLASS)
.addKeepRules("-keepclassmembers " + KEEP_RULE_NON_STATIC)
+ .addInliningAnnotations()
.noMinification();
}
@@ -238,7 +242,8 @@
return builder
.addProgramClasses(CLASSES)
.addKeepMainRule(MAIN_CLASS)
- .addKeepRules("-keepclassmembers " + KEEP_RULE);
+ .addKeepRules("-keepclassmembers " + KEEP_RULE)
+ .addInliningAnnotations();
}
private <CR extends TestCompileResult<CR, RR>, RR extends TestRunResult<RR>>
@@ -309,6 +314,7 @@
builder
.addProgramClasses(CLASSES)
.addKeepMainRule(MAIN_CLASS)
+ .addInliningAnnotations()
.noMinification()
.addKeepRules("-keepclassmembers class " + Bar.class.getTypeName())
.compile());
@@ -394,7 +400,8 @@
return builder
.addProgramClasses(CLASSES)
.addKeepMainRule(MAIN_CLASS)
- .addKeepRules("-keepclassmembernames " + KEEP_RULE);
+ .addKeepRules("-keepclassmembernames " + KEEP_RULE)
+ .addInliningAnnotations();
}
private <CR extends TestCompileResult<CR, RR>, RR extends TestRunResult<RR>>
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InstantTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InstantTest.java
new file mode 100644
index 0000000..7e38a30
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InstantTest.java
@@ -0,0 +1,75 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.desugaredlibrary;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class InstantTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+
+ @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ }
+
+ public InstantTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testInstantD8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForD8()
+ .addInnerClasses(InstantTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutputLines("1970-01-02T10:17:36.789Z");
+ }
+
+ @Test
+ public void testInstantR8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForR8(parameters.getBackend())
+ .addInnerClasses(InstantTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepClassAndMembersRules(Executor.class)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutputLines("1970-01-02T10:17:36.789Z");
+ }
+
+ static class Executor {
+
+ public static void main(String[] args) {
+ System.out.println(Instant.ofEpochMilli(123456789L).atOffset(ZoneOffset.UTC));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
index 762916e..6ebe0db 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
@@ -54,7 +54,7 @@
.addOptionsModification(opt -> opt.ignoreMissingClasses = true)
.allowDiagnosticWarningMessages()
.compile()
- .assertAllWarningMessagesMatch(containsString("Missing class:"))
+ .assertAllWarningMessagesMatch(containsString("Missing class "))
.inspect(this::assertNotEmpty)
.inspect(Java11R8CompilationTest::assertNoNests);
}
diff --git a/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java b/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
index 7ea9110..d2a5729 100644
--- a/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
+++ b/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
@@ -64,7 +64,9 @@
@Test
public void testEmptyDebugInfo() {
DexDebugInfo debugInfo = new DexDebugInfo(1, DexString.EMPTY_ARRAY, new DexDebugEvent[]{});
- DebugBytecodeWriter writer = new DebugBytecodeWriter(debugInfo, emptyObjectTObjectMapping());
+ DebugBytecodeWriter writer =
+ new DebugBytecodeWriter(
+ debugInfo, emptyObjectTObjectMapping(), GraphLens.getIdentityLens());
Assert.assertEquals(3, writer.generate().length);
}
}
diff --git a/src/test/java/com/android/tools/r8/diagnostics/ModifyDiagnosticsLevelTest.java b/src/test/java/com/android/tools/r8/diagnostics/ModifyDiagnosticsLevelTest.java
index 8db3e12..3dabac7 100644
--- a/src/test/java/com/android/tools/r8/diagnostics/ModifyDiagnosticsLevelTest.java
+++ b/src/test/java/com/android/tools/r8/diagnostics/ModifyDiagnosticsLevelTest.java
@@ -12,7 +12,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.shaking.MissingClassesDiagnostic;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -20,7 +20,7 @@
@RunWith(Parameterized.class)
public class ModifyDiagnosticsLevelTest extends TestBase {
- private static final String MISSING_CLASS_MESSAGE_PREFIX = "Missing class: ";
+ private static final String MISSING_CLASS_MESSAGE_PREFIX = "Missing class ";
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
@@ -40,7 +40,7 @@
.setDiagnosticsLevelModifier(
(level, diagnostic) -> {
if (level == DiagnosticsLevel.WARNING
- && diagnostic instanceof StringDiagnostic
+ && diagnostic instanceof MissingClassesDiagnostic
&& diagnostic.getDiagnosticMessage().startsWith(MISSING_CLASS_MESSAGE_PREFIX)) {
return DiagnosticsLevel.INFO;
}
@@ -57,7 +57,7 @@
}
@Test
- public void testWarningToError() throws Exception {
+ public void testWarningToError() {
try {
testForR8(Backend.DEX)
.addProgramClasses(TestClass.class)
@@ -66,7 +66,7 @@
.setDiagnosticsLevelModifier(
(level, diagnostic) -> {
if (level == DiagnosticsLevel.WARNING
- && diagnostic instanceof StringDiagnostic
+ && diagnostic instanceof MissingClassesDiagnostic
&& diagnostic.getDiagnosticMessage().startsWith(MISSING_CLASS_MESSAGE_PREFIX)) {
return DiagnosticsLevel.ERROR;
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/GenerateEnumUnboxingMethods.java b/src/test/java/com/android/tools/r8/enumunboxing/GenerateEnumUnboxingMethods.java
index 54a405c..74bf88f 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/GenerateEnumUnboxingMethods.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/GenerateEnumUnboxingMethods.java
@@ -50,6 +50,10 @@
return METHOD_TEMPLATE_CLASSES;
}
+ @Override
+ protected int getYear() {
+ return 2020;
+ }
@Test
public void testEnumUtilityMethodsGenerated() throws Exception {
diff --git a/src/test/java/com/android/tools/r8/graph/MissingClassThrowingTest.java b/src/test/java/com/android/tools/r8/graph/MissingClassThrowingTest.java
index c73b948..213db97 100644
--- a/src/test/java/com/android/tools/r8/graph/MissingClassThrowingTest.java
+++ b/src/test/java/com/android/tools/r8/graph/MissingClassThrowingTest.java
@@ -4,7 +4,8 @@
package com.android.tools.r8.graph;
-import static org.hamcrest.core.StringContains.containsString;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.junit.Assert.assertEquals;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoStaticClassMerging;
@@ -12,8 +13,10 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.shaking.MissingClassesDiagnostic;
+import com.android.tools.r8.utils.InternalOptions.TestingOptions;
+import com.android.tools.r8.utils.codeinspector.AssertUtils;
import java.io.IOException;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -61,21 +64,32 @@
this.parameters = parameters;
}
- @Ignore("b/128885552")
@Test
public void testSuperTypeOfExceptions() throws Exception {
- testForR8(parameters.getBackend())
- .addProgramClasses(Program.class)
- .noMinification()
- .noTreeShaking()
- .enableInliningAnnotations()
- .enableNoStaticClassMergingAnnotations()
- .debug()
- .addKeepRules("-keep class ** { *; }", "-keepattributes *")
- .compile()
- .addRunClasspathClasses(MissingException.class)
- .run(parameters.getRuntime(), Program.class)
- .assertFailureWithErrorThatMatches(
- containsString("Missing class: " + MissingException.class.getTypeName()));
+ AssertUtils.assertFailsCompilation(
+ () ->
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Program.class)
+ .addKeepAllClassesRule()
+ .addKeepAllAttributes()
+ .addOptionsModification(TestingOptions::enableExperimentalMissingClassesReporting)
+ .noMinification()
+ .noTreeShaking()
+ .enableInliningAnnotations()
+ .enableNoStaticClassMergingAnnotations()
+ .debug()
+ .compileWithExpectedDiagnostics(
+ diagnostics -> {
+ diagnostics
+ .assertOnlyErrors()
+ .assertErrorsMatch(diagnosticType(MissingClassesDiagnostic.class));
+
+ MissingClassesDiagnostic diagnostic =
+ (MissingClassesDiagnostic) diagnostics.getErrors().get(0);
+ assertEquals(1, diagnostic.getMissingClasses().size());
+ assertEquals(
+ MissingException.class.getTypeName(),
+ diagnostic.getMissingClasses().iterator().next().getTypeName());
+ }));
}
}
diff --git a/src/test/java/com/android/tools/r8/internal/Iosched2019Test.java b/src/test/java/com/android/tools/r8/internal/Iosched2019Test.java
index 34d0e01..0dbe71a 100644
--- a/src/test/java/com/android/tools/r8/internal/Iosched2019Test.java
+++ b/src/test/java/com/android/tools/r8/internal/Iosched2019Test.java
@@ -61,7 +61,7 @@
containsString("Proguard configuration rule does not match anything: ")))
.assertAllWarningMessagesMatch(
anyOf(
- containsString("Missing class: "),
+ containsString("Missing class "),
containsString("required for default or static interface methods desugaring"),
equalTo("Resource 'META-INF/MANIFEST.MF' already exists.")))
.assertNoErrorMessages();
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
index 05c11c4..d78a1c7 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
@@ -68,6 +68,11 @@
return METHOD_TEMPLATE_CLASSES;
}
+ @Override
+ protected int getYear() {
+ return 2020;
+ }
+
@Test
public void testBackportsGenerated() throws Exception {
ArrayList<Class<?>> sorted = new ArrayList<>(getMethodTemplateClasses());
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java
index 964848a..9ba1f75 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java
@@ -65,7 +65,6 @@
.addProgramClasses(classes)
.addKeepMainRule(mainClass)
.addKeepRules(ImmutableList.of("-keepattributes InnerClasses,Signature,EnclosingMethod"))
- .addTestingAnnotationsAsProgramClasses()
// All tests are checking if invocations to certain null-check utils are gone.
.noMinification()
.addOptionsModification(
@@ -92,7 +91,7 @@
MethodSubject selfCheck = mainSubject.uniqueMethodWithName("selfCheck");
assertThat(selfCheck, isPresent());
- assertEquals(1, countCallToParamNullCheck(selfCheck));
+ assertEquals(0, countCallToParamNullCheck(selfCheck));
assertEquals(1, countPrintCall(selfCheck));
assertEquals(0, countThrow(selfCheck));
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/EscapingBuilderTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/EscapingBuilderTest.java
index bf5e92a..50201f3 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/EscapingBuilderTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/EscapingBuilderTest.java
@@ -10,6 +10,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import org.junit.Test;
@@ -23,6 +24,7 @@
.addInnerClasses(EscapingBuilderTest.class)
.addKeepMainRule(TestClass.class)
.enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.compile()
.inspect(this::inspect);
}
@@ -101,6 +103,7 @@
}
// Builder that escapes via field `f` that is assigned in a virtual method.
+ @NoHorizontalClassMerging
static class Builder2 {
public Builder2 f;
@@ -118,6 +121,7 @@
}
// Builder that escapes via field `f` that is assigned in a virtual method.
+ @NoHorizontalClassMerging
static class Builder3 {
public Builder3 f;
@@ -135,6 +139,7 @@
}
// Builder that escapes via field `f` that is assigned in a virtual method.
+ @NoHorizontalClassMerging
static class Builder4 {
public Builder4 f;
@@ -152,6 +157,7 @@
}
// Builder that escapes via field `f` that is assigned in a static method.
+ @NoHorizontalClassMerging
static class Builder5 {
public Builder5 f;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java
index f5c7da8..2012c6f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java
@@ -54,7 +54,6 @@
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.enableSideEffectAnnotations()
- .enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/ConditionalSimpleInliningTestBase.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/ConditionalSimpleInliningTestBase.java
new file mode 100644
index 0000000..61c9edc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/ConditionalSimpleInliningTestBase.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.inliner.conditionalsimpleinlining;
+
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ir.optimize.Inliner;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.SetUtils;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public abstract class ConditionalSimpleInliningTestBase extends TestBase {
+
+ protected final boolean enableSimpleInliningConstraints;
+ protected final TestParameters parameters;
+
+ @Parameters(name = "{1}, simple inlining constraints: {0}")
+ public static Iterable<?> data() {
+ return buildParameters(
+ BooleanUtils.values(), TestBase.getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ public ConditionalSimpleInliningTestBase(
+ boolean enableSimpleInliningConstraints, TestParameters parameters) {
+ this.enableSimpleInliningConstraints = enableSimpleInliningConstraints;
+ this.parameters = parameters;
+ }
+
+ public void configure(R8FullTestBuilder testBuilder) {
+ testBuilder
+ .addOptionsModification(this::enableSimpleInliningConstraints)
+ .setMinApi(parameters.getApiLevel());
+ }
+
+ private void enableSimpleInliningConstraints(InternalOptions options) {
+ options.enableSimpleInliningConstraints = enableSimpleInliningConstraints;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/ConditionalSimpleInliningWithEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/ConditionalSimpleInliningWithEnumUnboxingTest.java
new file mode 100644
index 0000000..3cebb62
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/ConditionalSimpleInliningWithEnumUnboxingTest.java
@@ -0,0 +1,71 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.inliner.conditionalsimpleinlining;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runners.Parameterized.Parameters;
+
+public class ConditionalSimpleInliningWithEnumUnboxingTest
+ extends ConditionalSimpleInliningTestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return TestBase.getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public ConditionalSimpleInliningWithEnumUnboxingTest(TestParameters parameters) {
+ super(true, parameters);
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .apply(this::configure)
+ .addEnumUnboxingInspector(
+ inspector ->
+ inspector.assertUnboxedIf(parameters.isDexRuntime(), EnumUnboxingCandidate.class))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ EnumUnboxingCandidate value =
+ System.currentTimeMillis() > 0 ? EnumUnboxingCandidate.A : EnumUnboxingCandidate.B;
+ simpleIfNullTest(value);
+ }
+
+ static void simpleIfNullTest(EnumUnboxingCandidate arg) {
+ if (arg == null) {
+ return;
+ }
+ System.out.print("H");
+ System.out.print("e");
+ System.out.print("l");
+ System.out.print("l");
+ System.out.print("o");
+ System.out.print(" ");
+ System.out.print("w");
+ System.out.print("o");
+ System.out.print("r");
+ System.out.print("l");
+ System.out.print("d");
+ System.out.println("!");
+ }
+ }
+
+ enum EnumUnboxingCandidate {
+ A,
+ B;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/SimpleIfNullOrNotNullInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/SimpleIfNullOrNotNullInliningTest.java
new file mode 100644
index 0000000..c5073c5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/SimpleIfNullOrNotNullInliningTest.java
@@ -0,0 +1,195 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.inliner.conditionalsimpleinlining;
+
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethodWithName;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverSingleCallerInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class SimpleIfNullOrNotNullInliningTest extends ConditionalSimpleInliningTestBase {
+
+ private final Class<?> mainClass;
+
+ @Parameters(name = "{2}, main: {1}, simple inlining constraints: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(),
+ ImmutableList.of(
+ TestClassEligibleForSimpleInlining.class, TestClassIneligibleForSimpleInlining.class),
+ TestBase.getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ public SimpleIfNullOrNotNullInliningTest(
+ boolean enableSimpleInliningConstraints, Class<?> mainClass, TestParameters parameters) {
+ super(enableSimpleInliningConstraints, parameters);
+ this.mainClass = mainClass;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(mainClass, TestMethods.class)
+ .addKeepMainRule(mainClass)
+ .apply(this::configure)
+ .enableNeverSingleCallerInlineAnnotations()
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), mainClass)
+ .assertSuccessWithOutput(getExpectedOutput());
+ }
+
+ private String getExpectedOutput() {
+ if (mainClass == TestClassEligibleForSimpleInlining.class) {
+ return "";
+ }
+ return StringUtils.times(StringUtils.lines("Hello world!"), 8);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ MethodSubject mainMethodSubject = inspector.clazz(mainClass).mainMethod();
+ assertThat(mainMethodSubject, isPresent());
+ assertThat(mainMethodSubject, not(invokesMethodWithName("print")));
+
+ ClassSubject classSubject = inspector.clazz(TestMethods.class);
+ assertThat(classSubject, notIf(isPresent(), shouldBeEligibleForSimpleInlining()));
+
+ if (shouldBeEligibleForSimpleInlining()) {
+ return;
+ }
+
+ assertThat(classSubject, isPresent());
+ assertThat(classSubject.uniqueMethodWithName("simpleIfNullTest"), isPresent());
+ assertThat(classSubject.uniqueMethodWithName("simpleIfBothNullTest"), isPresent());
+ assertThat(classSubject.uniqueMethodWithName("simpleIfNotNullTest"), isPresent());
+ assertThat(classSubject.uniqueMethodWithName("simpleIfBothNotNullTest"), isPresent());
+ }
+
+ private boolean shouldBeEligibleForSimpleInlining() {
+ return mainClass == TestClassEligibleForSimpleInlining.class && enableSimpleInliningConstraints;
+ }
+
+ static class TestClassEligibleForSimpleInlining {
+
+ public static void main(String[] args) {
+ Object notNull = new Object();
+ TestMethods.simpleIfNullTest(null);
+ TestMethods.simpleIfBothNullTest(null, null);
+ TestMethods.simpleIfNotNullTest(notNull);
+ TestMethods.simpleIfBothNotNullTest(notNull, notNull);
+ }
+ }
+
+ static class TestClassIneligibleForSimpleInlining {
+
+ public static void main(String[] args) {
+ Object notNull = new Object();
+ TestMethods.simpleIfNullTest(notNull);
+ TestMethods.simpleIfBothNullTest(null, notNull);
+ TestMethods.simpleIfBothNullTest(notNull, null);
+ TestMethods.simpleIfBothNullTest(notNull, notNull);
+ TestMethods.simpleIfNotNullTest(null);
+ TestMethods.simpleIfBothNotNullTest(null, notNull);
+ TestMethods.simpleIfBothNotNullTest(notNull, null);
+ TestMethods.simpleIfBothNotNullTest(null, null);
+ }
+ }
+
+ static class TestMethods {
+
+ @NeverSingleCallerInline
+ static void simpleIfNullTest(Object o) {
+ if (o == null) {
+ return;
+ }
+ System.out.print("H");
+ System.out.print("e");
+ System.out.print("l");
+ System.out.print("l");
+ System.out.print("o");
+ System.out.print(" ");
+ System.out.print("w");
+ System.out.print("o");
+ System.out.print("r");
+ System.out.print("l");
+ System.out.print("d");
+ System.out.println("!");
+ }
+
+ @NeverSingleCallerInline
+ static void simpleIfBothNullTest(Object o1, Object o2) {
+ if (o1 == null && o2 == null) {
+ return;
+ }
+ System.out.print("H");
+ System.out.print("e");
+ System.out.print("l");
+ System.out.print("l");
+ System.out.print("o");
+ System.out.print(" ");
+ System.out.print("w");
+ System.out.print("o");
+ System.out.print("r");
+ System.out.print("l");
+ System.out.print("d");
+ System.out.println("!");
+ }
+
+ @NeverSingleCallerInline
+ static void simpleIfNotNullTest(Object o) {
+ if (o != null) {
+ return;
+ }
+ System.out.print("H");
+ System.out.print("e");
+ System.out.print("l");
+ System.out.print("l");
+ System.out.print("o");
+ System.out.print(" ");
+ System.out.print("w");
+ System.out.print("o");
+ System.out.print("r");
+ System.out.print("l");
+ System.out.print("d");
+ System.out.println("!");
+ }
+
+ @NeverSingleCallerInline
+ static void simpleIfBothNotNullTest(Object o1, Object o2) {
+ if (o1 != null && o2 != null) {
+ return;
+ }
+ System.out.print("H");
+ System.out.print("e");
+ System.out.print("l");
+ System.out.print("l");
+ System.out.print("o");
+ System.out.print(" ");
+ System.out.print("w");
+ System.out.print("o");
+ System.out.print("r");
+ System.out.print("l");
+ System.out.print("d");
+ System.out.println("!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/SimpleIfTrueOrFalseInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/SimpleIfTrueOrFalseInliningTest.java
new file mode 100644
index 0000000..b7a7186
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/SimpleIfTrueOrFalseInliningTest.java
@@ -0,0 +1,193 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.inliner.conditionalsimpleinlining;
+
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethodWithName;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverSingleCallerInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class SimpleIfTrueOrFalseInliningTest extends ConditionalSimpleInliningTestBase {
+
+ private final Class<?> mainClass;
+
+ @Parameters(name = "{2}, main: {1}, simple inlining constraints: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(),
+ ImmutableList.of(
+ TestClassEligibleForSimpleInlining.class, TestClassIneligibleForSimpleInlining.class),
+ TestBase.getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ public SimpleIfTrueOrFalseInliningTest(
+ boolean enableSimpleInliningConstraints, Class<?> mainClass, TestParameters parameters) {
+ super(enableSimpleInliningConstraints, parameters);
+ this.mainClass = mainClass;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(mainClass, TestMethods.class)
+ .addKeepMainRule(mainClass)
+ .apply(this::configure)
+ .enableNeverSingleCallerInlineAnnotations()
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), mainClass)
+ .assertSuccessWithOutput(getExpectedOutput());
+ }
+
+ private String getExpectedOutput() {
+ if (mainClass == TestClassEligibleForSimpleInlining.class) {
+ return "";
+ }
+ return StringUtils.times(StringUtils.lines("Hello world!"), 8);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ MethodSubject mainMethodSubject = inspector.clazz(mainClass).mainMethod();
+ assertThat(mainMethodSubject, isPresent());
+ assertThat(mainMethodSubject, not(invokesMethodWithName("print")));
+
+ ClassSubject classSubject = inspector.clazz(TestMethods.class);
+ assertThat(classSubject, notIf(isPresent(), shouldBeEligibleForSimpleInlining()));
+
+ if (shouldBeEligibleForSimpleInlining()) {
+ return;
+ }
+
+ assertThat(classSubject, isPresent());
+ assertThat(classSubject.uniqueMethodWithName("simpleIfTrueTest"), isPresent());
+ assertThat(classSubject.uniqueMethodWithName("simpleIfBothTrueTest"), isPresent());
+ assertThat(classSubject.uniqueMethodWithName("simpleIfFalseTest"), isPresent());
+ assertThat(classSubject.uniqueMethodWithName("simpleIfBothFalseTest"), isPresent());
+ }
+
+ private boolean shouldBeEligibleForSimpleInlining() {
+ return mainClass == TestClassEligibleForSimpleInlining.class && enableSimpleInliningConstraints;
+ }
+
+ static class TestClassEligibleForSimpleInlining {
+
+ public static void main(String[] args) {
+ TestMethods.simpleIfTrueTest(true);
+ TestMethods.simpleIfBothTrueTest(true, true);
+ TestMethods.simpleIfFalseTest(false);
+ TestMethods.simpleIfBothFalseTest(false, false);
+ }
+ }
+
+ static class TestClassIneligibleForSimpleInlining {
+
+ public static void main(String[] args) {
+ TestMethods.simpleIfTrueTest(false);
+ TestMethods.simpleIfBothTrueTest(true, false);
+ TestMethods.simpleIfBothTrueTest(false, true);
+ TestMethods.simpleIfBothTrueTest(false, false);
+ TestMethods.simpleIfFalseTest(true);
+ TestMethods.simpleIfBothFalseTest(true, false);
+ TestMethods.simpleIfBothFalseTest(false, true);
+ TestMethods.simpleIfBothFalseTest(true, true);
+ }
+ }
+
+ static class TestMethods {
+
+ @NeverSingleCallerInline
+ static void simpleIfTrueTest(boolean b) {
+ if (b) {
+ return;
+ }
+ System.out.print("H");
+ System.out.print("e");
+ System.out.print("l");
+ System.out.print("l");
+ System.out.print("o");
+ System.out.print(" ");
+ System.out.print("w");
+ System.out.print("o");
+ System.out.print("r");
+ System.out.print("l");
+ System.out.print("d");
+ System.out.println("!");
+ }
+
+ @NeverSingleCallerInline
+ static void simpleIfBothTrueTest(boolean b1, boolean b2) {
+ if (b1 && b2) {
+ return;
+ }
+ System.out.print("H");
+ System.out.print("e");
+ System.out.print("l");
+ System.out.print("l");
+ System.out.print("o");
+ System.out.print(" ");
+ System.out.print("w");
+ System.out.print("o");
+ System.out.print("r");
+ System.out.print("l");
+ System.out.print("d");
+ System.out.println("!");
+ }
+
+ @NeverSingleCallerInline
+ static void simpleIfFalseTest(boolean b) {
+ if (!b) {
+ return;
+ }
+ System.out.print("H");
+ System.out.print("e");
+ System.out.print("l");
+ System.out.print("l");
+ System.out.print("o");
+ System.out.print(" ");
+ System.out.print("w");
+ System.out.print("o");
+ System.out.print("r");
+ System.out.print("l");
+ System.out.print("d");
+ System.out.println("!");
+ }
+
+ @NeverSingleCallerInline
+ static void simpleIfBothFalseTest(boolean b1, boolean b2) {
+ if (!b1 && !b2) {
+ return;
+ }
+ System.out.print("H");
+ System.out.print("e");
+ System.out.print("l");
+ System.out.print("l");
+ System.out.print("o");
+ System.out.print(" ");
+ System.out.print("w");
+ System.out.print("o");
+ System.out.print("r");
+ System.out.print("l");
+ System.out.print("d");
+ System.out.println("!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameInClassInitializerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameInClassInitializerTest.java
index d8d0c47..55afd12 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameInClassInitializerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameInClassInitializerTest.java
@@ -45,7 +45,6 @@
builder.addAll(ToolHelper.getClassFilesForTestDirectory(
ToolHelper.getPackageDirectoryForTestPackage(MAIN.getPackage()),
path -> path.getFileName().toString().startsWith("GetNameClinit")));
- builder.add(ToolHelper.getClassFileForTestClass(NeverInline.class));
classPaths = builder.build();
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetSimpleNameTest.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetSimpleNameTest.java
index 14d7837..a9a5c6f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetSimpleNameTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetSimpleNameTest.java
@@ -125,16 +125,6 @@
"Inner",
"Inner"
);
- private static final String OUTPUT_WITH_SHRUNK_ATTRIBUTES = StringUtils.lines(
- "Local_t03",
- "InnerLocal",
- "$",
- "$$",
- "Local[][][]",
- "[][][]",
- "Outer$Inner",
- "Outer$Inner"
- );
// JDK8 computes the simple name differently: some assumptions about non-member classes,
// e.g., 1 or more digits (followed by the simple name if it's local).
// Since JDK9, the simple name is computed by stripping off the package name.
@@ -171,8 +161,6 @@
builder.add(ToolHelper.getClassFileForTestClass(Outer.class));
builder.add(ToolHelper.getClassFileForTestClass(Outer.Inner.class));
builder.add(ToolHelper.getClassFileForTestClass(Outer.TestHelper.class));
- builder.add(ToolHelper.getClassFileForTestClass(ForceInline.class));
- builder.add(ToolHelper.getClassFileForTestClass(NeverInline.class));
classPaths = builder.build();
}
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 9d4c185..9ee6ad7 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
@@ -13,7 +13,6 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.NeverInline;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestBase;
@@ -88,7 +87,6 @@
private static final Class<?> main = TrivialTestClass.class;
private static final Class<?>[] classes = {
- NeverInline.class,
TrivialTestClass.class,
Simple.class,
SimpleWithGetter.class,
@@ -238,7 +236,6 @@
public void testMoveToHost_fieldOnly() throws Exception {
Class<?> main = MoveToHostFieldOnlyTestClass.class;
Class<?>[] classes = {
- NeverInline.class,
MoveToHostFieldOnlyTestClass.class,
HostOkFieldOnly.class,
CandidateOkFieldOnly.class
@@ -269,7 +266,6 @@
public void testMoveToHost() throws Exception {
Class<?> main = MoveToHostTestClass.class;
Class<?>[] classes = {
- NeverInline.class,
MoveToHostTestClass.class,
HostOk.class,
CandidateOk.class,
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/templates/GenerateCfUtilityMethodsForCodeOptimizations.java b/src/test/java/com/android/tools/r8/ir/optimize/templates/GenerateCfUtilityMethodsForCodeOptimizations.java
index c35135e..732952d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/templates/GenerateCfUtilityMethodsForCodeOptimizations.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/templates/GenerateCfUtilityMethodsForCodeOptimizations.java
@@ -43,6 +43,11 @@
return ImmutableList.of(CfUtilityMethodsForCodeOptimizationsTemplates.class);
}
+ @Override
+ protected int getYear() {
+ return 2020;
+ }
+
@Test
public void test() throws Exception {
ArrayList<Class<?>> sorted = new ArrayList<>(getMethodTemplateClasses());
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/ParameterRewritingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/ParameterRewritingTest.java
index 167c17b..8cc815b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/ParameterRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/ParameterRewritingTest.java
@@ -54,8 +54,8 @@
.addKeepMainRule(TestClass.class)
.enableInliningAnnotations()
.enableNoStaticClassMergingAnnotations()
- .addKeepRules("-dontobfuscate")
.addOptionsModification(options -> options.enableClassInlining = false)
+ .noMinification()
.run(TestClass.class)
.assertSuccessWithOutput(expected)
.inspector();
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
index dff06df..c253c45 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
@@ -145,6 +145,15 @@
}
@Override
+ public void addThrowingInstructionToPossiblyThrowingBlock(
+ IRCode code,
+ ListIterator<BasicBlock> blockIterator,
+ Instruction instruction,
+ InternalOptions options) {
+ throw new Unimplemented();
+ }
+
+ @Override
public BasicBlock split(
IRCode code, ListIterator<BasicBlock> blockIterator, boolean keepCatchHandlers) {
throw new Unimplemented();
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index a949213..293caa8 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -205,21 +205,29 @@
Predicate<DexType> lambdaCheck = createLambdaCheck(inspector);
ClassSubject clazz = inspector.clazz(mainClassName);
+ // TODO(b/173337498): Should be empty, but horizontal class merging interferes with
+ // class inlining.
assertEquals(
- Sets.newHashSet(),
+ Sets.newHashSet(
+ "class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateless$1"),
collectAccessedTypes(
lambdaCheck,
clazz,
"testKotlinSequencesStateless",
"kotlin.sequences.Sequence"));
+ // TODO(b/173337498): Should be absent, but horizontal class merging interferes with
+ // class inlining.
assertThat(
inspector.clazz(
"class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateless$1"),
- not(isPresent()));
+ isPresent());
+ // TODO(b/173337498): Should be empty, but horizontal class merging interferes with
+ // class inlining.
assertEquals(
- Sets.newHashSet(),
+ Sets.newHashSet(
+ "class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateful$1"),
collectAccessedTypes(
lambdaCheck,
clazz,
@@ -228,10 +236,12 @@
"int",
"kotlin.sequences.Sequence"));
+ // TODO(b/173337498): Should be absent, but horizontal class merging interferes with
+ // class inlining.
assertThat(
inspector.clazz(
"class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateful$1"),
- not(isPresent()));
+ isPresent());
assertEquals(
Sets.newHashSet(),
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromNewInstanceTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromNewInstanceTest.java
new file mode 100644
index 0000000..8f43ab8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromNewInstanceTest.java
@@ -0,0 +1,62 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.missingclasses;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.shaking.MissingClassesDiagnostic;
+import com.android.tools.r8.utils.codeinspector.AssertUtils;
+import org.junit.Test;
+
+public class MissingClassReferencedFromNewInstanceTest extends MissingClassesTestBase {
+
+ public MissingClassReferencedFromNewInstanceTest(
+ DontWarnConfiguration dontWarnConfiguration, TestParameters parameters) {
+ super(dontWarnConfiguration, parameters);
+ }
+
+ @Test
+ public void test() throws Exception {
+ AssertUtils.assertFailsCompilationIf(
+ // TODO(b/175542052): Should not fail compilation with -dontwarn Main.
+ !getDontWarnConfiguration().isDontWarnMissingClass(),
+ () ->
+ compileWithExpectedDiagnostics(
+ Main.class, MissingClass.class, this::inspectDiagnostics));
+ }
+
+ private void inspectDiagnostics(TestDiagnosticMessages diagnostics) {
+ // TODO(b/175542052): Should also not have any diagnostics with -dontwarn Main.
+ if (getDontWarnConfiguration().isDontWarnMissingClass()) {
+ diagnostics.assertNoMessages();
+ return;
+ }
+
+ diagnostics
+ .assertOnlyErrors()
+ .assertErrorsCount(1)
+ .assertAllErrorsMatch(diagnosticType(MissingClassesDiagnostic.class));
+
+ MissingClassesDiagnostic diagnostic = (MissingClassesDiagnostic) diagnostics.getErrors().get(0);
+ assertEquals(
+ !getDontWarnConfiguration().isDontWarnMissingClass(),
+ diagnostic.getMissingClasses().stream()
+ .map(TypeReference::getTypeName)
+ .anyMatch(MissingClass.class.getTypeName()::equals));
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new MissingClass();
+ }
+ }
+
+ static class MissingClass {}
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassesTestBase.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassesTestBase.java
new file mode 100644
index 0000000..7c72273
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassesTestBase.java
@@ -0,0 +1,70 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder.DiagnosticsConsumer;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.InternalOptions.TestingOptions;
+import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public abstract class MissingClassesTestBase extends TestBase {
+
+ enum DontWarnConfiguration {
+ DONT_WARN_MAIN_CLASS,
+ DONT_WARN_MISSING_CLASS,
+ NONE;
+
+ public boolean isDontWarnMainClass() {
+ return this == DONT_WARN_MAIN_CLASS;
+ }
+
+ public boolean isDontWarnMissingClass() {
+ return this == DONT_WARN_MISSING_CLASS;
+ }
+ }
+
+ private final DontWarnConfiguration dontWarnConfiguration;
+ private final TestParameters parameters;
+
+ @Parameters(name = "{1}, {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ DontWarnConfiguration.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ public MissingClassesTestBase(
+ DontWarnConfiguration dontWarnConfiguration, TestParameters parameters) {
+ this.dontWarnConfiguration = dontWarnConfiguration;
+ this.parameters = parameters;
+ }
+
+ public R8TestCompileResult compileWithExpectedDiagnostics(
+ Class<?> mainClass, Class<?> missingClass, DiagnosticsConsumer diagnosticsConsumer)
+ throws CompilationFailedException {
+ return testForR8(parameters.getBackend())
+ .addProgramClasses(mainClass)
+ .addKeepMainRule(mainClass)
+ .applyIf(
+ dontWarnConfiguration.isDontWarnMainClass(),
+ testBuilder -> testBuilder.addDontWarn(mainClass))
+ .applyIf(
+ dontWarnConfiguration.isDontWarnMissingClass(),
+ testBuilder -> testBuilder.addDontWarn(missingClass))
+ .addOptionsModification(TestingOptions::enableExperimentalMissingClassesReporting)
+ .setMinApi(parameters.getApiLevel())
+ .compileWithExpectedDiagnostics(diagnosticsConsumer);
+ }
+
+ public DontWarnConfiguration getDontWarnConfiguration() {
+ return dontWarnConfiguration;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/RenameSourceFileRetraceTest.java b/src/test/java/com/android/tools/r8/naming/RenameSourceFileRetraceTest.java
index 5dfd98f..18e92d8 100644
--- a/src/test/java/com/android/tools/r8/naming/RenameSourceFileRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/RenameSourceFileRetraceTest.java
@@ -101,7 +101,7 @@
.run(parameters.getRuntime(), Main.class)
.assertFailureWithErrorThatMatches(containsString("ClassToBeMinified.foo()"))
.inspectFailure(inspector -> inspectOutput(inspector, expectedName, expectedName))
- .inspectStackTrace(stackTrace -> inspectStackTrace(stackTrace, expectedName));
+ .inspectStackTrace(stackTrace -> inspectStackTrace(stackTrace, FILENAME_MAIN));
}
private String getDefaultExpectedName() {
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingFieldTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingFieldTest.java
index dc74758..5da69a2 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingFieldTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingFieldTest.java
@@ -82,7 +82,6 @@
testForJvm()
.addProgramClasses(LIBRARY_CLASSES)
.addProgramClasses(PROGRAM_CLASSES)
- .addTestingAnnotationsAsProgramClasses()
.run(parameters.getRuntime(), ProgramClass.class)
.assertSuccessWithOutput(EXPECTED_SUCCESS);
}
@@ -92,7 +91,6 @@
R8TestCompileResult libraryResult =
testForR8(parameters.getBackend())
.addProgramClasses(LIBRARY_CLASSES)
- .addTestingAnnotationsAsProgramClasses()
.addKeepMainRule(LibraryMain.class)
.setMinApi(parameters.getApiLevel())
.compile();
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingMethodTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingMethodTest.java
index 7429e57..e101caa 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingMethodTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingMethodTest.java
@@ -63,7 +63,6 @@
// Test runner code follows.
private static final Class<?>[] LIBRARY_CLASSES = {
- NeverInline.class,
LibraryA.class,
LibraryB.class,
LibraryMain.class
diff --git a/src/test/java/com/android/tools/r8/naming/b130791310/B130791310.java b/src/test/java/com/android/tools/r8/naming/b130791310/B130791310.java
index 23271e2..ad2f639 100644
--- a/src/test/java/com/android/tools/r8/naming/b130791310/B130791310.java
+++ b/src/test/java/com/android/tools/r8/naming/b130791310/B130791310.java
@@ -122,7 +122,7 @@
.addProgramClasses(CLASSES)
.addKeepClassAndMembersRules(MAIN)
.addKeepRules(RULES)
- .addTestingAnnotationsAsProgramClasses()
+ .addNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.applyIf(
!enableClassMerging, builder -> builder.addKeepRules("-optimizations !class/merging/*"))
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageDebugMinificationTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageDebugMinificationTest.java
new file mode 100644
index 0000000..fe19640
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageDebugMinificationTest.java
@@ -0,0 +1,99 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.repackage;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.LocalVariableTable;
+import com.android.tools.r8.utils.codeinspector.LocalVariableTable.LocalVariableTableEntry;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageDebugMinificationTest extends RepackageTestBase {
+
+ private static final String EXPECTED = "Hello World!";
+
+ public RepackageDebugMinificationTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(A.class, Main.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8WithDebugDex() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForR8(parameters.getBackend())
+ .addProgramClasses(A.class, Main.class)
+ .setMode(CompilationMode.DEBUG)
+ .setMinApi(parameters.getApiLevel())
+ .apply(this::configureRepackaging)
+ .addKeepMainRule(Main.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8WithDebugCf() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testForR8(parameters.getBackend())
+ .addProgramClasses(A.class, Main.class)
+ .setMode(CompilationMode.DEBUG)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .apply(this::configureRepackaging)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED)
+ .inspect(
+ inspector -> {
+ ClassSubject mainClass = inspector.clazz(Main.class);
+ assertThat(mainClass, isPresentAndNotRenamed());
+ MethodSubject main = mainClass.uniqueMethodWithName("main");
+ assertThat(main, isPresentAndNotRenamed());
+ ClassSubject aClass = inspector.clazz(A.class);
+ assertThat(aClass, isPresentAndRenamed());
+ LocalVariableTable localVariableTable = main.getLocalVariableTable();
+ // Take the second index which is localValue
+ assertEquals(2, localVariableTable.size());
+ LocalVariableTableEntry localVariableTableEntry = localVariableTable.get(1);
+ assertEquals("localValue", localVariableTableEntry.name);
+ assertTrue(localVariableTableEntry.type.is(aClass));
+ });
+ }
+
+ public static class A {
+
+ public void foo() {
+ System.out.println("Hello World!");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ A localValue = args.length == 0 ? new A() : null;
+ if (localValue != null) {
+ localValue.foo();
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithNestAndKeepOnPrivateTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithNestAndKeepOnPrivateTest.java
new file mode 100644
index 0000000..e915d3c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithNestAndKeepOnPrivateTest.java
@@ -0,0 +1,98 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.repackage;
+
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.FLATTEN_PACKAGE_HIERARCHY;
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RepackageWithNestAndKeepOnPrivateTest extends RepackageTestBase {
+
+ private final String EXPECTED = "Hello World!";
+
+ @Parameters(name = "{1}, kind: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ ImmutableList.of(FLATTEN_PACKAGE_HIERARCHY, REPACKAGE_CLASSES),
+ getTestParameters().withCfRuntimesStartingFromIncluding(CfVm.JDK11).build());
+ }
+
+ public RepackageWithNestAndKeepOnPrivateTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClassFileData(getProgramClassData())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(getProgramClassData())
+ .apply(this::configureRepackaging)
+ .addKeepClassAndMembersRules(A.class)
+ .enableInliningAnnotations()
+ .addKeepMainRule(Main.class)
+ .compile()
+ .inspect(
+ inspector -> {
+ assertThat(inspector.clazz(A.class), isPresentAndNotRenamed());
+ assertThat(B.class, isNotRepackaged(inspector));
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ private List<byte[]> getProgramClassData() throws Exception {
+ return ImmutableList.of(
+ transformer(Main.class).removeInnerClasses().transform(),
+ transformer(A.class)
+ .setPrivate(A.class.getDeclaredMethod("aPrivate"))
+ .removeInnerClasses()
+ .setNest(A.class, B.class)
+ .transform(),
+ transformer(B.class).removeInnerClasses().setNest(A.class, B.class).transform());
+ }
+
+ public static class A {
+
+ @NeverInline
+ /* private */ static void aPrivate() {
+ System.out.println("Hello World!");
+ }
+ }
+
+ public static class B {
+
+ @NeverInline
+ public static void foo() {
+ A.aPrivate();
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ B.foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithNestAndKeepOnReferenceToPrivateTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithNestAndKeepOnReferenceToPrivateTest.java
new file mode 100644
index 0000000..a976256
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithNestAndKeepOnReferenceToPrivateTest.java
@@ -0,0 +1,83 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.repackage;
+
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.FLATTEN_PACKAGE_HIERARCHY;
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RepackageWithNestAndKeepOnReferenceToPrivateTest extends RepackageTestBase {
+
+ private final String EXPECTED = "Hello World!";
+
+ @Parameters(name = "{1}, kind: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ ImmutableList.of(FLATTEN_PACKAGE_HIERARCHY, REPACKAGE_CLASSES),
+ getTestParameters().withCfRuntimesStartingFromIncluding(CfVm.JDK11).build());
+ }
+
+ public RepackageWithNestAndKeepOnReferenceToPrivateTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClassFileData(getProgramClassData())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(getProgramClassData())
+ .apply(this::configureRepackaging)
+ .enableInliningAnnotations()
+ .addKeepMainRule(Main.class)
+ .compile()
+ .inspect(inspector -> assertThat(A.class, isNotRepackaged(inspector)))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ private List<byte[]> getProgramClassData() throws Exception {
+ return ImmutableList.of(
+ transformer(A.class)
+ .setPrivate(A.class.getDeclaredMethod("aPrivate"))
+ .removeInnerClasses()
+ .setNest(A.class, Main.class)
+ .transform(),
+ transformer(Main.class).removeInnerClasses().setNest(A.class, Main.class).transform());
+ }
+
+ public static class A {
+
+ @NeverInline
+ /* private */ static void aPrivate() {
+ System.out.println("Hello World!");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ A.aPrivate();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
index 53d2a7c..d22a979 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
@@ -42,8 +42,7 @@
public class KotlinInlineFunctionRetraceTest extends KotlinTestBase {
private final TestParameters parameters;
- // TODO(b/151132660): Fix filename
- private static final String FILENAME_INLINE_STATIC = "InlineFunctionKt.kt";
+ private static final String FILENAME_INLINE_STATIC = "InlineFunction.kt";
private static final String FILENAME_INLINE_INSTANCE = "InlineFunction.kt";
@Parameters(name = "{0}")
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java
index 6c43c35..878a91d 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java
@@ -358,7 +358,7 @@
@Override
public List<String> retracedStackTrace() {
- return ImmutableList.of("a.b.d(d.java)");
+ return ImmutableList.of("a.b.d(SourceFile)");
}
@Override
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
index 72221e3..e8e09aa 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -39,6 +39,7 @@
import com.android.tools.r8.retrace.stacktraces.ObfucatedExceptionClassStackTrace;
import com.android.tools.r8.retrace.stacktraces.ObfuscatedRangeToSingleLineStackTrace;
import com.android.tools.r8.retrace.stacktraces.RetraceAssertionErrorStackTrace;
+import com.android.tools.r8.retrace.stacktraces.SourceFileNameSynthesizeStackTrace;
import com.android.tools.r8.retrace.stacktraces.SourceFileWithNumberAndEmptyStackTrace;
import com.android.tools.r8.retrace.stacktraces.StackTraceForTest;
import com.android.tools.r8.retrace.stacktraces.SuppressedStackTrace;
@@ -229,6 +230,11 @@
runRetraceTest(new SourceFileWithNumberAndEmptyStackTrace());
}
+ @Test
+ public void testSourceFileNameSynthesizeStackTrace() {
+ runRetraceTest(new SourceFileNameSynthesizeStackTrace());
+ }
+
private void inspectRetraceTest(
StackTraceForTest stackTraceForTest, Consumer<Retracer> inspection) {
inspection.accept(
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/ActualIdentityStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/ActualIdentityStackTrace.java
index c86411e..9ac7068 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/ActualIdentityStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/ActualIdentityStackTrace.java
@@ -37,10 +37,10 @@
+ " privateinterfacemethods.PrivateInterfaceMethods.main(PrivateInterfaceMethods.java:9)",
" \tat dalvik.system.NativeStart.main(Native Method)",
" Caused by: java.lang.NoSuchMethodError: privateinterfacemethods.I$-CC.$default$iFoo",
- " \tat privateinterfacemethods.I$-CC.sFoo(PrivateInterfaceMethods.java:40)",
- " \tat privateinterfacemethods.I$-CC.access$000(PrivateInterfaceMethods.java:28)",
- " \tat privateinterfacemethods.I$1.<init>(PrivateInterfaceMethods.java:31)",
- " \tat privateinterfacemethods.I.<clinit>(PrivateInterfaceMethods.java:30)",
+ " \tat privateinterfacemethods.I$-CC.sFoo(I.java:40)",
+ " \tat privateinterfacemethods.I$-CC.access$000(I.java:28)",
+ " \tat privateinterfacemethods.I$1.<init>(I.java:31)",
+ " \tat privateinterfacemethods.I.<clinit>(I.java:30)",
" \t... 2 more");
}
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousMissingLineStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousMissingLineStackTrace.java
index b44b364..b92190e 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousMissingLineStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousMissingLineStackTrace.java
@@ -30,7 +30,7 @@
" <OR> at com.android.tools.r8.R8.foo(R8.java:7)",
" at com.android.tools.r8.R8.bar(R8.java:8)",
" <OR> at com.android.tools.r8.R8.foo(R8.java:8)",
- " at com.android.tools.r8.R8.main(R8.java)",
+ " at com.android.tools.r8.R8.main(Unknown Source)",
"Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
" at com.android.tools.r8.R8.bar(R8.java:9)",
" <OR> at com.android.tools.r8.R8.foo(R8.java:9)",
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousStackTrace.java
index a3a8e58..429ee6b 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousStackTrace.java
@@ -57,7 +57,7 @@
" <OR> at com.android.tools.r8.R8.foo(R8.java)",
" at com.android.tools.r8.R8.bar(R8.java)",
" <OR> at com.android.tools.r8.R8.foo(R8.java)",
- " at com.android.tools.r8.R8.main(R8.java)",
+ " at com.android.tools.r8.R8.main(Unknown Source)",
"Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
" at com.android.tools.r8.R8.bar(R8.java)",
" <OR> at com.android.tools.r8.R8.foo(R8.java)",
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/FileNameExtensionStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/FileNameExtensionStackTrace.java
index 71a3e64..ae9e0ff 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/FileNameExtensionStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/FileNameExtensionStackTrace.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.retrace.stacktraces;
+import com.android.tools.r8.utils.StringUtils;
import java.util.Arrays;
import java.util.List;
@@ -29,24 +30,24 @@
@Override
public String mapping() {
- return "foo.bar.baz -> a.b.c:";
+ return StringUtils.lines("foo.bar.baz -> a.b.c:", "R8 -> R8:");
}
@Override
public List<String> retracedStackTrace() {
return Arrays.asList(
"foo.bar.baz: Problem when compiling program",
- " at R8.main(App:800)",
+ " at R8.main(R8.java:800)",
" at R8.main(Native Method)",
- " at R8.main(Main.java:)",
- " at R8.main(Main.kt:1)",
- " at R8.main(Main.foo)",
+ " at R8.main(R8.java:)",
+ " at R8.main(R8.kt:1)",
+ " at R8.main(R8.foo)",
" at R8.main(R8.java)",
" at R8.main(R8.java)",
" at R8.main(R8.java)",
" at R8.main(R8.java:1)",
"Suppressed: foo.bar.baz: You have to write the program first",
- " at R8.retrace(App:184)",
+ " at R8.retrace(R8.java:184)",
" ... 7 more");
}
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineWithLineNumbersStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineWithLineNumbersStackTrace.java
index a7cd28d..59c9693 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineWithLineNumbersStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineWithLineNumbersStackTrace.java
@@ -37,10 +37,10 @@
public List<String> retracedStackTrace() {
return Arrays.asList(
"Exception in thread \"main\" java.lang.NullPointerException",
- "\tat com.android.tools.r8.naming.retrace.Main.method3(InliningRetraceTest.java:81)",
- "\tat com.android.tools.r8.naming.retrace.Main.method2(InliningRetraceTest.java:88)",
- "\tat com.android.tools.r8.naming.retrace.Main.method1(InliningRetraceTest.java:96)",
- "\tat com.android.tools.r8.naming.retrace.Main.main(InliningRetraceTest.java:102)");
+ "\tat com.android.tools.r8.naming.retrace.Main.method3(Main.java:81)",
+ "\tat com.android.tools.r8.naming.retrace.Main.method2(Main.java:88)",
+ "\tat com.android.tools.r8.naming.retrace.Main.method1(Main.java:96)",
+ "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java:102)");
}
@Override
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/PGStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/PGStackTrace.java
index 2732b34..8698ac9 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/PGStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/PGStackTrace.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.retrace.stacktraces;
+import com.android.tools.r8.utils.StringUtils;
import java.util.Arrays;
import java.util.List;
@@ -24,7 +25,10 @@
@Override
public String mapping() {
- return "";
+ return StringUtils.lines(
+ "com.google.apps.sectionheader.SectionHeaderListController "
+ + "-> com.google.apps.sectionheader.SectionHeaderListController:",
+ "com.google.apps.Controller " + "-> com.google.apps.Controller:");
}
@Override
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/SourceFileNameSynthesizeStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/SourceFileNameSynthesizeStackTrace.java
new file mode 100644
index 0000000..0315fc0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/SourceFileNameSynthesizeStackTrace.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace.stacktraces;
+
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
+import java.util.List;
+
+public class SourceFileNameSynthesizeStackTrace implements StackTraceForTest {
+
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return Arrays.asList(
+ "\tat mapping.a(AW779999992:21)",
+ "\tat noMappingKt.noMapping(AW779999992:21)",
+ "\tat mappingKotlin.b(AW779999992:21)");
+ }
+
+ @Override
+ public String mapping() {
+ return StringUtils.joinLines(
+ "android.support.v7.widget.ActionMenuView -> mapping:",
+ " 21:21:void invokeItem():624 -> a",
+ "android.support.v7.widget.ActionMenuViewKt -> mappingKotlin:",
+ " 21:21:void invokeItem():624 -> b");
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return Arrays.asList(
+ "\tat android.support.v7.widget.ActionMenuView.invokeItem(ActionMenuView.java:624)",
+ "\tat noMappingKt.noMapping(AW779999992:21)",
+ "\tat android.support.v7.widget.ActionMenuViewKt.invokeItem(ActionMenuView.kt:624)");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 0;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/SourceFileWithNumberAndEmptyStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/SourceFileWithNumberAndEmptyStackTrace.java
index 8a94018..1522918 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/SourceFileWithNumberAndEmptyStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/SourceFileWithNumberAndEmptyStackTrace.java
@@ -32,7 +32,7 @@
return Arrays.asList(
" at com.android.tools.r8.utils.ExceptionUtils.withR8CompilationHandler("
+ "ExceptionUtils.java:59)",
- " at com.android.tools.r8.R8.runForTesting(R.java:261)",
+ " at com.android.tools.r8.R8.runForTesting(R8.java:261)",
" at com.android.tools.r8.utils.ExceptionUtils.withR8CompilationHandler("
+ "ExceptionUtils.java:59)",
" at com.android.tools.r8.R8.runForTesting(R8.java:261)");
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/UnknownSourceStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/UnknownSourceStackTrace.java
index 4219979..76b89e4 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/UnknownSourceStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/UnknownSourceStackTrace.java
@@ -30,7 +30,7 @@
" <OR> at com.android.tools.r8.R8.foo(R8.java)",
" at com.android.tools.r8.R8.bar(R8.java)",
" <OR> at com.android.tools.r8.R8.foo(R8.java)",
- " at com.android.tools.r8.R8.main(R8.java)",
+ " at com.android.tools.r8.R8.main(Unknown Source)",
"Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
" at com.android.tools.r8.R8.bar(R8.java)",
" <OR> at com.android.tools.r8.R8.foo(R8.java)",
diff --git a/src/test/java/com/android/tools/r8/rewrite/JavaScriptScriptEngineTest.java b/src/test/java/com/android/tools/r8/rewrite/JavaScriptScriptEngineTest.java
index b299b76..0078c1b 100644
--- a/src/test/java/com/android/tools/r8/rewrite/JavaScriptScriptEngineTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/JavaScriptScriptEngineTest.java
@@ -68,7 +68,7 @@
.compile()
.assertAllWarningMessagesMatch(
anyOf(
- containsString("Missing class:"),
+ containsString("Missing class "),
containsString("required for default or static interface methods desugaring"),
equalTo("Resource 'META-INF/MANIFEST.MF' already exists.")))
.run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/rewrite/ScriptEngineTest.java b/src/test/java/com/android/tools/r8/rewrite/ScriptEngineTest.java
index c7037ca..0077cef 100644
--- a/src/test/java/com/android/tools/r8/rewrite/ScriptEngineTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/ScriptEngineTest.java
@@ -81,7 +81,7 @@
.compile()
.assertAllWarningMessagesMatch(
anyOf(
- containsString("Missing class:"),
+ containsString("Missing class "),
containsString("it is required for default or static interface methods desugaring"),
equalTo("Resource 'META-INF/MANIFEST.MF' already exists.")))
.writeToZip(path)
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java
index f0152a6..5163bbd 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java
@@ -96,7 +96,7 @@
assertFalse(matchClassName("foobar", "!foobar", "*bar"));
assertTrue(matchClassName("foo", "!boo"));
- assertFalse(matchClassName("foo", "baz,!boo"));
+ assertTrue(matchClassName("foo", "baz", "!boo"));
assertFalse(matchClassName("boo", "!boo", "**"));
assertFalse(matchClassName("boo", "!b*<1>", "**"));
@@ -105,8 +105,7 @@
assertTrue(matchClassName("boo",
ImmutableList.of(ImmutableList.of("!boo"), ImmutableList.of("**"))));
- // TODO(b/174824047): This parses as !(boo*,*foo,boofoo) and it should be !boo*,*foo,boofoo.
- assertTrue(matchClassName("boofoo", "!boo*,*foo,boofoo"));
+ assertFalse(matchClassName("boofoo", "!boo*", "*foo", "boofoo"));
assertTrue(matchClassName("boofoo",
ImmutableList.of(ImmutableList.of("!boo*,*foo"), ImmutableList.of("boofoo"))));
}
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
index d246ba7..5ad87de 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
@@ -93,8 +93,8 @@
@Override
public void error(Diagnostic error) {
String expectedErrorMessage =
- "Compilation can't be completed because the class `java.lang.Object` is missing";
- if (error.getDiagnosticMessage().contains(expectedErrorMessage)) {
+ "Compilation can't be completed because the class java.lang.Object is missing.";
+ if (error.getDiagnosticMessage().equals(expectedErrorMessage)) {
return;
}
throw new RuntimeException("Unexpected compilation error");
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationsOnTargetedMethodTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationsOnTargetedMethodTest.java
index c4d9068..ace926f 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationsOnTargetedMethodTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationsOnTargetedMethodTest.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.shaking.annotations;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -34,7 +35,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public AnnotationsOnTargetedMethodTest(TestParameters parameters) {
@@ -52,9 +53,11 @@
testForR8(parameters.getBackend())
.addInnerClasses(AnnotationsOnTargetedMethodTest.class)
.addKeepMainRule(TestClass.class)
- .addKeepRules("-keepattributes *Annotation*", "-dontobfuscate")
+ .addKeepRuntimeVisibleAnnotations()
.enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.enableNoVerticalClassMergingAnnotations()
+ .noMinification()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(expectedOutput);
@@ -91,6 +94,7 @@
void targetedMethod();
}
+ @NoHorizontalClassMerging
static class InterfaceImpl implements Interface {
@NeverInline
@@ -100,6 +104,7 @@
}
}
+ @NoHorizontalClassMerging
static class OtherInterfaceImpl implements Interface {
@NeverInline
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java
index 445e24b..7c3a2d9 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java
@@ -54,13 +54,12 @@
switch (shrinker) {
case PROGUARD6:
assertTrue(parameters.isCfRuntime());
- return testForProguard().addTestingAnnotationsAsProgramClasses();
+ return testForProguard().addInliningAnnotations().addNeverClassInliningAnnotations();
case R8:
return testForR8(parameters.getBackend())
- .addTestingAnnotationsAsProgramClasses()
.allowUnusedProguardConfigurationRules(allowDiagnosticInfoMessages)
- .enableNeverClassInliningAnnotations()
- .enableInliningAnnotations();
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations();
default:
throw new Unreachable();
}
diff --git a/src/test/java/com/android/tools/r8/shaking/negatedrules/NegatedKeepRulesTest.java b/src/test/java/com/android/tools/r8/shaking/negatedrules/NegatedKeepRulesTest.java
index a0f56b6..b8de479 100644
--- a/src/test/java/com/android/tools/r8/shaking/negatedrules/NegatedKeepRulesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/negatedrules/NegatedKeepRulesTest.java
@@ -272,6 +272,35 @@
});
}
+ @Test
+ public void testNegatedWithPositiveR8Compat() throws Exception {
+ testNegatedWithPositive(testForR8Compat(parameters.getBackend()));
+ }
+
+ @Test
+ public void testNegatedWithPositiveR8Full() throws Exception {
+ testNegatedWithPositive(testForR8(parameters.getBackend()));
+ }
+
+ @Test
+ public void testNegatedWithPositiveProguard() throws Exception {
+ testNegatedWithPositive(testForProguard(ProguardVersion.V7_0_0).addKeepRules("-dontwarn"));
+ }
+
+ public void testNegatedWithPositive(TestShrinkerBuilder<?, ?, ?, ?, ?> testBuilder)
+ throws Exception {
+ run(testBuilder.addKeepRules("-keep class !**$Foo*,**Bar { *; }"))
+ .inspect(
+ inspector -> {
+ assertThat(inspector.clazz(BarBar.class), isPresent());
+ assertThat(inspector.clazz(FooBar.class), not(isPresent()));
+ assertThat(inspector.clazz(A.class), not(isPresent()));
+ assertThat(inspector.clazz(B.class), not(isPresent()));
+ assertThat(inspector.clazz(C.class), not(isPresent()));
+ assertThat(inspector.clazz(D.class), not(isPresent()));
+ });
+ }
+
private TestCompileResult<?, ?> run(TestShrinkerBuilder<?, ?, ?, ?, ?> testBuilder)
throws Exception {
return testBuilder
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AssertUtils.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AssertUtils.java
index c113b0c..87eb583 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AssertUtils.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AssertUtils.java
@@ -13,6 +13,16 @@
public class AssertUtils {
+ public static <E extends Throwable> void assertFailsCompilation(ThrowingAction<E> action)
+ throws E {
+ assertFailsCompilationIf(true, action);
+ }
+
+ public static <E extends Throwable> void assertFailsCompilation(
+ ThrowingAction<E> action, Consumer<Throwable> consumer) throws E {
+ assertFailsCompilationIf(true, action, consumer);
+ }
+
public static <E extends Throwable> void assertFailsCompilationIf(
boolean condition, ThrowingAction<E> action) throws E {
assertFailsCompilationIf(condition, action, null);
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java
index 654f3a1..7059ae1 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java
@@ -153,6 +153,10 @@
return invokesMethod(null, null, name, null);
}
+ public static Predicate<InstructionSubject> isInvokeWithTarget(MethodSubject target) {
+ return isInvokeWithTarget(target.getMethod().getReference());
+ }
+
public static Predicate<InstructionSubject> isInvokeWithTarget(DexMethod target) {
return instruction -> instruction.isInvoke() && instruction.getMethod() == target;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java
index 2d41c1f..38f7e92 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.utils.codeinspector;
import static com.android.tools.r8.TestBase.toDexType;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.graph.DexItemFactory;
@@ -25,6 +26,15 @@
return this;
}
+ public EnumUnboxingInspector assertUnboxedIf(boolean condition, Class<? extends Enum<?>> clazz) {
+ if (condition) {
+ assertUnboxed(clazz);
+ } else {
+ assertNotUnboxed(clazz);
+ }
+ return this;
+ }
+
@SafeVarargs
public final EnumUnboxingInspector assertUnboxed(Class<? extends Enum<?>>... classes) {
for (Class<? extends Enum<?>> clazz : classes) {
@@ -32,4 +42,9 @@
}
return this;
}
+
+ public EnumUnboxingInspector assertNotUnboxed(Class<? extends Enum<?>> clazz) {
+ assertFalse(unboxedEnums.isUnboxedEnum(toDexType(clazz, dexItemFactory)));
+ return this;
+ }
}
diff --git a/third_party/opensource-apps/tivi.tar.gz.sha1 b/third_party/opensource-apps/tivi.tar.gz.sha1
index 6bd16e2..50f9741 100644
--- a/third_party/opensource-apps/tivi.tar.gz.sha1
+++ b/third_party/opensource-apps/tivi.tar.gz.sha1
@@ -1 +1 @@
-2f4adb11dcc8c56f377ee9945d47e88313bc5855
\ No newline at end of file
+cde92f3abe4e6a10a6c7ec865d6240c7b625a3a2
\ No newline at end of file
diff --git a/tools/compiledump.py b/tools/compiledump.py
index ebf3371..29fed2c 100755
--- a/tools/compiledump.py
+++ b/tools/compiledump.py
@@ -174,10 +174,10 @@
dump_file = zipfile.ZipFile(os.path.abspath(args.dump), 'r')
with utils.ChangedWorkingDirectory(temp):
if args.override or not os.path.isfile(
- os.path.join(temp, 'proguard.config')):
+ os.path.join(temp, 'r8-version')):
print("Extracting into: %s" % temp)
dump_file.extractall()
- if not os.path.isfile(os.path.join(temp, 'proguard.config')):
+ if not os.path.isfile(os.path.join(temp, 'r8-version')):
error("Did not extract into %s. Either the zip file is invalid or the "
"dump is missing files" % temp)
return Dump(temp)
diff --git a/tools/run_on_app.py b/tools/run_on_app.py
index 1bd62e3..983ce6d 100755
--- a/tools/run_on_app.py
+++ b/tools/run_on_app.py
@@ -11,12 +11,15 @@
import sys
import time
+import archive
+import as_utils
import gmail_data
import gmscore_data
import golem
import nest_data
from sanitize_libraries import SanitizeLibraries, SanitizeLibrariesInPgconf
import toolhelper
+import update_prebuilds_in_android
import utils
import youtube_data
import chrome_data
@@ -49,6 +52,8 @@
help='Compiler build to use',
choices=COMPILER_BUILDS,
default='lib')
+ result.add_option('--hash',
+ help='The version of D8/R8 to use')
result.add_option('--app',
help='What app to run on',
choices=APPS)
@@ -186,8 +191,12 @@
help='Disable compiler logging',
default=False,
action='store_true')
-
- return result.parse_args(argv)
+ (options, args) = result.parse_args(argv)
+ assert not options.hash or options.no_build, (
+ 'Argument --no-build is required when using --hash')
+ assert not options.hash or options.compiler_build == 'full', (
+ 'Compiler build lib not yet supported with --hash')
+ return (options, args)
# Most apps have -printmapping, -printseeds, -printusage and
# -printconfiguration in the Proguard configuration. However we don't
@@ -556,6 +565,16 @@
and not os.path.exists(outdir):
os.makedirs(outdir)
+ if options.hash:
+ # Download r8-<hash>.jar from
+ # https://storage.googleapis.com/r8-releases/raw/<hash>/.
+ download_path = archive.GetUploadDestination(options.hash, 'r8.jar', True)
+ assert utils.file_exists_on_cloud_storage(download_path), (
+ 'Could not find r8.jar file from provided hash: %s' % options.hash)
+ destination = os.path.join(utils.LIBS, 'r8-' + options.hash + '.jar')
+ utils.download_file_from_cloud_storage(
+ download_path, destination, quiet=quiet)
+
# Additional flags for the compiler from the configuration file.
if 'flags' in values:
args.extend(values['flags'].split(' '))
@@ -601,11 +620,16 @@
build = not options.no_build and not options.golem
stderr_path = os.path.join(temp, 'stderr')
with open(stderr_path, 'w') as stderr:
+ jar = None
+ main = None
if options.compiler_build == 'full':
tool = options.compiler
else:
assert(options.compiler_build == 'lib')
tool = 'r8lib-' + options.compiler
+ if options.hash:
+ jar = os.path.join(utils.LIBS, 'r8-' + options.hash + '.jar')
+ main = 'com.android.tools.r8.' + options.compiler.upper()
exit_code = toolhelper.run(tool, args,
build=build,
debug=not options.no_debug,
@@ -617,7 +641,9 @@
timeout=options.timeout,
quiet=quiet,
cmd_prefix=[
- 'taskset', '-c', options.cpu_list] if options.cpu_list else [])
+ 'taskset', '-c', options.cpu_list] if options.cpu_list else [],
+ jar=jar,
+ main=main)
if exit_code != 0:
with open(stderr_path) as stderr:
stderr_text = stderr.read()
diff --git a/tools/run_on_app_dump.py b/tools/run_on_app_dump.py
index 0d48887..1461181 100755
--- a/tools/run_on_app_dump.py
+++ b/tools/run_on_app_dump.py
@@ -317,7 +317,7 @@
'dump_app': 'dump_app.zip',
'apk_app': 'app-release.apk',
'url': 'https://github.com/chrisbanes/tivi',
- 'revision': '8e2ddd8fe2d343264a66aa1ef8acbd4cc587e8ce',
+ 'revision': '5c6d9ed338885c59b1fc64050d92d056417bb4de',
'folder': 'tivi',
}),
App({
diff --git a/tools/toolhelper.py b/tools/toolhelper.py
index 1eee1f7..45d84e2 100644
--- a/tools/toolhelper.py
+++ b/tools/toolhelper.py
@@ -14,7 +14,7 @@
def run(tool, args, build=None, debug=True,
profile=False, track_memory_file=None, extra_args=None,
stderr=None, stdout=None, return_stdout=False, timeout=0, quiet=False,
- cmd_prefix=None):
+ cmd_prefix=None, jar=None, main=None):
cmd = []
if cmd_prefix:
cmd.extend(cmd_prefix)
@@ -31,7 +31,9 @@
cmd.append('-ea')
if profile:
cmd.append('-agentlib:hprof=cpu=samples,interval=1,depth=8')
- if tool == 'r8lib-d8':
+ if jar:
+ cmd.extend(['-cp', jar, main])
+ elif tool == 'r8lib-d8':
cmd.extend(['-cp', utils.R8LIB_JAR, 'com.android.tools.r8.D8'])
elif tool == 'r8lib-r8':
cmd.extend(['-cp', utils.R8LIB_JAR, 'com.android.tools.r8.R8'])
diff --git a/tools/update_prebuilds_in_android.py b/tools/update_prebuilds_in_android.py
index ee2b100..22c8863 100755
--- a/tools/update_prebuilds_in_android.py
+++ b/tools/update_prebuilds_in_android.py
@@ -70,20 +70,21 @@
def copy_other_targets(root, target_root):
copy_targets(root, target_root, OTHER_TARGETS, OTHER_TARGETS)
-def download_hash(root, commit_hash, target):
- download_target(root, target, commit_hash, True)
+def download_hash(root, commit_hash, target, quiet=False):
+ download_target(root, target, commit_hash, True, quiet=quiet)
def download_version(root, version, target):
download_target(root, target, version, False)
-def download_target(root, target, hash_or_version, is_hash):
+def download_target(root, target, hash_or_version, is_hash, quiet=False):
download_path = os.path.join(root, target)
url = archive.GetUploadDestination(
hash_or_version,
target,
is_hash)
- print 'Downloading: ' + url + ' -> ' + download_path
- utils.download_file_from_cloud_storage(url, download_path)
+ if not quiet:
+ print 'Downloading: ' + url + ' -> ' + download_path
+ utils.download_file_from_cloud_storage(url, download_path, quiet=quiet)
def main_download(hash, maps, targets, target_root, version):
jar_targets = JAR_TARGETS_MAP[targets]
diff --git a/tools/utils.py b/tools/utils.py
index 5396c0a..bed28d8 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -316,9 +316,9 @@
PrintCmd(cmd)
return subprocess.call(cmd) == 0
-def download_file_from_cloud_storage(source, destination):
+def download_file_from_cloud_storage(source, destination, quiet=False):
cmd = ['gsutil.py', 'cp', source, destination]
- PrintCmd(cmd)
+ PrintCmd(cmd, quiet=quiet)
subprocess.check_call(cmd)
def create_archive(name, sources=None):