Merge commit '1b868a6d67a6fd9b141bead5148cd42fc2c1c738' into dev-release
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 20faecf..cc80809 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -2,6 +2,16 @@
# 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.
+from os import path
+from subprocess import check_output, Popen, PIPE, STDOUT
+
+FMT_CMD = path.join(
+ 'third_party',
+ 'google-java-format',
+ 'google-java-format-google-java-format-1.7',
+ 'scripts',
+ 'google-java-format-diff.py')
+
def CheckDoNotMerge(input_api, output_api):
for l in input_api.change.FullDescriptionText().splitlines():
if l.lower().startswith('do not merge'):
@@ -9,7 +19,42 @@
return [output_api.PresubmitPromptWarning(msg, [])]
return []
-def CheckChangeOnUpload(input_api, output_api):
+def CheckFormatting(input_api, output_api):
+ branch = (
+ check_output(['git', 'cl', 'upstream'])
+ .strip()
+ .replace('refs/heads/', ''))
results = []
+ for f in input_api.AffectedFiles():
+ path = f.LocalPath()
+ if not path.endswith('.java'):
+ continue
+ diff = check_output(
+ ['git', 'diff', '--no-prefix', '-U0', branch, '--', path])
+ proc = Popen(FMT_CMD, stdin=PIPE, stdout=PIPE, stderr=STDOUT)
+ (stdout, stderr) = proc.communicate(input=diff)
+ if len(stdout) > 0:
+ results.append(output_api.PresubmitError(stdout))
+ if len(results) > 0:
+ results.append(output_api.PresubmitError(
+ """Please fix the formatting by running:
+
+ git diff -U0 $(git cl upstream) | %s -p1 -i
+
+or bypass the checks with:
+
+ cl upload --bypass-hooks
+ """ % FMT_CMD))
+ return results
+
+def CheckChange(input_api, output_api):
+ results = []
+ results.extend(CheckFormatting(input_api, output_api))
results.extend(CheckDoNotMerge(input_api, output_api))
return results
+
+def CheckChangeOnCommit(input_api, output_api):
+ return CheckChange(input_api, output_api)
+
+def CheckChangeOnUpload(input_api, output_api):
+ return CheckChange(input_api, output_api)
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 85abb47..1fa0bb0 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -21,6 +21,8 @@
import com.android.tools.r8.ir.desugar.PrefixRewritingMapper;
import com.android.tools.r8.ir.optimize.AssertionsRewriter;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
+import com.android.tools.r8.jar.CfApplicationWriter;
+import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.naming.PrefixRewritingNamingLens;
import com.android.tools.r8.origin.CommandLineOrigin;
import com.android.tools.r8.utils.AndroidApp;
@@ -218,15 +220,27 @@
markers.add(marker);
}
- new ApplicationWriter(
- app,
- null,
- options,
- marker == null ? null : ImmutableList.copyOf(markers),
- GraphLense.getIdentityLense(),
- PrefixRewritingNamingLens.createPrefixRewritingNamingLens(appView),
- null)
- .write(executor);
+ if (options.isGeneratingClassFiles()) {
+ new CfApplicationWriter(
+ app,
+ appView,
+ options,
+ marker,
+ GraphLense.getIdentityLense(),
+ NamingLens.getIdentityLens(),
+ null)
+ .write(options.getClassFileConsumer(), executor);
+ } else {
+ new ApplicationWriter(
+ app,
+ null,
+ options,
+ marker == null ? null : ImmutableList.copyOf(markers),
+ GraphLense.getIdentityLense(),
+ PrefixRewritingNamingLens.createPrefixRewritingNamingLens(appView),
+ null)
+ .write(executor);
+ }
options.printWarnings();
} catch (ExecutionException e) {
throw unwrapExecutionException(e);
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index d421ec3..b7c9347 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -175,7 +175,7 @@
void validate() {
Reporter reporter = getReporter();
if (getProgramConsumer() instanceof ClassFileConsumer) {
- reporter.error("D8 does not support compiling to Java class files");
+ reporter.warning("Compiling to Java class files with D8 is not officially supported");
}
if (getAppBuilder().hasMainDexList()) {
if (intermediate) {
@@ -332,6 +332,9 @@
assert !internal.debug;
internal.debug = getMode() == CompilationMode.DEBUG;
internal.programConsumer = getProgramConsumer();
+ if (internal.programConsumer instanceof ClassFileConsumer) {
+ internal.enableCfInterfaceMethodDesugaring = true;
+ }
internal.mainDexListConsumer = getMainDexListConsumer();
internal.minimalMainDex = internal.debug;
internal.minApiLevel = getMinApiLevel();
diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java
index f66fff8..a6827d3 100644
--- a/src/main/java/com/android/tools/r8/D8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
@@ -206,6 +206,8 @@
compilationMode = CompilationMode.RELEASE;
} else if (arg.equals("--file-per-class")) {
outputMode = OutputMode.DexFilePerClass;
+ } else if (arg.equals("--classfile")) {
+ outputMode = OutputMode.ClassFile;
} else if (arg.equals("--output")) {
if (outputPath != null) {
builder.error(
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 2086f5e..8817968 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -25,6 +25,7 @@
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.EnumValueInfoMapCollection;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
import com.android.tools.r8.graph.analysis.ClassInitializerAssertionEnablingAnalysis;
@@ -336,6 +337,7 @@
assert appView.rootSet().verifyKeptFieldsAreAccessedAndLive(appViewWithLiveness.appInfo());
assert appView.rootSet().verifyKeptMethodsAreTargetedAndLive(appViewWithLiveness.appInfo());
assert appView.rootSet().verifyKeptTypesAreLive(appViewWithLiveness.appInfo());
+ assert appView.rootSet().verifyKeptItemsAreKept(appView.appInfo().app(), appView.appInfo());
appView.rootSet().checkAllRulesAreUsed(options);
@@ -512,7 +514,7 @@
assert appView.dexItemFactory().verifyNoCachedTypeLatticeElements();
// Collect switch maps and ordinals maps.
- if (options.enableEnumValueOptimization) {
+ if (options.enableEnumSwitchMapRemoval) {
appViewWithLiveness.setAppInfo(new SwitchMapCollector(appViewWithLiveness).run());
}
if (options.enableEnumValueOptimization || options.enableEnumUnboxing) {
@@ -560,6 +562,10 @@
// Collect the already pruned types before creating a new app info without liveness.
Set<DexType> prunedTypes = appView.withLiveness().appInfo().getPrunedTypes();
+ // TODO: move to appview.
+ EnumValueInfoMapCollection enumValueInfoMapCollection =
+ appViewWithLiveness.appInfo().getEnumValueInfoMapCollection();
+
if (!options.mainDexKeepRules.isEmpty()) {
appView.setAppInfo(new AppInfoWithSubtyping(application));
// No need to build a new main dex root set
@@ -624,11 +630,18 @@
Enqueuer enqueuer = EnqueuerFactory.createForFinalTreeShaking(appView, keptGraphConsumer);
appView.setAppInfo(
- enqueuer.traceApplication(
- appView.rootSet(),
- options.getProguardConfiguration().getDontWarnPatterns(),
- executorService,
- timing));
+ enqueuer
+ .traceApplication(
+ appView.rootSet(),
+ options.getProguardConfiguration().getDontWarnPatterns(),
+ executorService,
+ timing)
+ .withEnumValueInfoMaps(enumValueInfoMapCollection));
+
+ appView.withGeneratedMessageLiteBuilderShrinker(
+ shrinker ->
+ shrinker.removeDeadBuilderReferencesFromDynamicMethods(
+ appViewWithLiveness, executorService, timing));
if (Log.ENABLED && Log.isLoggingEnabledFor(GeneratedExtensionRegistryShrinker.class)) {
appView.withGeneratedExtensionRegistryShrinker(
@@ -645,6 +658,7 @@
TreePruner pruner = new TreePruner(appViewWithLiveness, treePrunerConfiguration);
application = pruner.run(application);
+
if (options.usageInformationConsumer != null) {
ExceptionUtils.withFinishedResourceHandler(
options.reporter, options.usageInformationConsumer);
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 2992824..32a4bf7 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -250,7 +250,9 @@
if (appView != null) {
appView.appInfo().disableDefinitionForAssert();
}
+ namingLens.setIsSortingBeforeWriting(true);
application.dexItemFactory.sort(namingLens);
+ namingLens.setIsSortingBeforeWriting(false);
if (appView != null) {
appView.appInfo().enableDefinitionForAssert();
}
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 df91f96..f15e96e 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -47,6 +47,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.DexValue.DexValueKind;
import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
import com.android.tools.r8.graph.DexValue.DexValueMethodType;
import com.android.tools.r8.graph.DexValue.DexValueNull;
@@ -184,93 +185,112 @@
int header = dexReader.get() & 0xff;
int valueArg = header >> 5;
int valueType = header & 0x1f;
- switch (valueType) {
- case DexValue.VALUE_BYTE: {
- assert valueArg == 0;
- byte value = (byte) parseSigned(dexReader, 1);
- return DexValue.DexValueByte.create(value);
- }
- case DexValue.VALUE_SHORT: {
- int size = valueArg + 1;
- short value = (short) parseSigned(dexReader, size);
- return DexValue.DexValueShort.create(value);
- }
- case DexValue.VALUE_CHAR: {
- int size = valueArg + 1;
- char value = (char) parseUnsigned(dexReader, size);
- return DexValue.DexValueChar.create(value);
- }
- case DexValue.VALUE_INT: {
- int size = valueArg + 1;
- int value = (int) parseSigned(dexReader, size);
- return DexValue.DexValueInt.create(value);
- }
- case DexValue.VALUE_LONG: {
- int size = valueArg + 1;
- long value = parseSigned(dexReader, size);
- return DexValue.DexValueLong.create(value);
- }
- case DexValue.VALUE_FLOAT: {
- int size = valueArg + 1;
- return DexValue.DexValueFloat.create(parseFloat(dexReader, size));
- }
- case DexValue.VALUE_DOUBLE: {
- int size = valueArg + 1;
- return DexValue.DexValueDouble.create(parseDouble(dexReader, size));
- }
- case DexValue.VALUE_STRING: {
- int size = valueArg + 1;
- int index = (int) parseUnsigned(dexReader, size);
- DexString value = indexedItems.getString(index);
- return new DexValue.DexValueString(value);
- }
- case DexValue.VALUE_TYPE: {
- int size = valueArg + 1;
- DexType value = indexedItems.getType((int) parseUnsigned(dexReader, size));
- return new DexValue.DexValueType(value);
- }
- case DexValue.VALUE_FIELD: {
- int size = valueArg + 1;
- DexField value = indexedItems.getField((int) parseUnsigned(dexReader, size));
- checkName(value.name);
- return new DexValue.DexValueField(value);
- }
- case DexValue.VALUE_METHOD: {
- int size = valueArg + 1;
- DexMethod value = indexedItems.getMethod((int) parseUnsigned(dexReader, size));
- checkName(value.name);
- return new DexValue.DexValueMethod(value);
- }
- case DexValue.VALUE_ENUM: {
- int size = valueArg + 1;
- DexField value = indexedItems.getField((int) parseUnsigned(dexReader, size));
- return new DexValue.DexValueEnum(value);
- }
- case DexValue.VALUE_ARRAY: {
- assert valueArg == 0;
- return new DexValue.DexValueArray(parseEncodedArrayValues());
- }
- case DexValue.VALUE_ANNOTATION: {
- assert valueArg == 0;
- return new DexValue.DexValueAnnotation(parseEncodedAnnotation());
- }
- case DexValue.VALUE_NULL: {
- assert valueArg == 0;
- return DexValueNull.NULL;
- }
- case DexValue.VALUE_BOOLEAN: {
- // 0 is false, and 1 is true.
- return DexValue.DexValueBoolean.create(valueArg != 0);
- }
- case DexValue.VALUE_METHOD_TYPE: {
- int size = valueArg + 1;
- DexProto value = indexedItems.getProto((int) parseUnsigned(dexReader, size));
- return new DexValue.DexValueMethodType(value);
- }
- case DexValue.VALUE_METHOD_HANDLE: {
- int size = valueArg + 1;
- DexMethodHandle value = indexedItems.getMethodHandle((int) parseUnsigned(dexReader, size));
- return new DexValue.DexValueMethodHandle(value);
+ switch (DexValueKind.fromId(valueType)) {
+ case BYTE:
+ {
+ assert valueArg == 0;
+ byte value = (byte) parseSigned(dexReader, 1);
+ return DexValue.DexValueByte.create(value);
+ }
+ case SHORT:
+ {
+ int size = valueArg + 1;
+ short value = (short) parseSigned(dexReader, size);
+ return DexValue.DexValueShort.create(value);
+ }
+ case CHAR:
+ {
+ int size = valueArg + 1;
+ char value = (char) parseUnsigned(dexReader, size);
+ return DexValue.DexValueChar.create(value);
+ }
+ case INT:
+ {
+ int size = valueArg + 1;
+ int value = (int) parseSigned(dexReader, size);
+ return DexValue.DexValueInt.create(value);
+ }
+ case LONG:
+ {
+ int size = valueArg + 1;
+ long value = parseSigned(dexReader, size);
+ return DexValue.DexValueLong.create(value);
+ }
+ case FLOAT:
+ {
+ int size = valueArg + 1;
+ return DexValue.DexValueFloat.create(parseFloat(dexReader, size));
+ }
+ case DOUBLE:
+ {
+ int size = valueArg + 1;
+ return DexValue.DexValueDouble.create(parseDouble(dexReader, size));
+ }
+ case STRING:
+ {
+ int size = valueArg + 1;
+ int index = (int) parseUnsigned(dexReader, size);
+ DexString value = indexedItems.getString(index);
+ return new DexValue.DexValueString(value);
+ }
+ case TYPE:
+ {
+ int size = valueArg + 1;
+ DexType value = indexedItems.getType((int) parseUnsigned(dexReader, size));
+ return new DexValue.DexValueType(value);
+ }
+ case FIELD:
+ {
+ int size = valueArg + 1;
+ DexField value = indexedItems.getField((int) parseUnsigned(dexReader, size));
+ checkName(value.name);
+ return new DexValue.DexValueField(value);
+ }
+ case METHOD:
+ {
+ int size = valueArg + 1;
+ DexMethod value = indexedItems.getMethod((int) parseUnsigned(dexReader, size));
+ checkName(value.name);
+ return new DexValue.DexValueMethod(value);
+ }
+ case ENUM:
+ {
+ int size = valueArg + 1;
+ DexField value = indexedItems.getField((int) parseUnsigned(dexReader, size));
+ return new DexValue.DexValueEnum(value);
+ }
+ case ARRAY:
+ {
+ assert valueArg == 0;
+ return new DexValue.DexValueArray(parseEncodedArrayValues());
+ }
+ case ANNOTATION:
+ {
+ assert valueArg == 0;
+ return new DexValue.DexValueAnnotation(parseEncodedAnnotation());
+ }
+ case NULL:
+ {
+ assert valueArg == 0;
+ return DexValueNull.NULL;
+ }
+ case BOOLEAN:
+ {
+ // 0 is false, and 1 is true.
+ return DexValue.DexValueBoolean.create(valueArg != 0);
+ }
+ case METHOD_TYPE:
+ {
+ int size = valueArg + 1;
+ DexProto value = indexedItems.getProto((int) parseUnsigned(dexReader, size));
+ return new DexValue.DexValueMethodType(value);
+ }
+ case METHOD_HANDLE:
+ {
+ int size = valueArg + 1;
+ DexMethodHandle value =
+ indexedItems.getMethodHandle((int) parseUnsigned(dexReader, size));
+ return new DexValue.DexValueMethodHandle(value);
}
default:
throw new IndexOutOfBoundsException();
diff --git a/src/main/java/com/android/tools/r8/errors/CompilationError.java b/src/main/java/com/android/tools/r8/errors/CompilationError.java
index d165596..ccefc7b 100644
--- a/src/main/java/com/android/tools/r8/errors/CompilationError.java
+++ b/src/main/java/com/android/tools/r8/errors/CompilationError.java
@@ -40,6 +40,14 @@
this.position = position;
}
+ public Origin getOrigin() {
+ return origin;
+ }
+
+ public Position getPosition() {
+ return position;
+ }
+
public CompilationError withAdditionalOriginAndPositionInfo(Origin origin, Position position) {
if (this.origin == Origin.unknown() || this.position == Position.UNKNOWN) {
return new CompilationError(
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index a791cfc..640b7a1 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -98,7 +98,7 @@
assert previous == null || previous == clazz;
}
- public Collection<DexProgramClass> getSynthesizedClassesForSanityCheck() {
+ public Collection<DexProgramClass> synthesizedClasses() {
assert checkIfObsolete();
return Collections.unmodifiableCollection(synthesizedClasses.values());
}
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 35f989e..55c4ccb 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -9,10 +9,13 @@
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.utils.TraversalContinuation;
+import com.android.tools.r8.utils.WorkList;
import com.google.common.collect.Sets;
import java.util.ArrayDeque;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
+import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
@@ -203,6 +206,65 @@
return isSubtype(type, dexItemFactory().serializableType);
}
+ public List<DexProgramClass> computeProgramClassRelationChain(
+ DexProgramClass subClass, DexProgramClass superClass) {
+ assert isSubtype(subClass.type, superClass.type);
+ assert !subClass.isInterface();
+ if (!superClass.isInterface()) {
+ return computeChainInClassHierarchy(subClass, superClass.type);
+ }
+ // If the super type is an interface we first compute the program chain upwards, and in a
+ // top-down order check if the interface is a super-type to the class. Computing it this way
+ // guarantees to find the instantiated program-classes of the longest chain.
+ List<DexProgramClass> relationChain =
+ computeChainInClassHierarchy(subClass, dexItemFactory().objectType);
+ WorkList<DexType> interfaceWorklist = WorkList.newIdentityWorkList();
+ for (int i = relationChain.size() - 1; i >= 0; i--) {
+ DexProgramClass clazz = relationChain.get(i);
+ if (isInterfaceInSuperTypes(clazz, superClass.type, interfaceWorklist)) {
+ return relationChain.subList(0, i + 1);
+ }
+ }
+ return Collections.emptyList();
+ }
+
+ private boolean isInterfaceInSuperTypes(
+ DexProgramClass clazz, DexType ifaceToFind, WorkList<DexType> workList) {
+ workList.addIfNotSeen(clazz.allImmediateSupertypes());
+ while (workList.hasNext()) {
+ DexType superType = workList.next();
+ if (superType == ifaceToFind) {
+ return true;
+ }
+ DexClass superClass = definitionFor(superType);
+ if (superClass != null) {
+ workList.addIfNotSeen(superClass.allImmediateSupertypes());
+ }
+ }
+ return false;
+ }
+
+ private List<DexProgramClass> computeChainInClassHierarchy(
+ DexProgramClass subClass, DexType superType) {
+ assert isSubtype(subClass.type, superType);
+ assert !subClass.isInterface();
+ assert superType == dexItemFactory().objectType
+ || definitionFor(superType) == null
+ || !definitionFor(superType).isInterface();
+ List<DexProgramClass> relationChain = new ArrayList<>();
+ DexClass current = subClass;
+ while (current != null) {
+ if (current.isProgramClass()) {
+ relationChain.add(current.asProgramClass());
+ }
+ if (current.type == superType) {
+ return relationChain;
+ }
+ current = definitionFor(current.superType);
+ }
+ return relationChain;
+ }
+
/**
* Helper method used for emulated interface resolution (not in JVM specifications). The result
* may be abstract.
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
index 105868f..d1e70ed 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_GROUP_CLASS_NAME_PREFIX;
import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement;
+import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.WorkList;
import com.google.common.annotations.VisibleForTesting;
@@ -40,7 +41,7 @@
public void forEachInstantiatedSubType(
DexType type,
Consumer<DexProgramClass> subTypeConsumer,
- Consumer<DexCallSite> callSiteConsumer) {
+ Consumer<LambdaDescriptor> lambdaConsumer) {
WorkList<DexType> workList = WorkList.newIdentityWorkList();
workList.addIfNotSeen(type);
while (workList.hasNext()) {
@@ -52,7 +53,7 @@
workList.addIfNotSeen(allImmediateSubtypes(subType));
}
// TODO(b/148769279): Change this when we have information about callsites.
- callSiteConsumer.accept(null);
+ // This should effectively disappear once AppInfoWithLiveness implements support.
}
private static class TypeInfo {
@@ -356,7 +357,7 @@
return true;
}
- protected boolean hasAnyInstantiatedLambdas(DexProgramClass clazz) {
+ public boolean hasAnyInstantiatedLambdas(DexProgramClass clazz) {
assert checkIfObsolete();
return true; // Don't know, there might be.
}
@@ -480,9 +481,8 @@
}
public boolean mayHaveFinalizeMethodDirectlyOrIndirectly(ClassTypeLatticeElement type) {
- Set<DexType> interfaces = type.getInterfaces();
- if (!interfaces.isEmpty()) {
- for (DexType interfaceType : interfaces) {
+ if (type.getClassType() == dexItemFactory().objectType && !type.getInterfaces().isEmpty()) {
+ for (DexType interfaceType : type.getInterfaces()) {
if (computeMayHaveFinalizeMethodDirectlyOrIndirectlyIfAbsent(interfaceType, false)) {
return true;
}
@@ -509,13 +509,13 @@
if (clazz.isProgramClass()) {
if (lookUpwards) {
DexEncodedMethod resolutionResult =
- resolveMethod(type, dexItemFactory().objectMethods.finalize).getSingleTarget();
+ resolveMethod(type, dexItemFactory().objectMembers.finalize).getSingleTarget();
if (resolutionResult != null && resolutionResult.isProgramMethod(this)) {
mayHaveFinalizeMethodDirectlyOrIndirectlyCache.put(type, true);
return true;
}
} else {
- if (clazz.lookupVirtualMethod(dexItemFactory().objectMethods.finalize) != null) {
+ if (clazz.lookupVirtualMethod(dexItemFactory().objectMembers.finalize) != null) {
mayHaveFinalizeMethodDirectlyOrIndirectlyCache.put(type, true);
return true;
}
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 8e0dfc4..9ed88a6 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -19,12 +19,13 @@
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoFactory;
import com.android.tools.r8.ir.optimize.library.LibraryMethodOptimizer;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.LibraryModeledPredicate;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OptionalBool;
+import com.android.tools.r8.utils.ThrowingConsumer;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableSet;
-import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
@@ -32,7 +33,7 @@
import java.util.function.Function;
import java.util.function.Predicate;
-public class AppView<T extends AppInfo> implements DexDefinitionSupplier {
+public class AppView<T extends AppInfo> implements DexDefinitionSupplier, LibraryModeledPredicate {
private enum WholeProgramOptimizations {
ON,
@@ -65,7 +66,7 @@
private Set<DexMethod> unneededVisibilityBridgeMethods = ImmutableSet.of();
private HorizontallyMergedLambdaClasses horizontallyMergedLambdaClasses;
private VerticallyMergedClasses verticallyMergedClasses;
- private Set<DexType> unboxedEnums = Collections.emptySet();
+ private EnumValueInfoMapCollection unboxedEnums = EnumValueInfoMapCollection.empty();
private Map<DexClass, DexValueString> sourceDebugExtensions = new IdentityHashMap<>();
@@ -108,6 +109,11 @@
}
}
+ @Override
+ public boolean isModeled(DexType type) {
+ return libraryMethodOptimizer.isModeled(type);
+ }
+
public static <T extends AppInfo> AppView<T> createForD8(T appInfo, InternalOptions options) {
return new AppView<>(appInfo, WholeProgramOptimizations.OFF, options);
}
@@ -268,8 +274,8 @@
}
}
- public void withGeneratedMessageLiteBuilderShrinker(
- Consumer<GeneratedMessageLiteBuilderShrinker> consumer) {
+ public <E extends Throwable> void withGeneratedMessageLiteBuilderShrinker(
+ ThrowingConsumer<GeneratedMessageLiteBuilderShrinker, E> consumer) throws E {
if (protoShrinker != null && protoShrinker.generatedMessageLiteBuilderShrinker != null) {
consumer.accept(protoShrinker.generatedMessageLiteBuilderShrinker);
}
@@ -370,12 +376,16 @@
this.verticallyMergedClasses = verticallyMergedClasses;
}
- public void setUnboxedEnums(Set<DexType> unboxedEnums) {
+ public EnumValueInfoMapCollection unboxedEnums() {
+ return unboxedEnums;
+ }
+
+ public void setUnboxedEnums(EnumValueInfoMapCollection unboxedEnums) {
this.unboxedEnums = unboxedEnums;
}
public boolean validateUnboxedEnumsHaveBeenPruned() {
- for (DexType unboxedEnum : unboxedEnums) {
+ for (DexType unboxedEnum : unboxedEnums.enumSet()) {
assert definitionForProgramType(unboxedEnum) == null
: "Enum " + unboxedEnum + " has been unboxed but is still in the program.";
assert appInfo().withLiveness().wasPruned(unboxedEnum)
diff --git a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
index 2a6154d..74735f4 100644
--- a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
+++ b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
@@ -89,7 +89,11 @@
: FieldSignature.fromDexField(field.field);
writeAnnotations(field.annotations(), ps);
ps.print(field.accessFlags + " ");
- ps.println(fieldSignature);
+ ps.print(fieldSignature);
+ if (field.isStatic() && field.hasExplicitStaticValue()) {
+ ps.print(" = " + field.getStaticValue());
+ }
+ ps.println();
}
}
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 c474718..a75fd4e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -686,6 +686,10 @@
return null;
}
+ public boolean isPublic() {
+ return accessFlags.isPublic();
+ }
+
@Override
public boolean isStatic() {
return accessFlags.isStatic();
@@ -859,7 +863,7 @@
}
public boolean definesFinalizer(DexItemFactory factory) {
- return lookupVirtualMethod(factory.objectMethods.finalize) != null;
+ return lookupVirtualMethod(factory.objectMembers.finalize) != null;
}
public boolean defaultValuesForStaticFieldsMayTriggerAllocation() {
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
index ded00bd..37f8853 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
@@ -6,7 +6,7 @@
import com.android.tools.r8.errors.Unreachable;
-public class DexClassAndMethod {
+public class DexClassAndMethod implements LookupTarget {
private final DexClass holder;
private final DexEncodedMethod method;
@@ -35,6 +35,16 @@
throw new Unreachable("Unsupported attempt at computing the hashcode of DexClassAndMethod");
}
+ @Override
+ public boolean isMethodTarget() {
+ return true;
+ }
+
+ @Override
+ public DexClassAndMethod asMethodTarget() {
+ return this;
+ }
+
public DexClass getHolder() {
return holder;
}
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 cba6dfc..e8bd9dc 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -138,6 +138,10 @@
return accessFlags.isPrivate();
}
+ public boolean isPublic() {
+ return accessFlags.isPublic();
+ }
+
@Override
public boolean isStaticMember() {
return isStatic();
@@ -158,6 +162,11 @@
this.staticValue = staticValue;
}
+ public void clearStaticValue() {
+ assert accessFlags.isStatic();
+ this.staticValue = null;
+ }
+
public DexValue getStaticValue() {
assert accessFlags.isStatic();
return staticValue == null ? DexValue.defaultForType(field.type) : staticValue;
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 8cb71bb..58b768d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -406,7 +406,7 @@
new StringBuildingMethods(stringBufferType);
public final BooleanMembers booleanMembers = new BooleanMembers();
public final ObjectsMethods objectsMethods = new ObjectsMethods();
- public final ObjectMethods objectMethods = new ObjectMethods();
+ public final ObjectMembers objectMembers = new ObjectMembers();
public final StringMethods stringMethods = new StringMethods();
public final LongMethods longMethods = new LongMethods();
public final DoubleMethods doubleMethods = new DoubleMethods();
@@ -595,8 +595,10 @@
public Map<DexMethod, Predicate<InvokeMethod>> libraryMethodsWithoutSideEffects =
Streams.<Pair<DexMethod, Predicate<InvokeMethod>>>concat(
Stream.of(new Pair<>(enumMethods.constructor, alwaysTrue())),
- Stream.of(new Pair<>(objectMethods.constructor, alwaysTrue())),
- Stream.of(new Pair<>(objectMethods.getClass, alwaysTrue())),
+ Stream.of(new Pair<>(npeMethods.init, alwaysTrue())),
+ Stream.of(new Pair<>(npeMethods.initWithMessage, alwaysTrue())),
+ Stream.of(new Pair<>(objectMembers.constructor, alwaysTrue())),
+ Stream.of(new Pair<>(objectMembers.getClass, alwaysTrue())),
mapToPredicate(classMethods.getNames, alwaysTrue()),
mapToPredicate(
stringBufferMethods.constructorMethods,
@@ -625,12 +627,20 @@
public Set<DexType> libraryTypesAssumedToBePresent =
ImmutableSet.<DexType>builder()
- .add(objectType, callableType, stringBufferType, stringBuilderType, stringType)
+ .add(
+ callableType,
+ enumType,
+ npeType,
+ objectType,
+ stringBufferType,
+ stringBuilderType,
+ stringType)
.addAll(primitiveToBoxed.values())
.build();
public Set<DexType> libraryClassesWithoutStaticInitialization =
- ImmutableSet.of(boxedBooleanType, enumType, objectType, stringBufferType, stringBuilderType);
+ ImmutableSet.of(
+ boxedBooleanType, enumType, npeType, objectType, stringBufferType, stringBuilderType);
private boolean skipNameValidationForTesting = false;
@@ -735,16 +745,26 @@
}
}
- public class ObjectMethods {
+ public class ObjectMembers {
+ /**
+ * This field is not on {@link Object}, but will be synthesized on program classes as a static
+ * field, for the compiler to have a principled way to trigger the initialization of a given
+ * class.
+ */
+ public final DexField clinitField = createField(objectType, intType, "$r8$clinit");
+
+ public final DexMethod clone;
public final DexMethod getClass;
public final DexMethod constructor;
public final DexMethod finalize;
public final DexMethod toString;
- private ObjectMethods() {
- getClass = createMethod(objectDescriptor, getClassMethodName, classDescriptor,
- DexString.EMPTY_ARRAY);
+ private ObjectMembers() {
+ // The clone method is installed on each array, so one has to use method.match(clone).
+ clone = createMethod(objectType, createProto(objectType), cloneMethodName);
+ getClass = createMethod(objectDescriptor,
+ getClassMethodName, classDescriptor, DexString.EMPTY_ARRAY);
constructor = createMethod(objectDescriptor,
constructorMethodName, voidType.descriptor, DexString.EMPTY_ARRAY);
finalize = createMethod(objectDescriptor,
@@ -941,16 +961,10 @@
public class NullPointerExceptionMethods {
- public final DexMethod init;
-
- private NullPointerExceptionMethods() {
- init =
- createMethod(
- npeDescriptor,
- constructorMethodName,
- voidDescriptor,
- DexString.EMPTY_ARRAY);
- }
+ public final DexMethod init =
+ createMethod(npeType, createProto(voidType), constructorMethodName);
+ public final DexMethod initWithMessage =
+ createMethod(npeType, createProto(voidType, stringType), constructorMethodName);
}
/**
diff --git a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
index 8c622ae..4b0e1fb 100644
--- a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
@@ -58,7 +58,7 @@
// Set all static field values to unknown. We don't want to use the value from the library
// at compile time, as it can be different at runtime.
for (DexEncodedField staticField : staticFields) {
- staticField.setStaticValue(DexValue.UNKNOWN);
+ staticField.clearStaticValue();
}
assert kind == Kind.CF : "Invalid kind " + kind + " for library-path class " + type;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexProto.java b/src/main/java/com/android/tools/r8/graph/DexProto.java
index 5c05605..43d6a29 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProto.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProto.java
@@ -20,6 +20,10 @@
this.parameters = parameters;
}
+ public DexType getParameter(int index) {
+ return parameters.values[index];
+ }
+
@Override
public int computeHashCode() {
return shorty.hashCode()
diff --git a/src/main/java/com/android/tools/r8/graph/DexValue.java b/src/main/java/com/android/tools/r8/graph/DexValue.java
index f105f9c..794f7c8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexValue.java
+++ b/src/main/java/com/android/tools/r8/graph/DexValue.java
@@ -26,28 +26,89 @@
public abstract class DexValue extends DexItem {
+ public enum DexValueKind {
+ BYTE(0x00),
+ SHORT(0x02),
+ CHAR(0x03),
+ INT(0x04),
+ LONG(0x06),
+ FLOAT(0x10),
+ DOUBLE(0x11),
+ METHOD_TYPE(0x15),
+ METHOD_HANDLE(0x16),
+ STRING(0x17),
+ TYPE(0x18),
+ FIELD(0x19),
+ METHOD(0x1a),
+ ENUM(0x1b),
+ ARRAY(0x1c),
+ ANNOTATION(0x1d),
+ NULL(0x1e),
+ BOOLEAN(0x1f);
+
+ public static DexValueKind fromId(int id) {
+ switch (id) {
+ case 0x00:
+ return BYTE;
+ case 0x02:
+ return SHORT;
+ case 0x03:
+ return CHAR;
+ case 0x04:
+ return INT;
+ case 0x06:
+ return LONG;
+ case 0x10:
+ return FLOAT;
+ case 0x11:
+ return DOUBLE;
+ case 0x15:
+ return METHOD_TYPE;
+ case 0x16:
+ return METHOD_HANDLE;
+ case 0x17:
+ return STRING;
+ case 0x18:
+ return TYPE;
+ case 0x19:
+ return FIELD;
+ case 0x1a:
+ return METHOD;
+ case 0x1b:
+ return ENUM;
+ case 0x1c:
+ return ARRAY;
+ case 0x1d:
+ return ANNOTATION;
+ case 0x1e:
+ return NULL;
+ case 0x1f:
+ return BOOLEAN;
+ default:
+ throw new Unreachable();
+ }
+ }
+
+ private final byte b;
+
+ DexValueKind(int b) {
+ this.b = (byte) b;
+ }
+
+ byte toByte() {
+ return b;
+ }
+ }
+
public static final DexValue[] EMPTY_ARRAY = {};
- public static final UnknownDexValue UNKNOWN = UnknownDexValue.UNKNOWN;
+ public boolean isDexItemBasedValueString() {
+ return false;
+ }
- public static final byte VALUE_BYTE = 0x00;
- public static final byte VALUE_SHORT = 0x02;
- public static final byte VALUE_CHAR = 0x03;
- public static final byte VALUE_INT = 0x04;
- public static final byte VALUE_LONG = 0x06;
- public static final byte VALUE_FLOAT = 0x10;
- public static final byte VALUE_DOUBLE = 0x11;
- public static final byte VALUE_METHOD_TYPE = 0x15;
- public static final byte VALUE_METHOD_HANDLE = 0x16;
- public static final byte VALUE_STRING = 0x17;
- public static final byte VALUE_TYPE = 0x18;
- public static final byte VALUE_FIELD = 0x19;
- public static final byte VALUE_METHOD = 0x1a;
- public static final byte VALUE_ENUM = 0x1b;
- public static final byte VALUE_ARRAY = 0x1c;
- public static final byte VALUE_ANNOTATION = 0x1d;
- public static final byte VALUE_NULL = 0x1e;
- public static final byte VALUE_BOOLEAN = 0x1f;
+ public DexItemBasedValueString asDexItemBasedValueString() {
+ return null;
+ }
public DexValueMethodHandle asDexValueMethodHandle() {
return null;
@@ -57,30 +118,54 @@
return null;
}
- public DexValueType asDexValueType() {
- return null;
+ public boolean isDexValueArray() {
+ return false;
}
public DexValueArray asDexValueArray() {
return null;
}
- public DexValueString asDexValueString() {
+ public boolean isDexValueBoolean() {
+ return false;
+ }
+
+ public DexValueBoolean asDexValueBoolean() {
return null;
}
- public boolean isDexValueString() {
+ public boolean isDexValueByte() {
return false;
}
- public boolean isDexValueType() {
+ public DexValueByte asDexValueByte() {
+ return null;
+ }
+
+ public boolean isDexValueDouble() {
return false;
}
- public boolean isDexValueArray() {
+ public DexValueDouble asDexValueDouble() {
+ return null;
+ }
+
+ public boolean isDexValueChar() {
return false;
}
+ public DexValueChar asDexValueChar() {
+ return null;
+ }
+
+ public boolean isDexValueFloat() {
+ return false;
+ }
+
+ public DexValueFloat asDexValueFloat() {
+ return null;
+ }
+
public boolean isDexValueInt() {
return false;
}
@@ -89,7 +174,55 @@
return null;
}
- public static DexValue fromAsmBootstrapArgument(
+ public boolean isDexValueLong() {
+ return false;
+ }
+
+ public DexValueLong asDexValueLong() {
+ return null;
+ }
+
+ public boolean isDexValueNull() {
+ return false;
+ }
+
+ public DexValueNull asDexValueNull() {
+ return null;
+ }
+
+ public boolean isDexValueNumber() {
+ return false;
+ }
+
+ public DexValueNumber asDexValueNumber() {
+ return null;
+ }
+
+ public boolean isDexValueShort() {
+ return false;
+ }
+
+ public DexValueShort asDexValueShort() {
+ return null;
+ }
+
+ public boolean isDexValueString() {
+ return false;
+ }
+
+ public DexValueString asDexValueString() {
+ return null;
+ }
+
+ public boolean isDexValueType() {
+ return false;
+ }
+
+ public DexValueType asDexValueType() {
+ return null;
+ }
+
+ static DexValue fromAsmBootstrapArgument(
Object value, JarApplicationReader application, DexType clazz) {
if (value instanceof Integer) {
return DexValue.DexValueInt.create((Integer) value);
@@ -123,8 +256,8 @@
}
}
- private static void writeHeader(byte type, int arg, DexOutputBuffer dest) {
- dest.putByte((byte) ((arg << 5) | type));
+ private static void writeHeader(DexValueKind kind, int arg, DexOutputBuffer dest) {
+ dest.putByte((byte) ((arg << 5) | kind.toByte()));
}
@Override
@@ -195,72 +328,11 @@
public abstract Object asAsmEncodedObject();
- static public class UnknownDexValue extends DexValue {
-
- // Singleton instance.
- public static final UnknownDexValue UNKNOWN = new UnknownDexValue();
-
- private UnknownDexValue() {
- }
+ private abstract static class SimpleDexValue extends DexValue {
@Override
- public void collectIndexedItems(IndexedItemCollection indexedItems,
- DexMethod method, int instructionOffset) {
- throw new Unreachable();
- }
-
- @Override
- public void sort() {
- throw new Unreachable();
- }
-
- @Override
- public boolean mayHaveSideEffects() {
- return true;
- }
-
- @Override
- public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
- throw new Unreachable();
- }
-
- @Override
- public Object getBoxedValue() {
- throw new Unreachable();
- }
-
- @Override
- public Object asAsmEncodedObject() {
- throw new Unreachable();
- }
-
- @Override
- public int hashCode() {
- return System.identityHashCode(this);
- }
-
- @Override
- public boolean equals(Object other) {
- return other == this;
- }
-
- @Override
- public String toString() {
- return "UNKNOWN";
- }
-
- @Override
- public ConstInstruction asConstInstruction(
- AppView<? extends AppInfoWithSubtyping> appView, IRCode code, DebugLocalInfo local) {
- return null;
- }
- }
-
- static private abstract class SimpleDexValue extends DexValue {
-
- @Override
- public void collectIndexedItems(IndexedItemCollection indexedItems,
- DexMethod method, int instructionOffset) {
+ public void collectIndexedItems(
+ IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
// Intentionally left empty
}
@@ -274,18 +346,32 @@
return false;
}
- protected static void writeIntegerTo(byte type, long value, int expected,
- DexOutputBuffer dest) {
+ static void writeIntegerTo(DexValueKind kind, long value, int expected, DexOutputBuffer dest) {
// Leave space for header.
dest.forward(1);
int length = dest.putSignedEncodedValue(value, expected);
dest.rewind(length + 1);
- writeHeader(type, length - 1, dest);
+ writeHeader(kind, length - 1, dest);
dest.forward(length);
}
}
- static public class DexValueByte extends SimpleDexValue {
+ public abstract static class DexValueNumber extends SimpleDexValue {
+
+ public abstract long getRawValue();
+
+ @Override
+ public boolean isDexValueNumber() {
+ return true;
+ }
+
+ @Override
+ public DexValueNumber asDexValueNumber() {
+ return this;
+ }
+ }
+
+ public static class DexValueByte extends DexValueNumber {
public static final DexValueByte DEFAULT = new DexValueByte((byte) 0);
@@ -304,13 +390,28 @@
}
@Override
+ public long getRawValue() {
+ return value;
+ }
+
+ @Override
+ public boolean isDexValueByte() {
+ return true;
+ }
+
+ @Override
+ public DexValueByte asDexValueByte() {
+ return this;
+ }
+
+ @Override
public Object getBoxedValue() {
return getValue();
}
@Override
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
- writeHeader(VALUE_BYTE, 0, dest);
+ writeHeader(DexValueKind.BYTE, 0, dest);
dest.putSignedEncodedValue(value, 1);
}
@@ -344,7 +445,7 @@
}
}
- static public class DexValueShort extends SimpleDexValue {
+ public static class DexValueShort extends DexValueNumber {
public static final DexValueShort DEFAULT = new DexValueShort((short) 0);
final short value;
@@ -362,13 +463,28 @@
}
@Override
+ public long getRawValue() {
+ return value;
+ }
+
+ @Override
+ public boolean isDexValueShort() {
+ return true;
+ }
+
+ @Override
+ public DexValueShort asDexValueShort() {
+ return this;
+ }
+
+ @Override
public Object getBoxedValue() {
return getValue();
}
@Override
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
- writeIntegerTo(VALUE_SHORT, value, Short.BYTES, dest);
+ writeIntegerTo(DexValueKind.SHORT, value, Short.BYTES, dest);
}
@Override
@@ -401,7 +517,7 @@
}
}
- static public class DexValueChar extends SimpleDexValue {
+ public static class DexValueChar extends DexValueNumber {
public static final DexValueChar DEFAULT = new DexValueChar((char) 0);
final char value;
@@ -419,6 +535,21 @@
}
@Override
+ public long getRawValue() {
+ return value;
+ }
+
+ @Override
+ public boolean isDexValueChar() {
+ return true;
+ }
+
+ @Override
+ public DexValueChar asDexValueChar() {
+ return this;
+ }
+
+ @Override
public Object getBoxedValue() {
return getValue();
}
@@ -428,7 +559,7 @@
dest.forward(1);
int length = dest.putUnsignedEncodedValue(value, 2);
dest.rewind(length + 1);
- writeHeader(VALUE_CHAR, length - 1, dest);
+ writeHeader(DexValueKind.CHAR, length - 1, dest);
dest.forward(length);
}
@@ -462,7 +593,7 @@
}
}
- static public class DexValueInt extends SimpleDexValue {
+ public static class DexValueInt extends DexValueNumber {
public static final DexValueInt DEFAULT = new DexValueInt(0);
public final int value;
@@ -480,13 +611,18 @@
}
@Override
+ public long getRawValue() {
+ return value;
+ }
+
+ @Override
public Object getBoxedValue() {
return getValue();
}
@Override
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
- writeIntegerTo(VALUE_INT, value, Integer.BYTES, dest);
+ writeIntegerTo(DexValueKind.INT, value, Integer.BYTES, dest);
}
@Override
@@ -529,7 +665,7 @@
}
}
- static public class DexValueLong extends SimpleDexValue {
+ public static class DexValueLong extends DexValueNumber {
public static final DexValueLong DEFAULT = new DexValueLong(0);
final long value;
@@ -547,13 +683,28 @@
}
@Override
+ public long getRawValue() {
+ return value;
+ }
+
+ @Override
+ public boolean isDexValueLong() {
+ return true;
+ }
+
+ @Override
+ public DexValueLong asDexValueLong() {
+ return this;
+ }
+
+ @Override
public Object getBoxedValue() {
return getValue();
}
@Override
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
- writeIntegerTo(VALUE_LONG, value, Long.BYTES, dest);
+ writeIntegerTo(DexValueKind.LONG, value, Long.BYTES, dest);
}
@Override
@@ -586,7 +737,7 @@
}
}
- static public class DexValueFloat extends SimpleDexValue {
+ public static class DexValueFloat extends DexValueNumber {
public static final DexValueFloat DEFAULT = new DexValueFloat(0);
final float value;
@@ -604,6 +755,21 @@
}
@Override
+ public long getRawValue() {
+ return Float.floatToIntBits(value);
+ }
+
+ @Override
+ public boolean isDexValueFloat() {
+ return true;
+ }
+
+ @Override
+ public DexValueFloat asDexValueFloat() {
+ return this;
+ }
+
+ @Override
public Object getBoxedValue() {
return getValue();
}
@@ -613,7 +779,7 @@
dest.forward(1);
int length = EncodedValueUtils.putFloat(dest, value);
dest.rewind(length + 1);
- writeHeader(VALUE_FLOAT, length - 1, dest);
+ writeHeader(DexValueKind.FLOAT, length - 1, dest);
dest.forward(length);
}
@@ -646,10 +812,9 @@
public String toString() {
return "Float " + value;
}
-
}
- static public class DexValueDouble extends SimpleDexValue {
+ public static class DexValueDouble extends DexValueNumber {
public static final DexValueDouble DEFAULT = new DexValueDouble(0);
@@ -668,6 +833,21 @@
}
@Override
+ public long getRawValue() {
+ return Double.doubleToRawLongBits(value);
+ }
+
+ @Override
+ public boolean isDexValueDouble() {
+ return true;
+ }
+
+ @Override
+ public DexValueDouble asDexValueDouble() {
+ return this;
+ }
+
+ @Override
public Object getBoxedValue() {
return getValue();
}
@@ -677,7 +857,7 @@
dest.forward(1);
int length = EncodedValueUtils.putDouble(dest, value);
dest.rewind(length + 1);
- writeHeader(VALUE_DOUBLE, length - 1, dest);
+ writeHeader(DexValueKind.DOUBLE, length - 1, dest);
dest.forward(length);
}
@@ -720,7 +900,7 @@
this.value = value;
}
- protected abstract byte getValueKind();
+ protected abstract DexValueKind getValueKind();
public T getValue() {
return value;
@@ -759,7 +939,7 @@
@Override
public int hashCode() {
- return value.hashCode() * 7 + getValueKind();
+ return value.hashCode() * 7 + getValueKind().toByte();
}
@Override
@@ -802,8 +982,8 @@
}
@Override
- protected byte getValueKind() {
- return VALUE_STRING;
+ protected DexValueKind getValueKind() {
+ return DexValueKind.STRING;
}
@Override
@@ -840,13 +1020,23 @@
}
@Override
+ public boolean isDexItemBasedValueString() {
+ return true;
+ }
+
+ @Override
+ public DexItemBasedValueString asDexItemBasedValueString() {
+ return this;
+ }
+
+ @Override
public Object asAsmEncodedObject() {
return value.toString();
}
@Override
- protected byte getValueKind() {
- return VALUE_STRING;
+ protected DexValueKind getValueKind() {
+ return DexValueKind.STRING;
}
@Override
@@ -879,13 +1069,13 @@
}
@Override
- protected byte getValueKind() {
- return VALUE_TYPE;
+ protected DexValueKind getValueKind() {
+ return DexValueKind.TYPE;
}
@Override
- public void collectIndexedItems(IndexedItemCollection indexedItems,
- DexMethod method, int instructionOffset) {
+ public void collectIndexedItems(
+ IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
value.collectIndexedItems(indexedItems, method, instructionOffset);
}
@@ -907,13 +1097,13 @@
}
@Override
- protected byte getValueKind() {
- return VALUE_FIELD;
+ protected DexValueKind getValueKind() {
+ return DexValueKind.FIELD;
}
@Override
- public void collectIndexedItems(IndexedItemCollection indexedItems,
- DexMethod method, int instructionOffset) {
+ public void collectIndexedItems(
+ IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
value.collectIndexedItems(indexedItems, method, instructionOffset);
}
}
@@ -925,13 +1115,13 @@
}
@Override
- protected byte getValueKind() {
- return VALUE_METHOD;
+ protected DexValueKind getValueKind() {
+ return DexValueKind.METHOD;
}
@Override
- public void collectIndexedItems(IndexedItemCollection indexedItems,
- DexMethod method, int instructionOffset) {
+ public void collectIndexedItems(
+ IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
value.collectIndexedItems(indexedItems, method, instructionOffset);
}
}
@@ -943,13 +1133,13 @@
}
@Override
- protected byte getValueKind() {
- return VALUE_ENUM;
+ protected DexValueKind getValueKind() {
+ return DexValueKind.ENUM;
}
@Override
- public void collectIndexedItems(IndexedItemCollection indexedItems,
- DexMethod method, int instructionOffset) {
+ public void collectIndexedItems(
+ IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
value.collectIndexedItems(indexedItems, method, instructionOffset);
}
}
@@ -966,13 +1156,13 @@
}
@Override
- protected byte getValueKind() {
- return VALUE_METHOD_TYPE;
+ protected DexValueKind getValueKind() {
+ return DexValueKind.METHOD_TYPE;
}
@Override
- public void collectIndexedItems(IndexedItemCollection indexedItems,
- DexMethod method, int instructionOffset) {
+ public void collectIndexedItems(
+ IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
value.collectIndexedItems(indexedItems, method, instructionOffset);
}
}
@@ -997,7 +1187,7 @@
@Override
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
- writeHeader(VALUE_ARRAY, 0, dest);
+ writeHeader(DexValueKind.ARRAY, 0, dest);
dest.putUleb128(values.length);
for (DexValue value : values) {
value.writeTo(dest, mapping);
@@ -1070,7 +1260,7 @@
@Override
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
- writeHeader(VALUE_ANNOTATION, 0, dest);
+ writeHeader(DexValueKind.ANNOTATION, 0, dest);
FileWriter.writeEncodedAnnotation(value, dest, mapping);
}
@@ -1112,7 +1302,7 @@
}
}
- static public class DexValueNull extends SimpleDexValue {
+ public static class DexValueNull extends DexValueNumber {
public static final DexValue NULL = new DexValueNull();
@@ -1125,8 +1315,23 @@
}
@Override
+ public long getRawValue() {
+ return 0;
+ }
+
+ @Override
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
- writeHeader(VALUE_NULL, 0, dest);
+ writeHeader(DexValueKind.NULL, 0, dest);
+ }
+
+ @Override
+ public boolean isDexValueNull() {
+ return true;
+ }
+
+ @Override
+ public DexValueNull asDexValueNull() {
+ return this;
}
@Override
@@ -1164,7 +1369,7 @@
}
}
- static public class DexValueBoolean extends SimpleDexValue {
+ public static class DexValueBoolean extends DexValueNumber {
private static final DexValueBoolean TRUE = new DexValueBoolean(true);
private static final DexValueBoolean FALSE = new DexValueBoolean(false);
@@ -1186,13 +1391,28 @@
}
@Override
+ public long getRawValue() {
+ return BooleanUtils.longValue(value);
+ }
+
+ @Override
+ public boolean isDexValueBoolean() {
+ return true;
+ }
+
+ @Override
+ public DexValueBoolean asDexValueBoolean() {
+ return this;
+ }
+
+ @Override
public Object getBoxedValue() {
return getValue();
}
@Override
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
- writeHeader(VALUE_BOOLEAN, value ? 1 : 0, dest);
+ writeHeader(DexValueKind.BOOLEAN, value ? 1 : 0, dest);
}
@Override
@@ -1237,13 +1457,13 @@
}
@Override
- protected byte getValueKind() {
- return VALUE_METHOD_HANDLE;
+ protected DexValueKind getValueKind() {
+ return DexValueKind.METHOD_HANDLE;
}
@Override
- public void collectIndexedItems(IndexedItemCollection indexedItems,
- DexMethod method, int instructionOffset) {
+ public void collectIndexedItems(
+ IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
value.collectIndexedItems(indexedItems, method, instructionOffset);
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/EnumValueInfoMapCollection.java b/src/main/java/com/android/tools/r8/graph/EnumValueInfoMapCollection.java
index 470685f..4fd96f7 100644
--- a/src/main/java/com/android/tools/r8/graph/EnumValueInfoMapCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/EnumValueInfoMapCollection.java
@@ -86,6 +86,10 @@
return map.size();
}
+ public boolean hasEnumValueInfo(DexField field) {
+ return map.containsKey(field);
+ }
+
public EnumValueInfo getEnumValueInfo(DexField field) {
return map.get(field);
}
@@ -110,6 +114,10 @@
this.ordinal = ordinal;
}
+ public int convertToInt() {
+ return ordinal + 1;
+ }
+
EnumValueInfo rewrittenWithLens(GraphLense lens) {
DexType newType = lens.lookupType(type);
if (type == newType) {
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java
index 2bd8acf..38d3a8e 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java
@@ -32,12 +32,20 @@
boolean hasReflectiveAccess();
+ default boolean isAccessedFromMethodHandle() {
+ return isReadFromMethodHandle() || isWrittenFromMethodHandle();
+ }
+
boolean isRead();
+ boolean isReadFromMethodHandle();
+
boolean isReadOnlyIn(DexEncodedMethod method);
boolean isWritten();
+ boolean isWrittenFromMethodHandle();
+
boolean isWrittenInMethodSatisfying(Predicate<DexEncodedMethod> predicate);
boolean isWrittenOnlyInMethodSatisfying(Predicate<DexEncodedMethod> predicate);
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollection.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollection.java
index ddab792..9d19406 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollection.java
@@ -12,6 +12,8 @@
void flattenAccessContexts();
+ boolean contains(DexField field);
+
T get(DexField field);
void forEach(Consumer<T> consumer);
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
index 7ef10ac..5634d5b 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
@@ -21,6 +21,11 @@
}
@Override
+ public boolean contains(DexField field) {
+ return infos.containsKey(field);
+ }
+
+ @Override
public FieldAccessInfoImpl get(DexField field) {
return infos.get(field);
}
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
index 4715169..b15153b 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
@@ -22,11 +22,15 @@
public static final FieldAccessInfoImpl MISSING_FIELD_ACCESS_INFO = new FieldAccessInfoImpl(null);
+ public static int FLAG_IS_READ_FROM_METHOD_HANDLE = 1 << 0;
+ public static int FLAG_IS_WRITTEN_FROM_METHOD_HANDLE = 1 << 1;
+ public static int FLAG_HAS_REFLECTIVE_ACCESS = 1 << 2;
+
// A direct reference to the definition of the field.
private DexField field;
- // If this field has a reflective access.
- private boolean hasReflectiveAccess;
+ // If this field is accessed from a method handle or has a reflective access.
+ private int flags;
// Maps every direct and indirect reference in a read-context to the set of methods in which that
// reference appears.
@@ -185,11 +189,11 @@
@Override
public boolean hasReflectiveAccess() {
- return hasReflectiveAccess;
+ return (flags & FLAG_HAS_REFLECTIVE_ACCESS) != 0;
}
public void setHasReflectiveAccess() {
- hasReflectiveAccess = true;
+ flags |= FLAG_HAS_REFLECTIVE_ACCESS;
}
/** Returns true if this field is read by the program. */
@@ -199,6 +203,15 @@
}
@Override
+ public boolean isReadFromMethodHandle() {
+ return (flags & FLAG_IS_READ_FROM_METHOD_HANDLE) != 0;
+ }
+
+ public void setReadFromMethodHandle() {
+ flags |= FLAG_IS_READ_FROM_METHOD_HANDLE;
+ }
+
+ @Override
public boolean isReadOnlyIn(DexEncodedMethod method) {
assert isRead();
assert method != null;
@@ -212,6 +225,15 @@
return writesWithContexts != null && !writesWithContexts.isEmpty();
}
+ @Override
+ public boolean isWrittenFromMethodHandle() {
+ return (flags & FLAG_IS_WRITTEN_FROM_METHOD_HANDLE) != 0;
+ }
+
+ public void setWrittenFromMethodHandle() {
+ flags |= FLAG_IS_WRITTEN_FROM_METHOD_HANDLE;
+ }
+
/**
* Returns true if this field is written by a method for which {@param predicate} returns true.
*/
@@ -292,9 +314,7 @@
public FieldAccessInfoImpl rewrittenWithLens(DexDefinitionSupplier definitions, GraphLense lens) {
FieldAccessInfoImpl rewritten = new FieldAccessInfoImpl(lens.lookupField(field));
- if (hasReflectiveAccess) {
- rewritten.setHasReflectiveAccess();
- }
+ rewritten.flags = flags;
if (readsWithContexts != null) {
rewritten.readsWithContexts = new IdentityHashMap<>();
readsWithContexts.forEach(
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignature.java b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
index f948208..049cb7a 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignature.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
@@ -133,6 +133,8 @@
ClassSignature(
ClassTypeSignature superClassSignature,
List<ClassTypeSignature> superInterfaceSignatures) {
+ assert superClassSignature != null;
+ assert superInterfaceSignatures != null;
this.superClassSignature = superClassSignature;
this.superInterfaceSignatures = superInterfaceSignatures;
}
@@ -173,9 +175,13 @@
return null;
}
- public abstract TypeSignature toArrayTypeSignature(AppView<?> appView);
+ public TypeSignature toArrayTypeSignature(AppView<?> appView) {
+ return null;
+ }
- public abstract TypeSignature toArrayElementTypeSignature(AppView<?> appView);
+ public TypeSignature toArrayElementTypeSignature(AppView<?> appView) {
+ return null;
+ }
}
// TODO(b/129925954): better structures for a circle of
@@ -200,6 +206,14 @@
return null;
}
+ public boolean isArrayTypeSignature() {
+ return false;
+ }
+
+ public ArrayTypeSignature asArrayTypeSignature() {
+ return null;
+ }
+
public boolean isTypeVariableSignature() {
return false;
}
@@ -209,12 +223,10 @@
}
}
- // TODO(b/129925954): separate ArrayTypeSignature or just reuse ClassTypeSignature?
public static class ClassTypeSignature extends FieldTypeSignature {
static final ClassTypeSignature UNKNOWN_CLASS_TYPE_SIGNATURE =
- new ClassTypeSignature(null, ImmutableList.of());
+ new ClassTypeSignature(DexItemFactory.nullValueType, ImmutableList.of());
- // This covers class type or array type, with or without type arguments.
final DexType type;
// E.g., for Map<K, V>, a signature will indicate what types are for K and V.
// Note that this could be nested, e.g., Map<K, Consumer<V>>.
@@ -227,6 +239,8 @@
ClassTypeSignature innerTypeSignature;
ClassTypeSignature(DexType type, List<FieldTypeSignature> typeArguments) {
+ assert type != null;
+ assert typeArguments != null;
this.type = type;
this.typeArguments = typeArguments;
}
@@ -250,25 +264,8 @@
}
@Override
- public ClassTypeSignature toArrayTypeSignature(AppView<?> appView) {
- DexType arrayType = type.toArrayType(1, appView.dexItemFactory());
- ClassTypeSignature result = new ClassTypeSignature(arrayType, typeArguments);
- copyEnclosingRelations(result);
- return result;
- }
-
- @Override
- public ClassTypeSignature toArrayElementTypeSignature(AppView<?> appView) {
- assert type.isArrayType();
- DexType elementType = type.toArrayElementType( appView.dexItemFactory());
- ClassTypeSignature result = new ClassTypeSignature(elementType, typeArguments);
- copyEnclosingRelations(result);
- return result;
- }
-
- private void copyEnclosingRelations(ClassTypeSignature cloned) {
- cloned.enclosingTypeSignature = this.enclosingTypeSignature;
- cloned.innerTypeSignature = this.innerTypeSignature;
+ public ArrayTypeSignature toArrayTypeSignature(AppView<?> appView) {
+ return new ArrayTypeSignature(this);
}
static void link(ClassTypeSignature outer, ClassTypeSignature inner) {
@@ -276,12 +273,66 @@
outer.innerTypeSignature = inner;
inner.enclosingTypeSignature = outer;
}
+
+ // TODO(b/129925954): rewrite GenericSignatureRewriter with this pattern?
+ public interface Converter<R> {
+ R init();
+ R visitType(DexType type, R result);
+ R visitTypeArgument(FieldTypeSignature typeArgument, R result);
+ R visitInnerTypeSignature(ClassTypeSignature innerTypeSignature, R result);
+ }
+
+ public <R> R convert(Converter<R> converter) {
+ R result = converter.init();
+ result = converter.visitType(type, result);
+ for (FieldTypeSignature typeArgument : typeArguments) {
+ result = converter.visitTypeArgument(typeArgument, result);
+ }
+ if (innerTypeSignature != null) {
+ result = converter.visitInnerTypeSignature(innerTypeSignature, result);
+ }
+ return result;
+ }
+ }
+
+ public static class ArrayTypeSignature extends FieldTypeSignature {
+ final TypeSignature elementSignature;
+
+ ArrayTypeSignature(TypeSignature elementSignature) {
+ assert elementSignature != null;
+ this.elementSignature = elementSignature;
+ }
+
+ public TypeSignature elementSignature() {
+ return elementSignature;
+ }
+
+ @Override
+ public boolean isArrayTypeSignature() {
+ return true;
+ }
+
+ @Override
+ public ArrayTypeSignature asArrayTypeSignature() {
+ return this;
+ }
+
+ @Override
+ public TypeSignature toArrayTypeSignature(AppView<?> appView) {
+ return new ArrayTypeSignature(this);
+ }
+
+ @Override
+ public TypeSignature toArrayElementTypeSignature(AppView<?> appView) {
+ return elementSignature;
+ }
}
public static class TypeVariableSignature extends FieldTypeSignature {
final String typeVariable;
TypeVariableSignature(String typeVariable) {
+ assert typeVariable != null;
this.typeVariable = typeVariable;
}
@@ -296,13 +347,8 @@
}
@Override
- public TypeSignature toArrayTypeSignature(AppView<?> appView) {
- throw new Unimplemented("TypeVariableSignature::toArrayTypeSignature");
- }
-
- @Override
- public TypeSignature toArrayElementTypeSignature(AppView<?> appView) {
- throw new Unimplemented("TypeVariableSignature::toArrayElementTypeSignature");
+ public ArrayTypeSignature toArrayTypeSignature(AppView<?> appView) {
+ return new ArrayTypeSignature(this);
}
}
@@ -311,9 +357,8 @@
final DexType type;
BaseTypeSignature(DexType type) {
- assert type.isPrimitiveType() || type.isPrimitiveArrayType()
- || type.isVoidType()
- : type.toDescriptorString();
+ assert type != null;
+ assert type.isPrimitiveType() : type.toDescriptorString();
this.type = type;
}
@@ -328,36 +373,47 @@
}
@Override
- public BaseTypeSignature toArrayTypeSignature(AppView<?> appView) {
+ public ArrayTypeSignature toArrayTypeSignature(AppView<?> appView) {
assert !type.isVoidType();
- DexType arrayType = type.toArrayType(1, appView.dexItemFactory());
- return new BaseTypeSignature(arrayType);
+ return new ArrayTypeSignature(this);
+ }
+ }
+
+ public static class ReturnType {
+ static final ReturnType VOID = new ReturnType(null);
+
+ // `null` indicates that it's `void`.
+ final TypeSignature typeSignature;
+
+ ReturnType(TypeSignature typeSignature) {
+ this.typeSignature = typeSignature;
}
- @Override
- public BaseTypeSignature toArrayElementTypeSignature(AppView<?> appView) {
- assert type.isPrimitiveArrayType();
- DexType elementType = type.toArrayElementType(appView.dexItemFactory());
- return new BaseTypeSignature(elementType);
+ public boolean isVoidDescriptor() {
+ return typeSignature == null;
+ }
+
+ public TypeSignature typeSignature() {
+ return typeSignature;
}
}
public static class MethodTypeSignature implements DexDefinitionSignature<DexEncodedMethod> {
static final MethodTypeSignature UNKNOWN_METHOD_TYPE_SIGNATURE =
- new MethodTypeSignature(
- ImmutableList.of(),
- ClassTypeSignature.UNKNOWN_CLASS_TYPE_SIGNATURE,
- ImmutableList.of());
+ new MethodTypeSignature(ImmutableList.of(), ReturnType.VOID, ImmutableList.of());
// TODO(b/129925954): encoding formal type parameters
final List<TypeSignature> typeSignatures;
- final TypeSignature returnType;
+ final ReturnType returnType;
final List<TypeSignature> throwsSignatures;
MethodTypeSignature(
List<TypeSignature> typeSignatures,
- TypeSignature returnType,
+ ReturnType returnType,
List<TypeSignature> throwsSignatures) {
+ assert typeSignatures != null;
+ assert returnType != null;
+ assert throwsSignatures != null;
this.typeSignatures = typeSignatures;
this.returnType = returnType;
this.throwsSignatures = throwsSignatures;
@@ -370,7 +426,7 @@
return typeSignatures.get(i);
}
- public TypeSignature returnType() {
+ public ReturnType returnType() {
return returnType;
}
@@ -817,7 +873,7 @@
expect(')');
- TypeSignature returnType = updateReturnType();
+ ReturnType returnType = updateReturnType();
ImmutableList.Builder<TypeSignature> throwsSignatureBuilder = ImmutableList.builder();
if (symbol == '^') {
@@ -837,13 +893,13 @@
parameterSignatureBuilder.build(), returnType, throwsSignatureBuilder.build());
}
- private TypeSignature updateReturnType() {
+ private ReturnType updateReturnType() {
// ReturnType ::= TypeSignature | "V".
if (symbol != 'V') {
- return updateTypeSignature(ParserPosition.MEMBER_ANNOTATION);
+ return new ReturnType(updateTypeSignature(ParserPosition.MEMBER_ANNOTATION));
} else {
scanSymbol();
- return new BaseTypeSignature(appView.dexItemFactory().voidType);
+ return ReturnType.VOID;
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index 8588a9d..7d2d844 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -350,6 +350,12 @@
continue;
}
for (DexEncodedField field : clazz.fields()) {
+ // The field $r8$clinitField may be synthesized by R8 in order to trigger the initialization
+ // of the enclosing class. It is not present in the input, and therefore we do not require
+ // that it can be mapped back to the original program.
+ if (field.field.match(dexItemFactory.objectMembers.clinitField)) {
+ continue;
+ }
DexField originalField = getOriginalFieldSignature(field.field);
assert originalFields.contains(originalField)
: "Unable to map field `" + field.field.toSourceString() + "` back to original program";
diff --git a/src/main/java/com/android/tools/r8/graph/InstantiatedSubTypeInfo.java b/src/main/java/com/android/tools/r8/graph/InstantiatedSubTypeInfo.java
index e4942bb..7887fe9 100644
--- a/src/main/java/com/android/tools/r8/graph/InstantiatedSubTypeInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/InstantiatedSubTypeInfo.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.graph;
+import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import java.util.function.Consumer;
@FunctionalInterface
@@ -12,5 +13,5 @@
void forEachInstantiatedSubType(
DexType type,
Consumer<DexProgramClass> subTypeConsumer,
- Consumer<DexCallSite> callSiteConsumer);
+ Consumer<LambdaDescriptor> lambdaConsumer);
}
diff --git a/src/main/java/com/android/tools/r8/graph/LookupCompletenessHelper.java b/src/main/java/com/android/tools/r8/graph/LookupCompletenessHelper.java
index bfc0eb4..64fa1a6 100644
--- a/src/main/java/com/android/tools/r8/graph/LookupCompletenessHelper.java
+++ b/src/main/java/com/android/tools/r8/graph/LookupCompletenessHelper.java
@@ -14,6 +14,7 @@
private final PinnedPredicate pinnedPredicate;
private Set<DexType> pinnedInstantiations;
+ private Set<DexMethod> pinnedMethods;
LookupCompletenessHelper(PinnedPredicate pinnedPredicate) {
this.pinnedPredicate = pinnedPredicate;
@@ -28,15 +29,32 @@
}
}
+ void checkMethod(DexEncodedMethod method) {
+ if (pinnedPredicate.isPinned(method.method)) {
+ if (pinnedMethods == null) {
+ pinnedMethods = Sets.newIdentityHashSet();
+ }
+ pinnedMethods.add(method.method);
+ }
+ }
+
+ void checkDexClassAndMethod(DexClassAndMethod classAndMethod) {
+ checkClass(classAndMethod.getHolder());
+ checkMethod(classAndMethod.getMethod());
+ }
+
LookupResultCollectionState computeCollectionState(
- DexMethod method, AppView<? extends AppInfoWithClassHierarchy> appView) {
+ DexMethod method, AppInfoWithClassHierarchy appInfo) {
assert pinnedInstantiations == null || !pinnedInstantiations.isEmpty();
if (pinnedInstantiations == null) {
return LookupResultCollectionState.Complete;
}
+ if (pinnedMethods != null) {
+ return LookupResultCollectionState.Incomplete;
+ }
WorkList<DexType> workList = WorkList.newIdentityWorkList(pinnedInstantiations);
while (workList.hasNext()) {
- if (isMethodKeptInSuperTypeOrIsLibrary(workList, method, appView)) {
+ if (isMethodKeptInSuperTypeOrIsLibrary(workList, method, appInfo)) {
return LookupResultCollectionState.Incomplete;
}
}
@@ -44,11 +62,9 @@
}
private boolean isMethodKeptInSuperTypeOrIsLibrary(
- WorkList<DexType> workList,
- DexMethod method,
- AppView<? extends AppInfoWithClassHierarchy> appView) {
+ WorkList<DexType> workList, DexMethod method, AppInfoWithClassHierarchy appInfo) {
while (workList.hasNext()) {
- DexClass parent = appView.definitionFor(workList.next());
+ DexClass parent = appInfo.definitionFor(workList.next());
if (parent == null) {
continue;
}
diff --git a/src/main/java/com/android/tools/r8/graph/LookupLambdaTarget.java b/src/main/java/com/android/tools/r8/graph/LookupLambdaTarget.java
new file mode 100644
index 0000000..b41ef8f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/LookupLambdaTarget.java
@@ -0,0 +1,32 @@
+// 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.graph;
+
+import com.android.tools.r8.ir.desugar.LambdaDescriptor;
+
+public class LookupLambdaTarget implements LookupTarget {
+ private final LambdaDescriptor lambda;
+ private final DexClassAndMethod method;
+
+ public LookupLambdaTarget(LambdaDescriptor lambda, DexClassAndMethod method) {
+ assert lambda != null;
+ assert method != null;
+ this.lambda = lambda;
+ this.method = method;
+ }
+
+ @Override
+ public boolean isLambdaTarget() {
+ return true;
+ }
+
+ @Override
+ public LookupLambdaTarget asLambdaTarget() {
+ return this;
+ }
+
+ public DexClassAndMethod getImplementationMethod() {
+ return method;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/LookupResult.java b/src/main/java/com/android/tools/r8/graph/LookupResult.java
index fd71a8d..17d7f55 100644
--- a/src/main/java/com/android/tools/r8/graph/LookupResult.java
+++ b/src/main/java/com/android/tools/r8/graph/LookupResult.java
@@ -6,7 +6,9 @@
import com.android.tools.r8.graph.LookupResult.LookupResultSuccess.LookupResultCollectionState;
import java.util.Collections;
-import java.util.Set;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
public abstract class LookupResult {
@@ -26,9 +28,18 @@
return null;
}
+ public final void forEach(Consumer<LookupTarget> onTarget) {
+ forEach(onTarget::accept, onTarget::accept);
+ }
+
+ public abstract void forEach(
+ Consumer<DexClassAndMethod> onMethodTarget, Consumer<LookupLambdaTarget> onLambdaTarget);
+
public static LookupResultSuccess createResult(
- Set<DexEncodedMethod> methodTargets, LookupResultCollectionState state) {
- return new LookupResultSuccess(methodTargets, state);
+ Map<DexEncodedMethod, DexClassAndMethod> methodTargets,
+ List<LookupLambdaTarget> lambdaTargets,
+ LookupResultCollectionState state) {
+ return new LookupResultSuccess(methodTargets, lambdaTargets, state);
}
public static LookupResultFailure createFailedResult() {
@@ -42,23 +53,46 @@
public static class LookupResultSuccess extends LookupResult {
private static final LookupResultSuccess EMPTY_INSTANCE =
- new LookupResultSuccess(Collections.emptySet(), LookupResultCollectionState.Incomplete);
+ new LookupResultSuccess(
+ Collections.emptyMap(),
+ Collections.emptyList(),
+ LookupResultCollectionState.Incomplete);
- private final Set<DexEncodedMethod> methodTargets;
- private final LookupResultCollectionState state;
+ private final Map<DexEncodedMethod, DexClassAndMethod> methodTargets;
+ private final List<LookupLambdaTarget> lambdaTargets;
+ private LookupResultCollectionState state;
private LookupResultSuccess(
- Set<DexEncodedMethod> methodTargets, LookupResultCollectionState state) {
+ Map<DexEncodedMethod, DexClassAndMethod> methodTargets,
+ List<LookupLambdaTarget> lambdaTargets,
+ LookupResultCollectionState state) {
this.methodTargets = methodTargets;
+ this.lambdaTargets = lambdaTargets;
this.state = state;
}
public boolean isEmpty() {
- return methodTargets == null || methodTargets.isEmpty();
+ return methodTargets.isEmpty() && lambdaTargets.isEmpty();
}
- public Set<DexEncodedMethod> getMethodTargets() {
- return methodTargets;
+ public boolean hasMethodTargets() {
+ return !methodTargets.isEmpty();
+ }
+
+ public boolean hasLambdaTargets() {
+ return !lambdaTargets.isEmpty();
+ }
+
+ @Override
+ public void forEach(
+ Consumer<DexClassAndMethod> onMethodTarget, Consumer<LookupLambdaTarget> onLambdaTarget) {
+ methodTargets.forEach((ignore, method) -> onMethodTarget.accept(method));
+ lambdaTargets.forEach(onLambdaTarget);
+ }
+
+ public boolean contains(DexEncodedMethod method) {
+ // Containment of a method in the lookup results only pertains to the method targets.
+ return methodTargets.containsKey(method);
}
@Override
@@ -79,6 +113,24 @@
return state == LookupResultCollectionState.Complete;
}
+ public void setIncomplete() {
+ // TODO(b/148769279): Remove when we have instantiated info.
+ state = LookupResultCollectionState.Incomplete;
+ }
+
+ public LookupTarget getSingleLookupTarget() {
+ if (isIncomplete() || methodTargets.size() + lambdaTargets.size() > 1) {
+ return null;
+ }
+ // TODO(b/150932978): Check lambda targets implementation methods.
+ if (methodTargets.size() == 1) {
+ return methodTargets.values().iterator().next();
+ } else if (lambdaTargets.size() == 1) {
+ return lambdaTargets.get(0);
+ }
+ return null;
+ }
+
public enum LookupResultCollectionState {
Complete,
Incomplete,
@@ -102,5 +154,11 @@
public boolean isLookupResultFailure() {
return true;
}
+
+ @Override
+ public void forEach(
+ Consumer<DexClassAndMethod> onMethodTarget, Consumer<LookupLambdaTarget> onLambdaTarget) {
+ // Nothing to iterate for a failed lookup.
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/LookupTarget.java b/src/main/java/com/android/tools/r8/graph/LookupTarget.java
new file mode 100644
index 0000000..6966a44
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/LookupTarget.java
@@ -0,0 +1,22 @@
+// 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.graph;
+
+public interface LookupTarget {
+ default boolean isMethodTarget() {
+ return false;
+ }
+
+ default boolean isLambdaTarget() {
+ return false;
+ }
+
+ default DexClassAndMethod asMethodTarget() {
+ return null;
+ }
+
+ default LookupLambdaTarget asLambdaTarget() {
+ return null;
+ }
+}
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 53510bd..3869d23 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
@@ -6,15 +6,22 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+import com.android.tools.r8.ir.desugar.LambdaDescriptor;
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.utils.LensUtils;
+import com.android.tools.r8.utils.WorkList;
import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.IdentityHashMap;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
/** Stores the set of instantiated classes along with their allocation sites. */
public class ObjectAllocationInfoCollectionImpl implements ObjectAllocationInfoCollection {
@@ -68,11 +75,33 @@
private final boolean trackAllocationSites;
+ /** Instantiated classes with the contexts of the instantiations. */
private final Map<DexProgramClass, Set<DexEncodedMethod>> classesWithAllocationSiteTracking =
new IdentityHashMap<>();
+
+ /** Instantiated classes without contexts. */
private final Set<DexProgramClass> classesWithoutAllocationSiteTracking =
Sets.newIdentityHashSet();
+ /** Set of types directly implemented by a lambda. */
+ private final Map<DexType, List<LambdaDescriptor>> instantiatedLambdas =
+ new IdentityHashMap<>();
+
+ /**
+ * Hierarchy for instantiated types mapping a type to the set of immediate subtypes for which
+ * some subtype is either instantiated or is implemented by an instantiated lambda.
+ */
+ private final Map<DexType, Set<DexClass>> instantiatedHierarchy = new IdentityHashMap<>();
+
+ /**
+ * Set of interface types for which there may be instantiations, such as lambda expressions or
+ * explicit keep rules.
+ */
+ private final Set<DexProgramClass> instantiatedInterfaceTypes = Sets.newIdentityHashSet();
+
+ /** Subset of the above that are marked instantiated by usages that are not lambdas. */
+ public final Set<DexProgramClass> unknownInstantiatedInterfaceTypes = Sets.newIdentityHashSet();
+
private GraphReporter reporter;
private Builder(boolean trackAllocationSites, GraphReporter reporter) {
@@ -97,7 +126,15 @@
return !clazz.instanceFields().isEmpty();
}
+ public boolean isInstantiatedDirectlyOrIsInstantiationLeaf(DexProgramClass clazz) {
+ if (clazz.isInterface()) {
+ return instantiatedInterfaceTypes.contains(clazz);
+ }
+ return isInstantiatedDirectly(clazz);
+ }
+
public boolean isInstantiatedDirectly(DexProgramClass clazz) {
+ assert !clazz.isInterface();
if (classesWithAllocationSiteTracking.containsKey(clazz)) {
assert !classesWithAllocationSiteTracking.get(clazz).isEmpty();
return true;
@@ -105,6 +142,30 @@
return classesWithoutAllocationSiteTracking.contains(clazz);
}
+ public boolean isInstantiatedDirectlyOrHasInstantiatedSubtype(DexProgramClass clazz) {
+ return isInstantiatedDirectlyOrIsInstantiationLeaf(clazz)
+ || instantiatedHierarchy.containsKey(clazz.type);
+ }
+
+ public void forEachInstantiatedSubType(
+ DexType type,
+ Consumer<DexProgramClass> onClass,
+ Consumer<LambdaDescriptor> onLambda,
+ AppInfo appInfo) {
+ internalForEachInstantiatedSubType(
+ type,
+ onClass,
+ onLambda,
+ instantiatedHierarchy,
+ instantiatedLambdas,
+ this::isInstantiatedDirectlyOrIsInstantiationLeaf,
+ appInfo);
+ }
+
+ public Set<DexClass> getImmediateSubtypesInInstantiatedHierarchy(DexType type) {
+ return instantiatedHierarchy.get(type);
+ }
+
/**
* Records that {@param clazz} is instantiated in {@param context}.
*
@@ -114,11 +175,13 @@
DexProgramClass clazz,
DexEncodedMethod context,
InstantiationReason instantiationReason,
- KeepReason keepReason) {
+ KeepReason keepReason,
+ AppInfo appInfo) {
assert !clazz.isInterface();
if (reporter != null) {
reporter.registerClass(clazz, keepReason);
}
+ populateInstantiatedHierarchy(appInfo, clazz);
if (shouldTrackAllocationSitesForClass(clazz, instantiationReason)) {
assert context != null;
Set<DexEncodedMethod> allocationSitesForClass =
@@ -135,6 +198,51 @@
return false;
}
+ public boolean recordInstantiatedInterface(DexProgramClass iface) {
+ assert iface.isInterface();
+ assert !iface.isAnnotation();
+ unknownInstantiatedInterfaceTypes.add(iface);
+ return instantiatedInterfaceTypes.add(iface);
+ }
+
+ public void recordInstantiatedLambdaInterface(
+ DexType iface, LambdaDescriptor lambda, AppInfo appInfo) {
+ instantiatedLambdas.computeIfAbsent(iface, key -> new ArrayList<>()).add(lambda);
+ populateInstantiatedHierarchy(appInfo, iface);
+ }
+
+ private void populateInstantiatedHierarchy(AppInfo appInfo, DexType type) {
+ DexClass clazz = appInfo.definitionFor(type);
+ if (clazz != null) {
+ populateInstantiatedHierarchy(appInfo, clazz);
+ }
+ }
+
+ private void populateInstantiatedHierarchy(AppInfo appInfo, DexClass clazz) {
+ if (clazz.superType != null) {
+ populateInstantiatedHierarchy(appInfo, clazz.superType, clazz);
+ }
+ for (DexType iface : clazz.interfaces.values) {
+ populateInstantiatedHierarchy(appInfo, iface, clazz);
+ }
+ }
+
+ private void populateInstantiatedHierarchy(AppInfo appInfo, DexType type, DexClass subtype) {
+ if (type == appInfo.dexItemFactory().objectType) {
+ return;
+ }
+ Set<DexClass> subtypes = instantiatedHierarchy.get(type);
+ if (subtypes != null) {
+ subtypes.add(subtype);
+ return;
+ }
+ // This is the first time an instantiation appears below 'type', recursively populate.
+ subtypes = Sets.newIdentityHashSet();
+ subtypes.add(subtype);
+ instantiatedHierarchy.put(type, subtypes);
+ populateInstantiatedHierarchy(appInfo, type);
+ }
+
Builder rewrittenWithLens(
ObjectAllocationInfoCollectionImpl objectAllocationInfos,
DexDefinitionSupplier definitions,
@@ -173,4 +281,48 @@
classesWithAllocationSiteTracking, classesWithoutAllocationSiteTracking);
}
}
+
+ private static void internalForEachInstantiatedSubType(
+ DexType type,
+ Consumer<DexProgramClass> subTypeConsumer,
+ Consumer<LambdaDescriptor> lambdaConsumer,
+ Map<DexType, Set<DexClass>> instantiatedHierarchy,
+ Map<DexType, List<LambdaDescriptor>> instantiatedLambdas,
+ Predicate<DexProgramClass> isInstantiatedDirectly,
+ AppInfo appInfo) {
+ WorkList<DexClass> worklist = WorkList.newIdentityWorkList();
+ if (type == appInfo.dexItemFactory().objectType) {
+ // All types are below java.lang.Object, but we don't maintain an entry for it.
+ instantiatedHierarchy.forEach(
+ (key, subtypes) -> {
+ DexClass clazz = appInfo.definitionFor(key);
+ if (clazz != null) {
+ worklist.addIfNotSeen(clazz);
+ }
+ worklist.addIfNotSeen(subtypes);
+ });
+ } else {
+ DexClass initialClass = appInfo.definitionFor(type);
+ if (initialClass == null) {
+ // If no definition for the type is found, populate the worklist with any
+ // instantiated subtypes and callback with any lambda instance.
+ worklist.addIfNotSeen(instantiatedHierarchy.getOrDefault(type, Collections.emptySet()));
+ instantiatedLambdas.getOrDefault(type, Collections.emptyList()).forEach(lambdaConsumer);
+ } else {
+ worklist.addIfNotSeen(initialClass);
+ }
+ }
+
+ while (worklist.hasNext()) {
+ DexClass clazz = worklist.next();
+ if (clazz.isProgramClass()) {
+ DexProgramClass programClass = clazz.asProgramClass();
+ if (isInstantiatedDirectly.test(programClass)) {
+ subTypeConsumer.accept(programClass);
+ }
+ }
+ worklist.addIfNotSeen(instantiatedHierarchy.getOrDefault(clazz.type, Collections.emptySet()));
+ instantiatedLambdas.getOrDefault(clazz.type, Collections.emptyList()).forEach(lambdaConsumer);
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
index bb10b76..f9cea40 100644
--- a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
@@ -8,10 +8,13 @@
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.InstantiatedObject;
-import com.google.common.collect.Sets;
+import com.android.tools.r8.utils.Box;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.Set;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
@@ -58,8 +61,6 @@
public abstract boolean isAccessibleForVirtualDispatchFrom(
DexProgramClass context, AppInfoWithClassHierarchy appInfo);
- // TODO(b/145187573): Remove this and use proper access checks.
- @Deprecated
public abstract boolean isVirtualTarget();
/** Lookup the single target of an invoke-special on this resolution result if possible. */
@@ -80,25 +81,30 @@
public abstract LookupResult lookupVirtualDispatchTargets(
DexProgramClass context,
- AppView<? extends AppInfoWithClassHierarchy> appView,
+ AppInfoWithClassHierarchy appInfo,
InstantiatedSubTypeInfo instantiatedInfo,
PinnedPredicate pinnedPredicate);
public final LookupResult lookupVirtualDispatchTargets(
- DexProgramClass context, AppView<AppInfoWithLiveness> appView) {
- AppInfoWithLiveness appInfoWithLiveness = appView.appInfo();
+ DexProgramClass context, AppInfoWithLiveness appInfo) {
return lookupVirtualDispatchTargets(
- context, appView, appInfoWithLiveness, appInfoWithLiveness::isPinned);
+ context, appInfo, appInfo, appInfo::isPinnedNotProgramOrLibraryOverride);
}
- public abstract DexClassAndMethod lookupVirtualDispatchTarget(
- InstantiatedObject instance, AppView<? extends AppInfoWithClassHierarchy> appView);
+ public abstract LookupResult lookupVirtualDispatchTargets(
+ DexProgramClass context,
+ AppInfoWithLiveness appInfo,
+ DexProgramClass refinedReceiverUpperBound,
+ DexProgramClass refinedReceiverLowerBound);
+
+ public abstract LookupTarget lookupVirtualDispatchTarget(
+ InstantiatedObject instance, AppInfoWithClassHierarchy appInfo);
public abstract DexClassAndMethod lookupVirtualDispatchTarget(
- DexProgramClass dynamicInstance, AppView<? extends AppInfoWithClassHierarchy> appView);
+ DexProgramClass dynamicInstance, AppInfoWithClassHierarchy appInfo);
- public abstract DexClassAndMethod lookupVirtualDispatchTarget(
- LambdaDescriptor lambdaInstance, AppView<? extends AppInfoWithClassHierarchy> appView);
+ public abstract LookupTarget lookupVirtualDispatchTarget(
+ LambdaDescriptor lambdaInstance, AppInfoWithClassHierarchy appInfo);
/** Result for a resolution that succeeds with a known declaration/definition. */
public static class SingleResolutionResult extends ResolutionResult {
@@ -333,13 +339,13 @@
@Override
public LookupResult lookupVirtualDispatchTargets(
DexProgramClass context,
- AppView<? extends AppInfoWithClassHierarchy> appView,
+ AppInfoWithClassHierarchy appInfo,
InstantiatedSubTypeInfo instantiatedInfo,
PinnedPredicate pinnedPredicate) {
// Check that the initial resolution holder is accessible from the context.
- assert appView.isSubtype(initialResolutionHolder.type, resolvedHolder.type).isTrue()
+ assert appInfo.isSubtype(initialResolutionHolder.type, resolvedHolder.type)
: initialResolutionHolder.type + " is not a subtype of " + resolvedHolder.type;
- if (context != null && !isAccessibleFrom(context, appView.appInfo())) {
+ if (context != null && !isAccessibleFrom(context, appInfo)) {
return LookupResult.createFailedResult();
}
if (resolvedMethod.isPrivateMethod()) {
@@ -350,37 +356,121 @@
pinnedPredicate.isPinned(resolvedHolder.type)
&& pinnedPredicate.isPinned(resolvedMethod.method);
return LookupResult.createResult(
- Collections.singleton(resolvedMethod),
+ Collections.singletonMap(
+ resolvedMethod, DexClassAndMethod.create(resolvedHolder, resolvedMethod)),
+ Collections.emptyList(),
isIncomplete
? LookupResultCollectionState.Incomplete
: LookupResultCollectionState.Complete);
}
assert resolvedMethod.isNonPrivateVirtualMethod();
- Set<DexEncodedMethod> result = Sets.newIdentityHashSet();
+ Map<DexEncodedMethod, DexClassAndMethod> methodTargets = new IdentityHashMap<>();
+ List<LookupLambdaTarget> lambdaTargets = new ArrayList<>();
LookupCompletenessHelper incompleteness = new LookupCompletenessHelper(pinnedPredicate);
- // TODO(b/150171154): Use instantiationHolder below.
instantiatedInfo.forEachInstantiatedSubType(
- resolvedHolder.type,
+ initialResolutionHolder.type,
subClass -> {
incompleteness.checkClass(subClass);
DexClassAndMethod dexClassAndMethod =
- lookupVirtualDispatchTarget(subClass, appView, resolvedHolder.type);
+ lookupVirtualDispatchTarget(subClass, appInfo, resolvedHolder.type);
if (dexClassAndMethod != null) {
+ incompleteness.checkDexClassAndMethod(dexClassAndMethod);
addVirtualDispatchTarget(
- dexClassAndMethod.getMethod(), resolvedHolder.isInterface(), result);
+ dexClassAndMethod, resolvedHolder.isInterface(), methodTargets);
}
},
- dexCallSite -> {
- // TODO(b/148769279): We need to look at the call site to see if it overrides
- // the resolved method or not.
+ lambda -> {
+ assert resolvedHolder.isInterface();
+ LookupTarget target = lookupVirtualDispatchTarget(lambda, appInfo);
+ if (target != null) {
+ if (target.isLambdaTarget()) {
+ lambdaTargets.add(target.asLambdaTarget());
+ } else {
+ addVirtualDispatchTarget(
+ target.asMethodTarget(), resolvedHolder.isInterface(), methodTargets);
+ }
+ }
});
return LookupResult.createResult(
- result, incompleteness.computeCollectionState(resolvedMethod.method, appView));
+ methodTargets,
+ lambdaTargets,
+ incompleteness.computeCollectionState(resolvedMethod.method, appInfo));
+ }
+
+ @Override
+ public LookupResult lookupVirtualDispatchTargets(
+ DexProgramClass context,
+ AppInfoWithLiveness appInfo,
+ DexProgramClass refinedReceiverUpperBound,
+ DexProgramClass refinedReceiverLowerBound) {
+ assert refinedReceiverUpperBound != null;
+ assert appInfo.isSubtype(refinedReceiverUpperBound.type, initialResolutionHolder.type);
+ assert refinedReceiverLowerBound == null
+ || appInfo.isSubtype(refinedReceiverLowerBound.type, refinedReceiverUpperBound.type);
+ // TODO(b/148769279): Remove the check for hasInstantiatedLambdas.
+ Box<Boolean> hasInstantiatedLambdas = new Box<>(false);
+ InstantiatedSubTypeInfo instantiatedSubTypeInfo =
+ refinedReceiverLowerBound == null
+ ? instantiatedSubTypeInfoWithoutLowerBound(
+ appInfo, refinedReceiverUpperBound, hasInstantiatedLambdas)
+ : instantiatedSubTypeInfoWithLowerBound(
+ appInfo,
+ refinedReceiverUpperBound,
+ refinedReceiverLowerBound,
+ hasInstantiatedLambdas);
+ LookupResult lookupResult =
+ lookupVirtualDispatchTargets(
+ context,
+ appInfo,
+ instantiatedSubTypeInfo,
+ appInfo::isPinnedNotProgramOrLibraryOverride);
+ if (hasInstantiatedLambdas.get() && lookupResult.isLookupResultSuccess()) {
+ lookupResult.asLookupResultSuccess().setIncomplete();
+ }
+ return lookupResult;
+ }
+
+ private InstantiatedSubTypeInfo instantiatedSubTypeInfoWithoutLowerBound(
+ AppInfoWithLiveness appInfo,
+ DexProgramClass refinedReceiverUpperBound,
+ Box<Boolean> hasInstantiatedLambdas) {
+ return (type, subTypeConsumer, callSiteConsumer) -> {
+ appInfo.forEachInstantiatedSubType(
+ refinedReceiverUpperBound.type,
+ subType -> {
+ if (appInfo.hasAnyInstantiatedLambdas(subType)) {
+ hasInstantiatedLambdas.set(true);
+ }
+ subTypeConsumer.accept(subType);
+ },
+ callSiteConsumer);
+ };
+ }
+
+ private InstantiatedSubTypeInfo instantiatedSubTypeInfoWithLowerBound(
+ AppInfoWithLiveness appInfo,
+ DexProgramClass refinedReceiverUpperBound,
+ DexProgramClass refinedReceiverLowerBound,
+ Box<Boolean> hasInstantiatedLambdas) {
+ return (type, subTypeConsumer, callSiteConsumer) -> {
+ List<DexProgramClass> subTypes =
+ appInfo.computeProgramClassRelationChain(
+ refinedReceiverLowerBound, refinedReceiverUpperBound);
+ for (DexProgramClass subType : subTypes) {
+ if (appInfo.hasAnyInstantiatedLambdas(subType)) {
+ hasInstantiatedLambdas.set(true);
+ }
+ subTypeConsumer.accept(subType);
+ }
+ };
}
private static void addVirtualDispatchTarget(
- DexEncodedMethod target, boolean holderIsInterface, Set<DexEncodedMethod> result) {
- assert !target.isPrivateMethod();
+ DexClassAndMethod target,
+ boolean holderIsInterface,
+ Map<DexEncodedMethod, DexClassAndMethod> result) {
+ DexEncodedMethod targetMethod = target.getMethod();
+ assert !targetMethod.isPrivateMethod();
if (holderIsInterface) {
// Add default interface methods to the list of targets.
//
@@ -404,18 +494,18 @@
// public void bar() { }
// }
//
- if (target.isDefaultMethod()) {
- result.add(target);
+ if (targetMethod.isDefaultMethod()) {
+ result.putIfAbsent(targetMethod, target);
}
// Default methods are looked up when looking at a specific subtype that does not override
// them. Otherwise, we would look up default methods that are actually never used.
// However, we have to add bridge methods, otherwise we can remove a bridge that will be
// used.
- if (!target.accessFlags.isAbstract() && target.accessFlags.isBridge()) {
- result.add(target);
+ if (!targetMethod.accessFlags.isAbstract() && targetMethod.accessFlags.isBridge()) {
+ result.putIfAbsent(targetMethod, target);
}
} else {
- result.add(target);
+ result.putIfAbsent(targetMethod, target);
}
}
@@ -425,55 +515,61 @@
* we have an object ref on the stack.
*/
@Override
- public DexClassAndMethod lookupVirtualDispatchTarget(
- InstantiatedObject instance, AppView<? extends AppInfoWithClassHierarchy> appView) {
+ public LookupTarget lookupVirtualDispatchTarget(
+ InstantiatedObject instance, AppInfoWithClassHierarchy appInfo) {
return instance.isClass()
- ? lookupVirtualDispatchTarget(instance.asClass(), appView)
- : lookupVirtualDispatchTarget(instance.asLambda(), appView);
+ ? lookupVirtualDispatchTarget(instance.asClass(), appInfo)
+ : lookupVirtualDispatchTarget(instance.asLambda(), appInfo);
}
@Override
public DexClassAndMethod lookupVirtualDispatchTarget(
- DexProgramClass dynamicInstance, AppView<? extends AppInfoWithClassHierarchy> appView) {
- return lookupVirtualDispatchTarget(dynamicInstance, appView, initialResolutionHolder.type);
+ DexProgramClass dynamicInstance, AppInfoWithClassHierarchy appInfo) {
+ return lookupVirtualDispatchTarget(dynamicInstance, appInfo, initialResolutionHolder.type);
}
@Override
- public DexClassAndMethod lookupVirtualDispatchTarget(
- LambdaDescriptor lambdaInstance, AppView<? extends AppInfoWithClassHierarchy> appView) {
+ public LookupTarget lookupVirtualDispatchTarget(
+ LambdaDescriptor lambdaInstance, AppInfoWithClassHierarchy appInfo) {
if (lambdaInstance.getMainMethod().match(resolvedMethod)) {
DexMethod method = lambdaInstance.implHandle.asMethod();
- DexClass holder = appView.definitionFor(method.holder);
+ DexClass holder = appInfo.definitionFor(method.holder);
if (holder == null) {
assert false;
return null;
}
- DexEncodedMethod encodedMethod = appView.definitionFor(method);
+ DexEncodedMethod encodedMethod = appInfo.definitionFor(method);
if (encodedMethod == null) {
// The targeted method might not exist, eg, Throwable.addSuppressed in an old library.
return null;
}
- return DexClassAndMethod.create(holder, encodedMethod);
+ return new LookupLambdaTarget(
+ lambdaInstance, DexClassAndMethod.create(holder, encodedMethod));
}
- return lookupMaximallySpecificDispatchTarget(lambdaInstance, appView);
+ return lookupMaximallySpecificDispatchTarget(lambdaInstance, appInfo);
}
private DexClassAndMethod lookupVirtualDispatchTarget(
DexProgramClass dynamicInstance,
- AppView<? extends AppInfoWithClassHierarchy> appView,
+ AppInfoWithClassHierarchy appInfo,
DexType resolutionHolder) {
- assert appView.isSubtype(dynamicInstance.type, resolutionHolder).isTrue()
+ assert appInfo.isSubtype(dynamicInstance.type, resolutionHolder)
: dynamicInstance.type + " is not a subtype of " + resolutionHolder;
// TODO(b/148591377): Enable this assertion.
// The dynamic type cannot be an interface.
// assert !dynamicInstance.isInterface();
+ if (resolvedMethod.isPrivateMethod()) {
+ // If the resolved reference is private there is no dispatch.
+ // This is assuming that the method is accessible, which implies self/nest access.
+ return DexClassAndMethod.create(resolvedHolder, resolvedMethod);
+ }
boolean allowPackageBlocked = resolvedMethod.accessFlags.isPackagePrivate();
DexClass current = dynamicInstance;
DexEncodedMethod overrideTarget = resolvedMethod;
while (current != null) {
DexEncodedMethod candidate = lookupOverrideCandidate(overrideTarget, current);
if (candidate == DexEncodedMethod.SENTINEL && allowPackageBlocked) {
- overrideTarget = findWideningOverride(resolvedMethod, current, appView);
+ overrideTarget = findWideningOverride(resolvedMethod, current, appInfo);
allowPackageBlocked = false;
continue;
}
@@ -482,7 +578,7 @@
if (current.type == overrideTarget.method.holder) {
return null;
}
- current = current.superType == null ? null : appView.definitionFor(current.superType);
+ current = current.superType == null ? null : appInfo.definitionFor(current.superType);
continue;
}
return DexClassAndMethod.create(current, candidate);
@@ -492,21 +588,17 @@
if (!resolvedHolder.isInterface()) {
return null;
}
- return lookupMaximallySpecificDispatchTarget(dynamicInstance, appView);
+ return lookupMaximallySpecificDispatchTarget(dynamicInstance, appInfo);
}
private DexClassAndMethod lookupMaximallySpecificDispatchTarget(
- DexProgramClass dynamicInstance, AppView<? extends AppInfoWithClassHierarchy> appView) {
- return appView
- .appInfo()
- .lookupMaximallySpecificMethod(dynamicInstance, resolvedMethod.method);
+ DexProgramClass dynamicInstance, AppInfoWithClassHierarchy appInfo) {
+ return appInfo.lookupMaximallySpecificMethod(dynamicInstance, resolvedMethod.method);
}
private DexClassAndMethod lookupMaximallySpecificDispatchTarget(
- LambdaDescriptor lambdaDescriptor, AppView<? extends AppInfoWithClassHierarchy> appView) {
- return appView
- .appInfo()
- .lookupMaximallySpecificMethod(lambdaDescriptor, resolvedMethod.method);
+ LambdaDescriptor lambdaDescriptor, AppInfoWithClassHierarchy appInfo) {
+ return appInfo.lookupMaximallySpecificMethod(lambdaDescriptor, resolvedMethod.method);
}
/**
@@ -526,9 +618,7 @@
}
private static DexEncodedMethod findWideningOverride(
- DexEncodedMethod resolvedMethod,
- DexClass clazz,
- AppView<? extends AppInfoWithClassHierarchy> appView) {
+ DexEncodedMethod resolvedMethod, DexClass clazz, AppInfoWithClassHierarchy appView) {
// Otherwise, lookup to first override that is distinct from resolvedMethod.
assert resolvedMethod.accessFlags.isPackagePrivate();
while (clazz.superType != null) {
@@ -555,7 +645,7 @@
* the resolved method. It also assumes that resolvedMethod is the actual method to find a
* lookup for (that is, it is either mA or m').
*/
- private static boolean isOverriding(
+ public static boolean isOverriding(
DexEncodedMethod resolvedMethod, DexEncodedMethod candidate) {
assert resolvedMethod.method.match(candidate.method);
assert !candidate.isPrivateMethod();
@@ -597,27 +687,36 @@
@Override
public LookupResult lookupVirtualDispatchTargets(
DexProgramClass context,
- AppView<? extends AppInfoWithClassHierarchy> appView,
+ AppInfoWithClassHierarchy appInfo,
InstantiatedSubTypeInfo instantiatedInfo,
PinnedPredicate pinnedPredicate) {
return LookupResult.getIncompleteEmptyResult();
}
@Override
+ public LookupResult lookupVirtualDispatchTargets(
+ DexProgramClass context,
+ AppInfoWithLiveness appInfo,
+ DexProgramClass refinedReceiverUpperBound,
+ DexProgramClass refinedReceiverLowerBound) {
+ return LookupResult.getIncompleteEmptyResult();
+ }
+
+ @Override
public DexClassAndMethod lookupVirtualDispatchTarget(
- InstantiatedObject instance, AppView<? extends AppInfoWithClassHierarchy> appView) {
+ InstantiatedObject instance, AppInfoWithClassHierarchy appInfo) {
return null;
}
@Override
public DexClassAndMethod lookupVirtualDispatchTarget(
- DexProgramClass dynamicInstance, AppView<? extends AppInfoWithClassHierarchy> appView) {
+ DexProgramClass dynamicInstance, AppInfoWithClassHierarchy appInfo) {
return null;
}
@Override
public DexClassAndMethod lookupVirtualDispatchTarget(
- LambdaDescriptor lambdaInstance, AppView<? extends AppInfoWithClassHierarchy> appView) {
+ LambdaDescriptor lambdaInstance, AppInfoWithClassHierarchy appInfo) {
return null;
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
index fb41239..f452556 100644
--- a/src/main/java/com/android/tools/r8/graph/UseRegistry.java
+++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -35,16 +35,32 @@
public abstract boolean registerInvokeSuper(DexMethod method);
+ public abstract boolean registerInstanceFieldRead(DexField field);
+
+ public boolean registerInstanceFieldReadFromMethodHandle(DexField field) {
+ return registerInstanceFieldRead(field);
+ }
+
public abstract boolean registerInstanceFieldWrite(DexField field);
- public abstract boolean registerInstanceFieldRead(DexField field);
+ public boolean registerInstanceFieldWriteFromMethodHandle(DexField field) {
+ return registerInstanceFieldWrite(field);
+ }
public abstract boolean registerNewInstance(DexType type);
public abstract boolean registerStaticFieldRead(DexField field);
+ public boolean registerStaticFieldReadFromMethodHandle(DexField field) {
+ return registerStaticFieldRead(field);
+ }
+
public abstract boolean registerStaticFieldWrite(DexField field);
+ public boolean registerStaticFieldWriteFromMethodHandle(DexField field) {
+ return registerStaticFieldWrite(field);
+ }
+
public abstract boolean registerTypeReference(DexType type);
public boolean registerConstClass(DexType type) {
@@ -55,20 +71,19 @@
return registerTypeReference(type);
}
- public void registerMethodHandle(
- DexMethodHandle methodHandle, MethodHandleUse use) {
+ public void registerMethodHandle(DexMethodHandle methodHandle, MethodHandleUse use) {
switch (methodHandle.type) {
case INSTANCE_GET:
- registerInstanceFieldRead(methodHandle.asField());
+ registerInstanceFieldReadFromMethodHandle(methodHandle.asField());
break;
case INSTANCE_PUT:
- registerInstanceFieldWrite(methodHandle.asField());
+ registerInstanceFieldWriteFromMethodHandle(methodHandle.asField());
break;
case STATIC_GET:
- registerStaticFieldRead(methodHandle.asField());
+ registerStaticFieldReadFromMethodHandle(methodHandle.asField());
break;
case STATIC_PUT:
- registerStaticFieldWrite(methodHandle.asField());
+ registerStaticFieldWriteFromMethodHandle(methodHandle.asField());
break;
case INVOKE_INSTANCE:
registerInvokeVirtual(methodHandle.asMethod());
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 c85439b..d8d48a2 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
@@ -121,9 +121,8 @@
registration.accept(virtualMethod);
}
DexProgramClass reverseWrapper = wrappersToReverseMap.get(wrapper);
- if (reverseWrapper != null) {
- registration.accept(getConvertMethod(reverseWrapper));
- }
+ assert reverseWrapper != null;
+ registration.accept(getConvertMethod(reverseWrapper));
return this;
}
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 2b55be8..8374410 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
@@ -15,6 +15,9 @@
/** Called when a class is found to be instantiated. */
public void processNewlyInstantiatedClass(DexProgramClass clazz, DexEncodedMethod context) {}
+ /** Called when a class is found to be live. */
+ public void processNewlyLiveClass(DexProgramClass clazz, EnqueuerWorklist worklist) {}
+
/** Called when a field is found to be live. */
public void processNewlyLiveField(DexEncodedField field) {}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
index 4064d79..d5bd10a 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.FieldAccessInfo;
import com.android.tools.r8.graph.FieldAccessInfoCollection;
import com.android.tools.r8.graph.ObjectAllocationInfoCollection;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -75,6 +76,8 @@
* is interpreted as if we known nothing about the value of the field.
*/
private void initializeAbstractInstanceFieldValues() {
+ FieldAccessInfoCollection<?> fieldAccessInfos =
+ appView.appInfo().getFieldAccessInfoCollection();
ObjectAllocationInfoCollection objectAllocationInfos =
appView.appInfo().getObjectAllocationInfoCollection();
objectAllocationInfos.forEachClassWithKnownAllocationSites(
@@ -91,7 +94,10 @@
Map<DexEncodedField, AbstractValue> abstractInstanceFieldValuesForClass =
new IdentityHashMap<>();
for (DexEncodedField field : clazz.instanceFields()) {
- abstractInstanceFieldValuesForClass.put(field, BottomValue.getInstance());
+ FieldAccessInfo fieldAccessInfo = fieldAccessInfos.get(field.field);
+ if (fieldAccessInfo != null && !fieldAccessInfo.hasReflectiveAccess()) {
+ abstractInstanceFieldValuesForClass.put(field, BottomValue.getInstance());
+ }
}
abstractInstanceFieldValues.put(clazz, abstractInstanceFieldValuesForClass);
});
@@ -313,7 +319,7 @@
assert false;
return;
}
- if (!info.hasReflectiveAccess()) {
+ if (!info.hasReflectiveAccess() && !info.isWrittenFromMethodHandle()) {
info.forEachWriteContext(
context ->
fieldWrites.computeIfAbsent(context, ignore -> new ArrayList<>()).add(field));
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
new file mode 100644
index 0000000..b06a015
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
@@ -0,0 +1,261 @@
+// 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.fieldaccess;
+
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+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.FieldAccessFlags;
+import com.android.tools.r8.graph.FieldAccessInfo;
+import com.android.tools.r8.graph.FieldAccessInfoCollection;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.SingleFieldValue;
+import com.android.tools.r8.ir.analysis.value.SingleValue;
+import com.android.tools.r8.ir.conversion.PostMethodProcessor;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.OptionalBool;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.Sets;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+public class TrivialFieldAccessReprocessor {
+
+ private final AppView<AppInfoWithLiveness> appView;
+ private final PostMethodProcessor.Builder postMethodProcessorBuilder;
+
+ /** Updated concurrently from {@link #processClass(DexProgramClass)}. */
+ private final Set<DexEncodedField> fieldsOfInterest = Sets.newConcurrentHashSet();
+
+ /** Updated concurrently from {@link #processClass(DexProgramClass)}. */
+ private final Set<DexEncodedMethod> methodsToReprocess = Sets.newConcurrentHashSet();
+
+ public TrivialFieldAccessReprocessor(
+ AppView<AppInfoWithLiveness> appView,
+ PostMethodProcessor.Builder postMethodProcessorBuilder) {
+ this.appView = appView;
+ this.postMethodProcessorBuilder = postMethodProcessorBuilder;
+ }
+
+ public void run(
+ ExecutorService executorService, OptimizationFeedbackDelayed feedback, Timing timing)
+ throws ExecutionException {
+ AppInfoWithLiveness appInfo = appView.appInfo();
+
+ timing.begin("Trivial field accesses analysis");
+ assert feedback.noUpdatesLeft();
+
+ timing.begin("Compute fields of interest");
+ computeFieldsOfInterest(appInfo);
+ timing.end(); // Compute fields of interest
+
+ if (fieldsOfInterest.isEmpty()) {
+ timing.end(); // Trivial field accesses analysis
+ return;
+ }
+
+ timing.begin("Clear reads from fields of interest");
+ clearReadsFromFieldsOfInterest(appInfo);
+ timing.end(); // Clear reads from fields of interest
+
+ timing.begin("Enqueue methods for reprocessing");
+ enqueueMethodsForReprocessing(appInfo, executorService);
+ timing.end(); // Enqueue methods for reprocessing
+ timing.end(); // Trivial field accesses analysis
+
+ fieldsOfInterest.forEach(OptimizationFeedbackSimple.getInstance()::markFieldAsDead);
+ }
+
+ private void computeFieldsOfInterest(AppInfoWithLiveness appInfo) {
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ for (DexProgramClass clazz : appInfo.classes()) {
+ for (DexEncodedField field : clazz.instanceFields()) {
+ if (canOptimizeField(field, appView)) {
+ fieldsOfInterest.add(field);
+ }
+ }
+ OptionalBool mayRequireClinitField = OptionalBool.unknown();
+ for (DexEncodedField field : clazz.staticFields()) {
+ if (canOptimizeField(field, appView)) {
+ if (mayRequireClinitField.isUnknown()) {
+ mayRequireClinitField =
+ OptionalBool.of(clazz.classInitializationMayHaveSideEffects(appView));
+ }
+ fieldsOfInterest.add(field);
+ }
+ }
+ if (mayRequireClinitField.isTrue()) {
+ DexField clinitField = dexItemFactory.objectMembers.clinitField;
+ if (clazz.lookupStaticField(dexItemFactory.objectMembers.clinitField) == null) {
+ FieldAccessFlags accessFlags =
+ FieldAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_SYNTHETIC
+ | Constants.ACC_FINAL
+ | Constants.ACC_PUBLIC
+ | Constants.ACC_STATIC);
+ clazz.appendStaticField(
+ new DexEncodedField(
+ dexItemFactory.createField(clazz.type, clinitField.type, clinitField.name),
+ accessFlags,
+ DexAnnotationSet.empty(),
+ null));
+ appView.appInfo().invalidateTypeCacheFor(clazz.type);
+ }
+ }
+ }
+ assert verifyNoConstantFieldsOnSynthesizedClasses(appView);
+ }
+
+ private void clearReadsFromFieldsOfInterest(AppInfoWithLiveness appInfo) {
+ FieldAccessInfoCollection<?> fieldAccessInfoCollection = appInfo.getFieldAccessInfoCollection();
+ for (DexEncodedField field : fieldsOfInterest) {
+ fieldAccessInfoCollection.get(field.field).asMutable().clearReads();
+ }
+ }
+
+ private void enqueueMethodsForReprocessing(
+ AppInfoWithLiveness appInfo, ExecutorService executorService) throws ExecutionException {
+ ThreadUtils.processItems(appInfo.classes(), this::processClass, executorService);
+ ThreadUtils.processItems(appInfo.synthesizedClasses(), this::processClass, executorService);
+ postMethodProcessorBuilder.put(methodsToReprocess);
+ }
+
+ private void processClass(DexProgramClass clazz) {
+ for (DexEncodedMethod method : clazz.methods()) {
+ if (method.hasCode()) {
+ method.getCode().registerCodeReferences(method, new TrivialFieldAccessUseRegistry(method));
+ }
+ }
+ }
+
+ private static boolean canOptimizeField(
+ DexEncodedField field, AppView<AppInfoWithLiveness> appView) {
+ FieldAccessInfo fieldAccessInfo =
+ appView.appInfo().getFieldAccessInfoCollection().get(field.field);
+ if (fieldAccessInfo == null || fieldAccessInfo.isAccessedFromMethodHandle()) {
+ return false;
+ }
+ AbstractValue abstractValue = field.getOptimizationInfo().getAbstractValue();
+ if (abstractValue.isSingleValue()) {
+ SingleValue singleValue = abstractValue.asSingleValue();
+ if (!singleValue.isMaterializableInAllContexts(appView)) {
+ return false;
+ }
+ if (singleValue.isSingleConstValue()) {
+ return true;
+ }
+ if (singleValue.isSingleFieldValue()) {
+ SingleFieldValue singleFieldValue = singleValue.asSingleFieldValue();
+ DexField singleField = singleFieldValue.getField();
+ return singleField != field.field
+ && !singleFieldValue.mayHaveFinalizeMethodDirectlyOrIndirectly(appView);
+ }
+ }
+ return false;
+ }
+
+ private static boolean verifyNoConstantFieldsOnSynthesizedClasses(
+ AppView<AppInfoWithLiveness> appView) {
+ for (DexProgramClass clazz : appView.appInfo().synthesizedClasses()) {
+ for (DexEncodedField field : clazz.fields()) {
+ assert field.getOptimizationInfo().getAbstractValue().isUnknown();
+ }
+ }
+ return true;
+ }
+
+ class TrivialFieldAccessUseRegistry extends UseRegistry {
+
+ private final DexEncodedMethod method;
+
+ TrivialFieldAccessUseRegistry(DexEncodedMethod method) {
+ super(appView.dexItemFactory());
+ this.method = method;
+ }
+
+ private boolean registerFieldAccess(DexField field, boolean isStatic) {
+ DexEncodedField encodedField = appView.appInfo().resolveField(field);
+ if (encodedField != null) {
+ if (encodedField.isStatic() == isStatic) {
+ if (fieldsOfInterest.contains(encodedField)) {
+ methodsToReprocess.add(method);
+ }
+ } else {
+ // Should generally not happen.
+ fieldsOfInterest.remove(encodedField);
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean registerInstanceFieldWrite(DexField field) {
+ return registerFieldAccess(field, false);
+ }
+
+ @Override
+ public boolean registerInstanceFieldRead(DexField field) {
+ return registerFieldAccess(field, false);
+ }
+
+ @Override
+ public boolean registerStaticFieldRead(DexField field) {
+ return registerFieldAccess(field, true);
+ }
+
+ @Override
+ public boolean registerStaticFieldWrite(DexField field) {
+ return registerFieldAccess(field, true);
+ }
+
+ @Override
+ public boolean registerInvokeVirtual(DexMethod method) {
+ return false;
+ }
+
+ @Override
+ public boolean registerInvokeDirect(DexMethod method) {
+ return false;
+ }
+
+ @Override
+ public boolean registerInvokeStatic(DexMethod method) {
+ return false;
+ }
+
+ @Override
+ public boolean registerInvokeInterface(DexMethod method) {
+ return false;
+ }
+
+ @Override
+ public boolean registerInvokeSuper(DexMethod method) {
+ return false;
+ }
+
+ @Override
+ public boolean registerNewInstance(DexType type) {
+ return false;
+ }
+
+ @Override
+ public boolean registerTypeReference(DexType type) {
+ return false;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/AbstractFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/AbstractFieldSet.java
index 90320b7..c6e42b6 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/AbstractFieldSet.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/AbstractFieldSet.java
@@ -4,7 +4,9 @@
package com.android.tools.r8.ir.analysis.fieldvalueanalysis;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.GraphLense;
/**
* Implements a lifted subset lattice for fields.
@@ -42,6 +44,8 @@
public abstract boolean contains(DexEncodedField field);
+ public abstract boolean isEmpty();
+
public boolean isBottom() {
return false;
}
@@ -65,4 +69,6 @@
public final boolean strictlyLessThan(AbstractFieldSet other) {
return lessThanOrEqual(other) && !equals(other);
}
+
+ public abstract AbstractFieldSet rewrittenWithLens(AppView<?> appView, GraphLense lens);
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
index bc13aa1..a2a932c 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
@@ -4,7 +4,9 @@
package com.android.tools.r8.ir.analysis.fieldvalueanalysis;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.SetUtils;
import com.google.common.collect.Sets;
@@ -65,6 +67,26 @@
}
@Override
+ public AbstractFieldSet rewrittenWithLens(AppView<?> appView, GraphLense lens) {
+ assert !isEmpty();
+ ConcreteMutableFieldSet rewrittenSet = new ConcreteMutableFieldSet();
+ for (DexEncodedField field : fields) {
+ DexEncodedField rewrittenField = appView.definitionFor(lens.lookupField(field.field));
+ if (rewrittenField == null) {
+ assert false;
+ continue;
+ }
+ rewrittenSet.add(field);
+ }
+ return rewrittenSet;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return fields.isEmpty();
+ }
+
+ @Override
public int size() {
return fields.size();
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/EmptyFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/EmptyFieldSet.java
index c50ded8..73559f3 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/EmptyFieldSet.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/EmptyFieldSet.java
@@ -4,7 +4,9 @@
package com.android.tools.r8.ir.analysis.fieldvalueanalysis;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.GraphLense;
public class EmptyFieldSet extends AbstractFieldSet implements KnownFieldSet {
@@ -37,6 +39,16 @@
}
@Override
+ public AbstractFieldSet rewrittenWithLens(AppView<?> appView, GraphLense lens) {
+ return this;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return true;
+ }
+
+ @Override
public int size() {
return 0;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
index c934275..af990b1 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
@@ -40,6 +40,8 @@
private DominatorTree dominatorTree;
private Map<BasicBlock, AbstractFieldSet> fieldsMaybeReadBeforeBlockInclusiveCache;
+ final Map<DexEncodedField, LinkedList<FieldInstruction>> putsPerField = new IdentityHashMap<>();
+
FieldValueAnalysis(
AppView<AppInfoWithLiveness> appView,
IRCode code,
@@ -78,7 +80,6 @@
// Find all the static-put instructions that assign a field in the enclosing class which is
// guaranteed to be assigned only in the current initializer.
boolean isStraightLineCode = true;
- Map<DexEncodedField, LinkedList<FieldInstruction>> putsPerField = new IdentityHashMap<>();
for (BasicBlock block : code.blocks) {
if (block.getSuccessors().size() >= 2) {
isStraightLineCode = false;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
index f25c566..10dd287 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
import com.android.tools.r8.ir.analysis.value.SingleEnumValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
import com.android.tools.r8.ir.code.ArrayPut;
@@ -55,6 +56,36 @@
}
@Override
+ void computeFieldOptimizationInfo(ClassInitializerDefaultsResult classInitializerDefaultsResult) {
+ super.computeFieldOptimizationInfo(classInitializerDefaultsResult);
+
+ classInitializerDefaultsResult.forEachOptimizedField(
+ (field, value) -> {
+ if (putsPerField.containsKey(field)
+ || !appView.appInfo().isFieldOnlyWrittenInMethod(field, method)) {
+ return;
+ }
+
+ AbstractValueFactory factory = appView.abstractValueFactory();
+ if (value.isDexValueNumber()) {
+ feedback.recordFieldHasAbstractValue(
+ field,
+ appView,
+ factory.createSingleNumberValue(value.asDexValueNumber().getRawValue()));
+ } else if (value.isDexValueString()) {
+ feedback.recordFieldHasAbstractValue(
+ field,
+ appView,
+ factory.createSingleStringValue(value.asDexValueString().getValue()));
+ } else if (value.isDexItemBasedValueString()) {
+ // TODO(b/150835624): Extend to dex item based const strings.
+ } else {
+ assert false : value.getClass().getName();
+ }
+ });
+ }
+
+ @Override
boolean isSubjectToOptimization(DexEncodedField field) {
return field.isStatic()
&& field.holder() == clazz.type
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/UnknownFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/UnknownFieldSet.java
index 9df87cc..f083438 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/UnknownFieldSet.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/UnknownFieldSet.java
@@ -4,7 +4,9 @@
package com.android.tools.r8.ir.analysis.fieldvalueanalysis;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.GraphLense;
public class UnknownFieldSet extends AbstractFieldSet {
@@ -22,7 +24,17 @@
}
@Override
+ public boolean isEmpty() {
+ return false;
+ }
+
+ @Override
public boolean isTop() {
return true;
}
+
+ @Override
+ public AbstractFieldSet rewrittenWithLens(AppView<?> appView, GraphLense lens) {
+ return this;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/modeling/LibraryMethodReadSetModeling.java b/src/main/java/com/android/tools/r8/ir/analysis/modeling/LibraryMethodReadSetModeling.java
index 57f8d2e..34ab168 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/modeling/LibraryMethodReadSetModeling.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/modeling/LibraryMethodReadSetModeling.java
@@ -36,7 +36,7 @@
// Modeling of other library methods.
DexType holder = invokedMethod.holder;
if (holder == dexItemFactory.objectType) {
- if (invokedMethod == dexItemFactory.objectMethods.constructor) {
+ if (invokedMethod == dexItemFactory.objectMembers.constructor) {
return EmptyFieldSet.getInstance();
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
index 717df30..8da13ed 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
@@ -11,36 +11,51 @@
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.EnumValueInfoMapCollection.EnumValueInfo;
+import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap;
import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
+import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.code.CheckCast;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.IntSwitch;
import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.CallGraph.Node;
+import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.optimize.CodeRewriter;
import com.android.tools.r8.ir.optimize.Inliner;
import com.android.tools.r8.ir.optimize.Inliner.Reason;
+import com.android.tools.r8.ir.optimize.controlflow.SwitchCaseAnalyzer;
import com.android.tools.r8.ir.optimize.enums.EnumValueOptimizer;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.ir.optimize.inliner.FixedInliningReasonStrategy;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.PredicateSet;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
import com.google.common.collect.Sets;
import java.util.ArrayList;
+import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
import java.util.function.BooleanSupplier;
-// TODO(b/112437944): Should remove the new Builder() instructions from each dynamicMethod() that
-// references a dead proto builder.
public class GeneratedMessageLiteBuilderShrinker {
private final AppView<? extends AppInfoWithSubtyping> appView;
private final ProtoReferences references;
+ private final Map<DexProgramClass, DexEncodedMethod> builders = new IdentityHashMap<>();
+
GeneratedMessageLiteBuilderShrinker(
AppView<? extends AppInfoWithSubtyping> appView, ProtoReferences references) {
this.appView = appView;
@@ -51,24 +66,66 @@
public boolean deferDeadProtoBuilders(
DexProgramClass clazz, DexEncodedMethod context, BooleanSupplier register) {
if (references.isDynamicMethod(context) && references.isGeneratedMessageLiteBuilder(clazz)) {
- return register.getAsBoolean();
+ if (register.getAsBoolean()) {
+ assert builders.getOrDefault(clazz, context) == context;
+ builders.put(clazz, context);
+ return true;
+ }
}
return false;
}
+ /**
+ * Reprocesses each dynamicMethod() that references a dead builder to remove the dead builder
+ * references.
+ */
+ public void removeDeadBuilderReferencesFromDynamicMethods(
+ AppView<AppInfoWithLiveness> appView, ExecutorService executorService, Timing timing)
+ throws ExecutionException {
+ timing.begin("Remove dead builder references");
+ AppInfoWithLiveness appInfo = appView.appInfo();
+ IRConverter converter = new IRConverter(appView, Timing.empty());
+ CodeRewriter codeRewriter = new CodeRewriter(appView, converter);
+ MethodToInvokeSwitchCaseAnalyzer switchCaseAnalyzer = new MethodToInvokeSwitchCaseAnalyzer();
+ if (switchCaseAnalyzer.isInitialized()) {
+ ThreadUtils.processItems(
+ builders.entrySet(),
+ entry -> {
+ if (!appInfo.isLiveProgramClass(entry.getKey())) {
+ removeDeadBuilderReferencesFromDynamicMethod(
+ appView, entry.getValue(), converter, codeRewriter, switchCaseAnalyzer);
+ }
+ },
+ executorService);
+ }
+ builders.clear();
+ timing.end(); // Remove dead builder references
+ }
+
+ private void removeDeadBuilderReferencesFromDynamicMethod(
+ AppView<AppInfoWithLiveness> appView,
+ DexEncodedMethod dynamicMethod,
+ IRConverter converter,
+ CodeRewriter codeRewriter,
+ SwitchCaseAnalyzer switchCaseAnalyzer) {
+ Origin origin = appView.appInfo().originFor(dynamicMethod.holder());
+ IRCode code = dynamicMethod.buildIR(appView, origin);
+ codeRewriter.rewriteSwitch(code, switchCaseAnalyzer);
+ converter.removeDeadCodeAndFinalizeIR(
+ dynamicMethod, code, OptimizationFeedbackSimple.getInstance(), Timing.empty());
+ }
+
public static void addInliningHeuristicsForBuilderInlining(
AppView<? extends AppInfoWithSubtyping> appView,
PredicateSet<DexType> alwaysClassInline,
Set<DexType> neverMerge,
Set<DexMethod> alwaysInline,
- Set<DexMethod> neverInline,
Set<DexMethod> bypassClinitforInlining) {
new RootSetExtension(
appView,
alwaysClassInline,
neverMerge,
alwaysInline,
- neverInline,
bypassClinitforInlining)
.extend();
}
@@ -177,6 +234,47 @@
}
}
+ private class MethodToInvokeSwitchCaseAnalyzer extends SwitchCaseAnalyzer {
+
+ private final int newBuilderOrdinal;
+
+ private MethodToInvokeSwitchCaseAnalyzer() {
+ EnumValueInfoMap enumValueInfoMap =
+ appView.appInfo().withLiveness().getEnumValueInfoMap(references.methodToInvokeType);
+ if (enumValueInfoMap != null) {
+ EnumValueInfo newBuilderValueInfo =
+ enumValueInfoMap.getEnumValueInfo(references.methodToInvokeMembers.newBuilderField);
+ if (newBuilderValueInfo != null) {
+ newBuilderOrdinal = newBuilderValueInfo.ordinal;
+ return;
+ }
+ }
+ newBuilderOrdinal = -1;
+ }
+
+ public boolean isInitialized() {
+ return newBuilderOrdinal >= 0;
+ }
+
+ @Override
+ public boolean switchCaseIsUnreachable(IntSwitch theSwitch, int index) {
+ if (index != newBuilderOrdinal) {
+ return false;
+ }
+ Value switchValue = theSwitch.value();
+ if (!switchValue.isDefinedByInstructionSatisfying(Instruction::isInvokeVirtual)) {
+ return false;
+ }
+ InvokeVirtual definition = switchValue.definition.asInvokeVirtual();
+ if (definition.getInvokedMethod() != appView.dexItemFactory().enumMethods.ordinal) {
+ return false;
+ }
+ TypeLatticeElement enumType = definition.getReceiver().getTypeLattice();
+ return enumType.isClassType()
+ && enumType.asClassTypeLatticeElement().getClassType() == references.methodToInvokeType;
+ }
+ }
+
private static class RootSetExtension {
private final AppView<? extends AppInfoWithSubtyping> appView;
@@ -186,7 +284,6 @@
private final Set<DexType> neverMerge;
private final Set<DexMethod> alwaysInline;
- private final Set<DexMethod> neverInline;
private final Set<DexMethod> bypassClinitforInlining;
RootSetExtension(
@@ -194,14 +291,12 @@
PredicateSet<DexType> alwaysClassInline,
Set<DexType> neverMerge,
Set<DexMethod> alwaysInline,
- Set<DexMethod> neverInline,
Set<DexMethod> bypassClinitforInlining) {
this.appView = appView;
this.references = appView.protoShrinker().references;
this.alwaysClassInline = alwaysClassInline;
this.neverMerge = neverMerge;
this.alwaysInline = alwaysInline;
- this.neverInline = neverInline;
this.bypassClinitforInlining = bypassClinitforInlining;
}
@@ -210,7 +305,6 @@
// GeneratedMessageLite heuristics.
alwaysInlineCreateBuilderFromGeneratedMessageLite();
- neverInlineIsInitializedFromGeneratedMessageLite();
// * extends GeneratedMessageLite heuristics.
bypassClinitforInliningNewBuilderMethods();
@@ -257,13 +351,5 @@
neverMerge.add(references.generatedMessageLiteBuilderType);
neverMerge.add(references.generatedMessageLiteExtendableBuilderType);
}
-
- /**
- * Without this rule, GeneratedMessageLite$Builder.build() becomes too big for class inlining.
- * TODO(b/112437944): Maybe introduce a -neverinlineinto rule instead?
- */
- private void neverInlineIsInitializedFromGeneratedMessageLite() {
- neverInline.add(references.generatedMessageLiteMethods.isInitializedMethod);
- }
}
}
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 4ff6283..bf6c8b1 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
@@ -6,6 +6,7 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
@@ -32,6 +33,7 @@
import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo;
import com.android.tools.r8.shaking.Enqueuer;
import com.android.tools.r8.shaking.EnqueuerWorklist;
+import com.android.tools.r8.shaking.InstantiationReason;
import com.android.tools.r8.shaking.KeepReason;
import com.android.tools.r8.utils.BitUtils;
import com.android.tools.r8.utils.OptionalBool;
@@ -89,6 +91,34 @@
this.references = protoShrinker.references;
}
+ @Override
+ public void processNewlyLiveClass(DexProgramClass clazz, EnqueuerWorklist worklist) {
+ assert appView.appInfo().hasClassHierarchy();
+ AppInfoWithClassHierarchy appInfo = appView.appInfo().withClassHierarchy();
+ if (appInfo.isStrictSubtypeOf(clazz.type, references.generatedMessageLiteType)) {
+ markGeneratedMessageLiteSubtypeAsInstantiated(clazz, worklist);
+ }
+ }
+
+ private void markGeneratedMessageLiteSubtypeAsInstantiated(
+ DexProgramClass clazz, EnqueuerWorklist worklist) {
+ if (clazz.isAbstract()) {
+ assert clazz.type == references.extendableMessageType;
+ return;
+ }
+ DexEncodedMethod dynamicMethod = clazz.lookupVirtualMethod(references::isDynamicMethod);
+ if (dynamicMethod != null) {
+ worklist.enqueueMarkInstantiatedAction(
+ clazz,
+ dynamicMethod,
+ InstantiationReason.REFLECTION,
+ KeepReason.reflectiveUseIn(dynamicMethod));
+ } else {
+ assert false
+ : "Expected class `" + clazz.type.toSourceString() + "` to declare a dynamicMethod()";
+ }
+ }
+
/**
* When a dynamicMethod() of a proto message becomes live, then build the corresponding {@link
* ProtoMessageInfo} object, and create a mapping from the holder to it.
@@ -372,6 +402,7 @@
newlyLiveField = protoFieldInfo.getOneOfCaseField(appView, protoMessageInfo);
} else if (protoFieldInfo.hasHazzerBitField(protoMessageInfo)) {
newlyLiveField = protoFieldInfo.getHazzerBitField(appView, protoMessageInfo);
+ enqueuer.registerReflectiveFieldAccess(valueStorage.field, dynamicMethod);
}
} else {
// For one-of fields, mark the one-of field as live if the one-of-case field is live, and
@@ -556,8 +587,10 @@
DexType baseMessageType = protoFieldInfo.getBaseMessageType(factory);
if (baseMessageType != null) {
ProtoMessageInfo protoMessageInfo = getOrCreateProtoMessageInfo(baseMessageType);
- assert protoMessageInfo != null;
- return reachesMapOrRequiredField(protoMessageInfo);
+ if (protoMessageInfo != null) {
+ return reachesMapOrRequiredField(protoMessageInfo);
+ }
+ assert false : "Unable to find proto message info for `" + baseMessageType + "`";
}
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeUtils.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeUtils.java
new file mode 100644
index 0000000..912b379
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeUtils.java
@@ -0,0 +1,15 @@
+// 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.type;
+
+import com.android.tools.r8.graph.AppView;
+
+public class TypeUtils {
+
+ public static boolean isNullPointerException(TypeLatticeElement type, AppView<?> appView) {
+ return type.isClassType()
+ && type.asClassTypeLatticeElement().getClassType() == appView.dexItemFactory().npeType;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
index 5371255..404faff 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
@@ -4,6 +4,10 @@
package com.android.tools.r8.ir.analysis.value;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+
public abstract class AbstractValue {
public abstract boolean isNonTrivial();
@@ -28,6 +32,10 @@
return null;
}
+ public boolean isSingleConstValue() {
+ return false;
+ }
+
public boolean isSingleConstClassValue() {
return false;
}
@@ -85,6 +93,9 @@
return UnknownValue.getInstance();
}
+ public abstract AbstractValue rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLense lens);
+
@Override
public abstract boolean equals(Object o);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/BottomValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/BottomValue.java
index ca376e7..f5b27f7 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/BottomValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/BottomValue.java
@@ -4,6 +4,10 @@
package com.android.tools.r8.ir.analysis.value;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+
public class BottomValue extends AbstractValue {
private static final BottomValue INSTANCE = new BottomValue();
@@ -20,6 +24,11 @@
}
@Override
+ public AbstractValue rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLense lens) {
+ return this;
+ }
+
+ @Override
public boolean isNonTrivial() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java
index a6bf499..897b31e 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java
@@ -13,14 +13,16 @@
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.code.ConstClass;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
-public class SingleConstClassValue extends SingleValue {
+public class SingleConstClassValue extends SingleConstValue {
private final DexType type;
@@ -88,4 +90,21 @@
assert baseType.isPrimitiveType();
return true;
}
+
+ @Override
+ public boolean isMaterializableInAllContexts(AppView<?> appView) {
+ DexType baseType = type.toBaseType(appView.dexItemFactory());
+ if (baseType.isClassType()) {
+ DexClass clazz = appView.definitionFor(type);
+ return clazz != null && clazz.isPublic() && clazz.isResolvable(appView);
+ }
+ assert baseType.isPrimitiveType();
+ return true;
+ }
+
+ @Override
+ public SingleValue rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLense lens) {
+ assert lens.lookupType(type) == type;
+ return this;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstValue.java
new file mode 100644
index 0000000..ecf9838
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstValue.java
@@ -0,0 +1,13 @@
+// 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.value;
+
+public abstract class SingleConstValue extends SingleValue {
+
+ @Override
+ public boolean isSingleConstValue() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleEnumValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleEnumValue.java
index 313f977..4beb525 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleEnumValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleEnumValue.java
@@ -4,7 +4,11 @@
package com.android.tools.r8.ir.analysis.value;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
public class SingleEnumValue extends SingleFieldValue {
@@ -27,4 +31,18 @@
public String toString() {
return "SingleEnumValue(" + getField().toSourceString() + ")";
}
+
+ @Override
+ public SingleValue rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLense lens) {
+ DexField field = getField();
+ EnumValueInfoMap unboxedEnumInfo = appView.unboxedEnums().getEnumValueInfoMap(field.type);
+ if (unboxedEnumInfo != null) {
+ // Return the ordinal of the unboxed enum.
+ assert unboxedEnumInfo.hasEnumValueInfo(field);
+ return appView
+ .abstractValueFactory()
+ .createSingleNumberValue(unboxedEnumInfo.getEnumValueInfo(field).convertToInt());
+ }
+ return appView.abstractValueFactory().createSingleEnumValue(lens.lookupField(field));
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
index 0adfe65..dffd9e0 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
@@ -9,15 +9,19 @@
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
public class SingleFieldValue extends SingleValue {
@@ -32,6 +36,18 @@
return field;
}
+ public boolean mayHaveFinalizeMethodDirectlyOrIndirectly(AppView<AppInfoWithLiveness> appView) {
+ DexType fieldType = field.type;
+ if (fieldType.isClassType()) {
+ ClassTypeLatticeElement fieldClassType =
+ TypeLatticeElement.fromDexType(fieldType, maybeNull(), appView)
+ .asClassTypeLatticeElement();
+ return appView.appInfo().mayHaveFinalizeMethodDirectlyOrIndirectly(fieldClassType);
+ }
+ assert fieldType.isArrayType() || fieldType.isPrimitiveType();
+ return false;
+ }
+
@Override
public boolean isSingleFieldValue() {
return true;
@@ -72,4 +88,30 @@
return isMemberVisibleFromOriginalContext(
appView, context, encodedField.field.holder, encodedField.accessFlags);
}
+
+ @Override
+ public boolean isMaterializableInAllContexts(AppView<?> appView) {
+ DexEncodedField encodedField = appView.appInfo().resolveField(field);
+ if (encodedField == null) {
+ assert false;
+ return false;
+ }
+ if (!encodedField.isPublic()) {
+ return false;
+ }
+ DexClass holder = appView.definitionFor(encodedField.holder());
+ if (holder == null) {
+ assert false;
+ return false;
+ }
+ return holder.isPublic();
+ }
+
+ @Override
+ public SingleValue rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLense lens) {
+ DexField rewrittenField = lens.lookupField(field);
+ assert !appView.unboxedEnums().containsEnum(field.holder)
+ || !appView.definitionFor(rewrittenField).accessFlags.isEnum();
+ return appView.abstractValueFactory().createSingleFieldValue(rewrittenField);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
index 4a5ad60..b408c90 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
@@ -8,14 +8,16 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
-public class SingleNumberValue extends SingleValue {
+public class SingleNumberValue extends SingleConstValue {
private final long value;
@@ -79,4 +81,14 @@
public boolean isMaterializableInContext(AppView<?> appView, DexType context) {
return true;
}
+
+ @Override
+ public boolean isMaterializableInAllContexts(AppView<?> appView) {
+ return true;
+ }
+
+ @Override
+ public SingleValue rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLense lens) {
+ return this;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java
index 417fe19..a7439c0 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
import com.android.tools.r8.ir.code.ConstString;
@@ -19,8 +20,9 @@
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
-public class SingleStringValue extends SingleValue {
+public class SingleStringValue extends SingleConstValue {
private final DexString string;
@@ -84,4 +86,14 @@
public boolean isMaterializableInContext(AppView<?> appView, DexType context) {
return true;
}
+
+ @Override
+ public boolean isMaterializableInAllContexts(AppView<?> appView) {
+ return true;
+ }
+
+ @Override
+ public SingleValue rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLense lens) {
+ return this;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java
index c7b249f..1b3e59a 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java
@@ -7,10 +7,12 @@
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfo;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
public abstract class SingleValue extends AbstractValue implements InstanceFieldInitializationInfo {
@@ -37,4 +39,10 @@
AppView<? extends AppInfoWithSubtyping> appView, IRCode code, TypeAndLocalInfoSupplier info);
public abstract boolean isMaterializableInContext(AppView<?> appView, DexType context);
+
+ public abstract boolean isMaterializableInAllContexts(AppView<?> appView);
+
+ @Override
+ public abstract SingleValue rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLense lens);
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/UnknownValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/UnknownValue.java
index 216ea99..2b4f841 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/UnknownValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/UnknownValue.java
@@ -4,6 +4,10 @@
package com.android.tools.r8.ir.analysis.value;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+
public class UnknownValue extends AbstractValue {
private static final UnknownValue INSTANCE = new UnknownValue();
@@ -25,6 +29,11 @@
}
@Override
+ public AbstractValue rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLense lens) {
+ return this;
+ }
+
+ @Override
public boolean equals(Object o) {
return this == o;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
index 0c118fb..17dcc5e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
@@ -251,7 +251,7 @@
}
@Override
- public boolean throwsNpeIfValueIsNull(Value value, DexItemFactory dexItemFactory) {
+ public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, DexType context) {
return array() == value;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
index b2f908c..12ff943 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.cf.code.CfArrayLength;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.AbstractError;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
@@ -140,7 +139,7 @@
}
@Override
- public boolean throwsNpeIfValueIsNull(Value value, DexItemFactory dexItemFactory) {
+ public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, DexType context) {
return array() == value;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
index b8d8526..8ab298a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
@@ -15,7 +15,6 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.ArrayTypeLatticeElement;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
@@ -252,7 +251,7 @@
}
@Override
- public boolean throwsNpeIfValueIsNull(Value value, DexItemFactory dexItemFactory) {
+ public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, DexType context) {
return array() == value;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
index abc0440..c28bc4a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
@@ -4,6 +4,9 @@
package com.android.tools.r8.ir.code;
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
+import static com.android.tools.r8.optimize.MemberRebindingAnalysis.isClassTypeVisibleFromContext;
+
import com.android.tools.r8.cf.LoadStoreHelper;
import com.android.tools.r8.cf.TypeVerificationHelper;
import com.android.tools.r8.cf.code.CfCheckCast;
@@ -11,12 +14,15 @@
import com.android.tools.r8.code.MoveObjectFrom16;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.AbstractError;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
public class CheckCast extends Instruction {
@@ -89,6 +95,44 @@
}
@Override
+ public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ return instructionInstanceCanThrow(appView, context).isThrowing();
+ }
+
+ @Override
+ public AbstractError instructionInstanceCanThrow(AppView<?> appView, DexType context) {
+ if (appView.options().debug || !appView.appInfo().hasLiveness()) {
+ return AbstractError.top();
+ }
+ if (type.isPrimitiveType()) {
+ return AbstractError.top();
+ }
+ DexType baseType = type.toBaseType(appView.dexItemFactory());
+ if (baseType.isClassType()) {
+ DexClass dexClass = appView.definitionFor(baseType);
+ // * NoClassDefFoundError (resolution failure).
+ if (dexClass == null || !dexClass.isResolvable(appView)) {
+ return AbstractError.specific(appView.dexItemFactory().noClassDefFoundErrorType);
+ }
+ // * IllegalAccessError (not visible from the access context).
+ if (!isClassTypeVisibleFromContext(appView, context, dexClass)) {
+ return AbstractError.specific(appView.dexItemFactory().illegalAccessErrorType);
+ }
+ }
+ AppView<? extends AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+ TypeLatticeElement castType =
+ TypeLatticeElement.fromDexType(type, definitelyNotNull(), appView);
+ if (object()
+ .getDynamicUpperBoundType(appViewWithLiveness)
+ .lessThanOrEqualUpToNullability(castType, appView)) {
+ // This is a check-cast that has to be there for bytecode correctness, but R8 has proven
+ // that this cast will never throw.
+ return AbstractError.bottom();
+ }
+ return AbstractError.top();
+ }
+
+ @Override
public boolean instructionTypeCanThrow() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
index 69c6281..366f0cf 100644
--- a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.UnknownFieldSet;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.SingleFieldValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.Sets;
@@ -29,8 +30,13 @@
public enum Assumption {
NONE,
+ CLASS_ALREADY_INITIALIZED,
RECEIVER_NOT_NULL;
+ boolean canAssumeClassIsAlreadyInitialized() {
+ return this == CLASS_ALREADY_INITIALIZED;
+ }
+
boolean canAssumeReceiverIsNotNull() {
return this == RECEIVER_NOT_NULL;
}
@@ -132,7 +138,8 @@
if (!appView.enableWholeProgramOptimizations()) {
return AbstractError.bottom();
}
- boolean mayTriggerClassInitialization = isStaticGet() || isStaticPut();
+ boolean mayTriggerClassInitialization =
+ isStaticFieldInstruction() && !assumption.canAssumeClassIsAlreadyInitialized();
if (mayTriggerClassInitialization) {
// Only check for <clinit> side effects if there is no -assumenosideeffects rule.
if (appView.appInfo().hasLiveness()) {
@@ -192,34 +199,47 @@
* default finalize() method in a field. In that case, it is not safe to remove this instruction,
* since that could change the lifetime of the value.
*/
- boolean isStoringObjectWithFinalizer(AppInfoWithLiveness appInfo) {
+ boolean isStoringObjectWithFinalizer(
+ AppView<AppInfoWithLiveness> appView, DexEncodedField field) {
assert isFieldPut();
+
TypeLatticeElement type = value().getTypeLattice();
TypeLatticeElement baseType =
type.isArrayType() ? type.asArrayTypeLatticeElement().getArrayBaseTypeLattice() : type;
- if (baseType.isClassType()) {
- Value root = value().getAliasedValue();
- if (!root.isPhi() && root.definition.isNewInstance()) {
- DexClass clazz = appInfo.definitionFor(root.definition.asNewInstance().clazz);
- if (clazz == null) {
- return true;
- }
- if (clazz.superType == null) {
- return false;
- }
- DexItemFactory dexItemFactory = appInfo.dexItemFactory();
- DexEncodedMethod resolutionResult =
- appInfo
- .resolveMethod(clazz.type, dexItemFactory.objectMethods.finalize)
- .getSingleTarget();
- return resolutionResult != null && resolutionResult.isProgramMethod(appInfo);
- }
-
- return appInfo.mayHaveFinalizeMethodDirectlyOrIndirectly(
- baseType.asClassTypeLatticeElement());
+ if (!baseType.isClassType()) {
+ return false;
}
- return false;
+ AbstractValue abstractValue = field.getOptimizationInfo().getAbstractValue();
+ if (abstractValue.isSingleValue()) {
+ if (abstractValue.isZero()) {
+ return false;
+ }
+ if (abstractValue.isSingleFieldValue()) {
+ SingleFieldValue singleFieldValue = abstractValue.asSingleFieldValue();
+ return singleFieldValue.mayHaveFinalizeMethodDirectlyOrIndirectly(appView);
+ }
+ }
+
+ AppInfoWithLiveness appInfo = appView.appInfo();
+ Value root = value().getAliasedValue();
+ if (!root.isPhi() && root.definition.isNewInstance()) {
+ DexClass clazz = appView.definitionFor(root.definition.asNewInstance().clazz);
+ if (clazz == null) {
+ return true;
+ }
+ if (clazz.superType == null) {
+ return false;
+ }
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ DexEncodedMethod resolutionResult =
+ appInfo
+ .resolveMethod(clazz.type, dexItemFactory.objectMembers.finalize)
+ .getSingleTarget();
+ return resolutionResult != null && resolutionResult.isProgramMethod(appView);
+ }
+
+ return appInfo.mayHaveFinalizeMethodDirectlyOrIndirectly(baseType.asClassTypeLatticeElement());
}
@Override
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 ff88eed..7f8d5d3 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
@@ -50,6 +50,11 @@
}
@Override
+ public void removeInstructionIgnoreOutValue() {
+ instructionIterator.removeInstructionIgnoreOutValue();
+ }
+
+ @Override
public void replaceCurrentInstructionWithThrowNull(
AppView<? extends AppInfoWithSubtyping> appView,
IRCode code,
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java b/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java
index 8db09e9..5c488c4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java
@@ -65,6 +65,10 @@
return get(Opcodes.AND);
}
+ public boolean mayHaveArrayLength() {
+ return get(Opcodes.ARRAY_LENGTH);
+ }
+
public boolean mayHaveCheckCast() {
return get(Opcodes.CHECK_CAST);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceFieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/InstanceFieldInstruction.java
new file mode 100644
index 0000000..94bfdaf
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceFieldInstruction.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.code;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.FieldInstruction.Assumption;
+
+public interface InstanceFieldInstruction {
+
+ boolean hasOutValue();
+
+ Value outValue();
+
+ Value object();
+
+ boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context, Assumption assumption);
+
+ FieldInstruction asFieldInstruction();
+
+ boolean isInstanceGet();
+
+ InstanceGet asInstanceGet();
+
+ boolean isInstancePut();
+
+ InstancePut asInstancePut();
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
index 2ca1b2a..b070660 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -18,7 +18,6 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
@@ -31,7 +30,7 @@
import com.android.tools.r8.ir.optimize.InliningConstraints;
import java.util.Set;
-public class InstanceGet extends FieldInstruction {
+public class InstanceGet extends FieldInstruction implements InstanceFieldInstruction {
public InstanceGet(Value dest, Value object, DexField field) {
super(field, dest, object);
@@ -56,6 +55,7 @@
return outValue;
}
+ @Override
public Value object() {
assert inValues.size() == 1;
return inValues.get(0);
@@ -119,6 +119,7 @@
return instructionMayHaveSideEffects(appView, context, Assumption.NONE);
}
+ @Override
public boolean instructionMayHaveSideEffects(
AppView<?> appView, DexType context, Assumption assumption) {
return instructionInstanceCanThrow(appView, context, assumption).isThrowing();
@@ -198,7 +199,7 @@
}
@Override
- public boolean throwsNpeIfValueIsNull(Value value, DexItemFactory dexItemFactory) {
+ public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, DexType context) {
return object() == value;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
index cd71750..00a5f30 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -18,7 +18,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
@@ -31,7 +30,7 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.Arrays;
-public class InstancePut extends FieldInstruction {
+public class InstancePut extends FieldInstruction implements InstanceFieldInstruction {
public InstancePut(DexField field, Value object, Value value) {
this(field, object, value, false);
@@ -63,6 +62,7 @@
return visitor.visit(this);
}
+ @Override
public Value object() {
return inValues.get(0);
}
@@ -115,17 +115,24 @@
@Override
public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
- if (appView.appInfo().hasLiveness()) {
- AppInfoWithLiveness appInfoWithLiveness = appView.appInfo().withLiveness();
+ return instructionMayHaveSideEffects(appView, context, Assumption.NONE);
+ }
- if (instructionInstanceCanThrow(appView, context).isThrowing()) {
+ @Override
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, Assumption assumption) {
+ if (appView.appInfo().hasLiveness()) {
+ AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+ AppInfoWithLiveness appInfoWithLiveness = appViewWithLiveness.appInfo();
+
+ if (instructionInstanceCanThrow(appView, context, assumption).isThrowing()) {
return true;
}
DexEncodedField encodedField = appInfoWithLiveness.resolveField(getField());
assert encodedField != null : "NoSuchFieldError (resolution failure) should be caught.";
return appInfoWithLiveness.isFieldRead(encodedField)
- || isStoringObjectWithFinalizer(appInfoWithLiveness);
+ || isStoringObjectWithFinalizer(appViewWithLiveness, encodedField);
}
// In D8, we always have to assume that the field can be read, and thus have side effects.
@@ -220,7 +227,7 @@
}
@Override
- public boolean throwsNpeIfValueIsNull(Value value, DexItemFactory dexItemFactory) {
+ public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, DexType context) {
return object() == value;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 8deda3f..b1459be 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DebugLocalInfo;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.AbstractError;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
@@ -533,6 +532,29 @@
return false;
}
+ public boolean isBlockLocalInstructionWithoutSideEffects(AppView<?> appView, DexType context) {
+ return definesBlockLocalValue() && !instructionMayHaveSideEffects(appView, context);
+ }
+
+ private boolean definesBlockLocalValue() {
+ return !definesValueWithNonLocalUsages();
+ }
+
+ private boolean definesValueWithNonLocalUsages() {
+ if (hasOutValue()) {
+ Value outValue = outValue();
+ if (outValue.numberOfPhiUsers() > 0) {
+ return true;
+ }
+ for (Instruction user : outValue.uniqueUsers()) {
+ if (user.getBlock() != getBlock()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
public boolean instructionTypeCanBeCanonicalized() {
return false;
}
@@ -990,6 +1012,10 @@
return null;
}
+ public boolean isStaticFieldInstruction() {
+ return false;
+ }
+
public boolean isStaticGet() {
return false;
}
@@ -1372,11 +1398,12 @@
* given value is null at runtime execution.
*
* @param value the value representing an object that may be null at runtime execution.
- * @param dexItemFactory where pre-defined descriptors are retrieved
+ * @param appView where pre-defined descriptors are retrieved
+ * @param context
* @return true if the instruction throws NullPointerException if value is null at runtime, false
* otherwise.
*/
- public boolean throwsNpeIfValueIsNull(Value value, DexItemFactory dexItemFactory) {
+ public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, DexType context) {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index db939e8..ec0c68f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -159,10 +159,6 @@
@Override
public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
- if (!appView.enableWholeProgramOptimizations()) {
- return true;
- }
-
if (appView.options().debug) {
return true;
}
@@ -181,42 +177,41 @@
}
// Find the target and check if the invoke may have side effects.
- if (appView.appInfo().hasLiveness()) {
- AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
- DexEncodedMethod target = lookupSingleTarget(appViewWithLiveness, context);
- if (target == null) {
- return true;
- }
-
- // Verify that the target method is accessible in the current context.
- if (!isMemberVisibleFromOriginalContext(
- appView, context, target.method.holder, target.accessFlags)) {
- return true;
- }
-
- // Verify that the target method does not have side-effects.
- DexClass clazz = appView.definitionFor(target.method.holder);
- if (clazz == null) {
- assert false : "Expected to be able to find the enclosing class of a method definition";
- return true;
- }
-
- if (appViewWithLiveness.appInfo().noSideEffects.containsKey(target.method)) {
- return false;
- }
-
- MethodOptimizationInfo optimizationInfo = target.getOptimizationInfo();
- if (target.isInstanceInitializer()) {
- InstanceInitializerInfo initializerInfo = optimizationInfo.getInstanceInitializerInfo();
- if (!initializerInfo.mayHaveOtherSideEffectsThanInstanceFieldAssignments()) {
- return false;
- }
- }
-
- return optimizationInfo.mayHaveSideEffects();
+ if (!appView.appInfo().hasLiveness()) {
+ return true;
}
- return true;
+ AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+ DexEncodedMethod target = lookupSingleTarget(appViewWithLiveness, context);
+ if (target == null) {
+ return true;
+ }
+
+ // Verify that the target method is accessible in the current context.
+ if (!isMemberVisibleFromOriginalContext(
+ appView, context, target.method.holder, target.accessFlags)) {
+ return true;
+ }
+
+ // Verify that the target method does not have side-effects.
+ DexClass clazz = appView.definitionFor(target.method.holder);
+ if (clazz == null) {
+ assert false : "Expected to be able to find the enclosing class of a method definition";
+ return true;
+ }
+
+ if (appViewWithLiveness.appInfo().noSideEffects.containsKey(target.method)) {
+ return false;
+ }
+
+ MethodOptimizationInfo optimizationInfo = target.getOptimizationInfo();
+ if (target.isInstanceInitializer()) {
+ InstanceInitializerInfo initializerInfo = optimizationInfo.getInstanceInitializerInfo();
+ if (!initializerInfo.mayHaveOtherSideEffectsThanInstanceFieldAssignments()) {
+ return false;
+ }
+ }
+ return optimizationInfo.mayHaveSideEffects();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
index 860080f..e9f3063 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
@@ -89,12 +89,15 @@
public DexEncodedMethod lookupSingleTarget(AppView<?> appView, DexType invocationContext) {
if (appView.appInfo().hasLiveness()) {
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
- AppInfoWithLiveness appInfo = appViewWithLiveness.appInfo();
- return appInfo.lookupSingleInterfaceTarget(
- getInvokedMethod(),
- invocationContext,
- TypeAnalysis.getRefinedReceiverType(appViewWithLiveness, this),
- getReceiver().getDynamicLowerBoundType(appViewWithLiveness));
+ return appViewWithLiveness
+ .appInfo()
+ .lookupSingleVirtualTarget(
+ getInvokedMethod(),
+ invocationContext,
+ true,
+ appView,
+ TypeAnalysis.getRefinedReceiverType(appViewWithLiveness, this),
+ getReceiver().getDynamicLowerBoundType(appViewWithLiveness));
}
return null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index df0c10d..401b4df 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.LoadStoreHelper;
import com.android.tools.r8.cf.TypeVerificationHelper;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -25,6 +26,7 @@
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
+import java.util.BitSet;
import java.util.Collection;
import java.util.List;
import java.util.Set;
@@ -89,12 +91,17 @@
.appInfo()
.resolveMethod(method.holder, method)
.lookupVirtualDispatchTargets(
- appView.definitionForProgramType(invocationContext), appView.withLiveness())
+ appView.definitionForProgramType(invocationContext),
+ appView.withLiveness().appInfo())
.asLookupResultSuccess();
- if (lookupResult == null) {
+ if (lookupResult == null || lookupResult.isEmpty()) {
return null;
}
- assert lookupResult.getMethodTargets() != null;
+ assert lookupResult.hasMethodTargets();
+ if (lookupResult.hasLambdaTargets()) {
+ // TODO(b/150277553): Support lambda targets.
+ return null;
+ }
DexType staticReceiverType = getInvokedMethod().holder;
DexType refinedReceiverType =
TypeAnalysis.getRefinedReceiverType(
@@ -107,17 +114,30 @@
if (refinedResolution.isSingleResolution()) {
DexEncodedMethod refinedTarget = refinedResolution.getSingleTarget();
Set<DexEncodedMethod> result = Sets.newIdentityHashSet();
- for (DexEncodedMethod target : lookupResult.getMethodTargets()) {
- if (target == refinedTarget
- || appView.isSubtype(target.method.holder, refinedReceiverType).isPossiblyTrue()) {
- result.add(target);
- }
- }
+ lookupResult.forEach(
+ methodTarget -> {
+ DexEncodedMethod target = methodTarget.getMethod();
+ if (target == refinedTarget
+ || appView
+ .isSubtype(target.method.holder, refinedReceiverType)
+ .isPossiblyTrue()) {
+ result.add(target);
+ }
+ },
+ lambdaTarget -> {
+ throw new Unreachable();
+ });
return result;
}
// If resolution at the refined type fails, conservatively return the full set of targets.
}
- return lookupResult.getMethodTargets();
+ Set<DexEncodedMethod> result = Sets.newIdentityHashSet();
+ lookupResult.forEach(
+ methodTarget -> result.add(methodTarget.getMethod()),
+ lambda -> {
+ assert false;
+ });
+ return result;
}
public abstract InlineAction computeInlining(
@@ -202,4 +222,17 @@
assert lookupDirectTargetOnItself == hierarchyResult;
return true;
}
+
+ @Override
+ public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, DexType context) {
+ DexEncodedMethod singleTarget = lookupSingleTarget(appView, context);
+ if (singleTarget != null) {
+ BitSet nonNullParamOrThrow = singleTarget.getOptimizationInfo().getNonNullParamOrThrow();
+ if (nonNullParamOrThrow != null) {
+ int argumentIndex = inValues.indexOf(value);
+ return argumentIndex >= 0 && nonNullParamOrThrow.get(argumentIndex);
+ }
+ }
+ return false;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
index a42988e..e15a387 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
@@ -5,7 +5,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
@@ -52,8 +51,8 @@
}
@Override
- public boolean throwsNpeIfValueIsNull(Value value, DexItemFactory dexItemFactory) {
- return getReceiver() == value;
+ public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, DexType context) {
+ return value == getReceiver() || super.throwsNpeIfValueIsNull(value, appView, context);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
index e1f67a9..1ec8873 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -93,12 +93,15 @@
public DexEncodedMethod lookupSingleTarget(AppView<?> appView, DexType invocationContext) {
if (appView.appInfo().hasLiveness()) {
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
- AppInfoWithLiveness appInfo = appViewWithLiveness.appInfo();
- return appInfo.lookupSingleVirtualTarget(
- getInvokedMethod(),
- invocationContext,
- TypeAnalysis.getRefinedReceiverType(appViewWithLiveness, this),
- getReceiver().getDynamicLowerBoundType(appViewWithLiveness));
+ return appViewWithLiveness
+ .appInfo()
+ .lookupSingleVirtualTarget(
+ getInvokedMethod(),
+ invocationContext,
+ false,
+ appView,
+ TypeAnalysis.getRefinedReceiverType(appViewWithLiveness, this),
+ getReceiver().getDynamicLowerBoundType(appViewWithLiveness));
}
// In D8, allow lookupSingleTarget() to be used for finding final library methods. This is used
// for library modeling.
@@ -154,6 +157,11 @@
return true;
}
+ if (getInvokedMethod().holder.isArrayType()
+ && getInvokedMethod().match(appView.dexItemFactory().objectMembers.clone)) {
+ return false;
+ }
+
// Check if it is a call to one of library methods that are known to be side-effect free.
Predicate<InvokeMethod> noSideEffectsPredicate =
appView.dexItemFactory().libraryMethodsWithoutSideEffects.get(getInvokedMethod());
diff --git a/src/main/java/com/android/tools/r8/ir/code/Monitor.java b/src/main/java/com/android/tools/r8/ir/code/Monitor.java
index 78b9b64..598bc6c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Monitor.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Monitor.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.code.MonitorExit;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -142,7 +141,7 @@
}
@Override
- public boolean throwsNpeIfValueIsNull(Value value, DexItemFactory dexItemFactory) {
+ public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, DexType context) {
return object() == value;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
index a8fa713..23ac981 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -142,8 +142,10 @@
@Override
public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
if (!appView.enableWholeProgramOptimizations()) {
- return true;
+ return !(dexItemFactory.libraryTypesAssumedToBePresent.contains(clazz)
+ && dexItemFactory.libraryClassesWithoutStaticInitialization.contains(clazz));
}
if (clazz.isPrimitiveType() || clazz.isArrayType()) {
@@ -157,7 +159,7 @@
}
if (definition.isLibraryClass()
- && !appView.dexItemFactory().libraryTypesAssumedToBePresent.contains(clazz)) {
+ && !dexItemFactory.libraryTypesAssumedToBePresent.contains(clazz)) {
return true;
}
@@ -178,13 +180,12 @@
}
// Verify that the object does not have a finalizer.
- DexItemFactory dexItemFactory = appView.dexItemFactory();
ResolutionResult finalizeResolutionResult =
- appView.appInfo().resolveMethod(clazz, dexItemFactory.objectMethods.finalize);
+ appView.appInfo().resolveMethod(clazz, dexItemFactory.objectMembers.finalize);
if (finalizeResolutionResult.isSingleResolution()) {
DexMethod finalizeMethod = finalizeResolutionResult.getSingleTarget().method;
if (finalizeMethod != dexItemFactory.enumMethods.finalize
- && finalizeMethod != dexItemFactory.objectMethods.finalize) {
+ && finalizeMethod != dexItemFactory.objectMembers.finalize) {
return true;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticFieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/StaticFieldInstruction.java
new file mode 100644
index 0000000..9c5a123
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticFieldInstruction.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.code;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.FieldInstruction.Assumption;
+
+public interface StaticFieldInstruction {
+
+ boolean hasOutValue();
+
+ Value outValue();
+
+ boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context, Assumption assumption);
+
+ FieldInstruction asFieldInstruction();
+
+ boolean isStaticFieldInstruction();
+
+ boolean isStaticGet();
+
+ StaticGet asStaticGet();
+
+ boolean isStaticPut();
+
+ StaticPut asStaticPut();
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
index 08285c5..e7c2dee 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -30,7 +30,7 @@
import com.google.common.collect.Sets;
import java.util.Set;
-public class StaticGet extends FieldInstruction {
+public class StaticGet extends FieldInstruction implements StaticFieldInstruction {
public StaticGet(Value dest, DexField field) {
super(field, dest, (Value) null);
@@ -137,7 +137,13 @@
@Override
public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
- return instructionInstanceCanThrow(appView, context).isThrowing();
+ return instructionMayHaveSideEffects(appView, context, Assumption.NONE);
+ }
+
+ @Override
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, Assumption assumption) {
+ return instructionInstanceCanThrow(appView, context, assumption).isThrowing();
}
@Override
@@ -181,6 +187,11 @@
}
@Override
+ public boolean isStaticFieldInstruction() {
+ return true;
+ }
+
+ @Override
public boolean isStaticGet() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
index c9bab8b..9d25691 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -30,7 +30,7 @@
import com.android.tools.r8.shaking.ProguardMemberRule;
import com.google.common.collect.Sets;
-public class StaticPut extends FieldInstruction {
+public class StaticPut extends FieldInstruction implements StaticFieldInstruction {
public StaticPut(Value source, DexField field) {
super(field, null, source);
@@ -95,8 +95,15 @@
@Override
public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ return instructionMayHaveSideEffects(appView, context, Assumption.NONE);
+ }
+
+ @Override
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, Assumption assumption) {
if (appView.appInfo().hasLiveness()) {
- AppInfoWithLiveness appInfoWithLiveness = appView.appInfo().withLiveness();
+ AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+ AppInfoWithLiveness appInfoWithLiveness = appViewWithLiveness.appInfo();
// MemberValuePropagation will replace the field read only if the target field has bound
// -assumevalues rule whose return value is *single*.
//
@@ -107,7 +114,7 @@
return false;
}
- if (instructionInstanceCanThrow(appView, context).isThrowing()) {
+ if (instructionInstanceCanThrow(appView, context, assumption).isThrowing()) {
return true;
}
@@ -122,7 +129,7 @@
}
return appInfoWithLiveness.isFieldRead(encodedField)
- || isStoringObjectWithFinalizer(appInfoWithLiveness);
+ || isStoringObjectWithFinalizer(appViewWithLiveness, encodedField);
}
// In D8, we always have to assume that the field can be read, and thus have side effects.
@@ -195,6 +202,11 @@
}
@Override
+ public boolean isStaticFieldInstruction() {
+ return true;
+ }
+
+ @Override
public boolean isStaticPut() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Throw.java b/src/main/java/com/android/tools/r8/ir/code/Throw.java
index 8a513c2..115e9fd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Throw.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Throw.java
@@ -6,7 +6,7 @@
import com.android.tools.r8.cf.LoadStoreHelper;
import com.android.tools.r8.cf.code.CfThrow;
import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.conversion.CfBuilder;
@@ -87,7 +87,7 @@
}
@Override
- public boolean throwsNpeIfValueIsNull(Value value, DexItemFactory dexItemFactory) {
+ public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, DexType context) {
if (exception() == value) {
return true;
}
@@ -105,7 +105,7 @@
if (!aliasedValue.isPhi()) {
Instruction definition = aliasedValue.getDefinition();
if (definition.isNewInstance()
- && definition.asNewInstance().clazz == dexItemFactory.npeType) {
+ && definition.asNewInstance().clazz == appView.dexItemFactory().npeType) {
// throw new NullPointerException()
return true;
}
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 bdaa741..25ed39f 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
@@ -864,6 +864,10 @@
return isConstant() && getConstInstruction().isConstNumber();
}
+ public boolean isConstZero() {
+ return isConstNumber() && definition.asConstNumber().isZero();
+ }
+
public boolean isConstString() {
return isConstant() && getConstInstruction().isConstString();
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
index 2bb3869..25ddb6a 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
@@ -34,6 +34,7 @@
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Deque;
+import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
@@ -173,7 +174,7 @@
}
} else {
DexEncodedMethod singleTarget =
- appView.appInfo().lookupSingleTarget(type, method, context.holder);
+ appView.appInfo().lookupSingleTarget(type, method, context.holder, appView);
if (singleTarget != null) {
assert !source.accessFlags.isBridge() || singleTarget != currentMethod.method;
DexProgramClass clazz =
@@ -215,9 +216,18 @@
if (resolution.isVirtualTarget()) {
LookupResult lookupResult =
resolution.lookupVirtualDispatchTargets(
- appView.definitionForProgramType(context), appView);
+ appView.definitionForProgramType(context), appView.appInfo());
if (lookupResult.isLookupResultSuccess()) {
- return lookupResult.asLookupResultSuccess().getMethodTargets();
+ // TODO(b/150277553): Add lambda methods to the call graph.
+ Set<DexEncodedMethod> targets = new HashSet<>();
+ lookupResult
+ .asLookupResultSuccess()
+ .forEach(
+ methodTarget -> targets.add(methodTarget.getMethod()),
+ lambdaTarget -> {
+ assert false;
+ });
+ return targets;
}
}
return null;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/FieldOptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/conversion/FieldOptimizationFeedback.java
index 99c2933..a6d6267 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/FieldOptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/FieldOptimizationFeedback.java
@@ -15,6 +15,8 @@
void markFieldCannotBeKept(DexEncodedField field);
+ void markFieldAsDead(DexEncodedField field);
+
void markFieldAsPropagated(DexEncodedField field);
void markFieldHasDynamicLowerBoundType(DexEncodedField field, ClassTypeLatticeElement type);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 47c2c79..f4e315b 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -27,6 +27,7 @@
import com.android.tools.r8.ir.analysis.TypeChecker;
import com.android.tools.r8.ir.analysis.constant.SparseConditionalConstantPropagation;
import com.android.tools.r8.ir.analysis.fieldaccess.FieldAccessAnalysis;
+import com.android.tools.r8.ir.analysis.fieldaccess.TrivialFieldAccessReprocessor;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.InstanceFieldValueAnalysis;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValueAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
@@ -94,6 +95,7 @@
import com.android.tools.r8.shaking.MainDexClasses;
import com.android.tools.r8.utils.CfgPrinter;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.DesugarState;
import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
@@ -700,8 +702,11 @@
}
if (enumUnboxer != null) {
enumUnboxer.finishAnalysis();
- enumUnboxer.unboxEnums(postMethodProcessorBuilder);
+ enumUnboxer.unboxEnums(postMethodProcessorBuilder, executorService, feedback);
}
+ new TrivialFieldAccessReprocessor(appView.withLiveness(), postMethodProcessorBuilder)
+ .run(executorService, feedback, timing);
+
timing.begin("IR conversion phase 2");
graphLenseForIR = appView.graphLense();
PostMethodProcessor postMethodProcessor =
@@ -754,6 +759,7 @@
generateDesugaredLibraryAPIWrappers(builder, executorService);
if (serviceLoaderRewriter != null && serviceLoaderRewriter.getSynthesizedClass() != null) {
+ appView.appInfo().addSynthesizedClass(serviceLoaderRewriter.getSynthesizedClass());
processSynthesizedServiceLoaderMethods(
serviceLoaderRewriter.getSynthesizedClass(), executorService);
builder.addSynthesizedClass(serviceLoaderRewriter.getSynthesizedClass(), true);
@@ -824,13 +830,8 @@
// Check if what we've added to the application builder as synthesized classes are same as
// what we've added and used through AppInfo.
- assert appView
- .appInfo()
- .getSynthesizedClassesForSanityCheck()
- .containsAll(builder.getSynthesizedClasses())
- && builder
- .getSynthesizedClasses()
- .containsAll(appView.appInfo().getSynthesizedClassesForSanityCheck());
+ assert appView.appInfo().synthesizedClasses().containsAll(builder.getSynthesizedClasses())
+ && builder.getSynthesizedClasses().containsAll(appView.appInfo().synthesizedClasses());
return builder.build();
}
@@ -1066,18 +1067,10 @@
private Timing rewriteCode(
DexEncodedMethod method, OptimizationFeedback feedback, MethodProcessor methodProcessor) {
Origin origin = appView.appInfo().originFor(method.method.holder);
- try {
- return rewriteCodeInternal(method, feedback, methodProcessor, origin);
- } catch (CompilationError e) {
- // If rewriting throws a compilation error, attach the origin and method if missing.
- throw e.withAdditionalOriginAndPositionInfo(origin, new MethodPosition(method.method));
- } catch (NullPointerException e) {
- throw new CompilationError(
- "NullPointerException during IR Conversion",
- e,
- origin,
- new MethodPosition(method.method));
- }
+ return ExceptionUtils.withOriginAttachmentHandler(
+ origin,
+ new MethodPosition(method.method),
+ () -> rewriteCodeInternal(method, feedback, methodProcessor, origin));
}
private Timing rewriteCodeInternal(
@@ -1357,9 +1350,11 @@
invertConditionalsForTesting(code);
}
- timing.begin("Rewrite throw NPE");
- codeRewriter.rewriteThrowNullPointerException(code);
- timing.end();
+ if (!isDebugMode) {
+ timing.begin("Rewrite throw NPE");
+ codeRewriter.rewriteThrowNullPointerException(code);
+ timing.end();
+ }
timing.begin("Optimize class initializers");
ClassInitializerDefaultsResult classInitializerDefaultsResult =
@@ -1673,18 +1668,27 @@
private void markProcessed(DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) {
// After all the optimizations have take place, we compute whether method should be inlined.
- ConstraintWithTarget state;
- if (!options.enableInlining
- || inliner == null
- || method.isClassInitializer()
- || method.getOptimizationInfo().isReachabilitySensitive()) {
- state = ConstraintWithTarget.NEVER;
- } else {
- state = inliner.computeInliningConstraint(code, method);
- }
+ ConstraintWithTarget state =
+ shouldComputeInliningConstraint(method)
+ ? inliner.computeInliningConstraint(code, method)
+ : ConstraintWithTarget.NEVER;
feedback.markProcessed(method, state);
}
+ private boolean shouldComputeInliningConstraint(DexEncodedMethod method) {
+ if (!options.enableInlining || inliner == null) {
+ return false;
+ }
+ if (method.isClassInitializer() || method.getOptimizationInfo().isReachabilitySensitive()) {
+ return false;
+ }
+ if (appView.appInfo().hasLiveness()
+ && appView.appInfo().withLiveness().isPinned(method.method)) {
+ return false;
+ }
+ return true;
+ }
+
private synchronized void updateHighestSortingStrings(DexEncodedMethod method) {
DexString highestSortingReferencedString = method.getCode().asDexCode().highestSortingString;
if (highestSortingReferencedString != null) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
index 6052427..e742528 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
@@ -83,7 +83,7 @@
}
}
- void put(Set<DexEncodedMethod> methodsToRevisit) {
+ public void put(Set<DexEncodedMethod> methodsToRevisit) {
put(methodsToRevisit, defaultCodeOptimizations);
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchConverter.java
index b6a02df..41d1363 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchConverter.java
@@ -634,28 +634,29 @@
if (numberOfInstructions == 1) {
JumpInstruction exit = end.exit();
if (exit.isIf()) {
- return extendWithIf(toBeExtended, exit.asIf());
+ return extendWithIf(toBeExtended, exit.asIf(), block);
}
if (exit.isIntSwitch()) {
- return extendWithSwitch(toBeExtended, exit.asIntSwitch());
+ return extendWithSwitch(toBeExtended, exit.asIntSwitch(), block);
}
}
if (numberOfInstructions == 2) {
Instruction entry = end.entry();
Instruction exit = end.exit();
if (entry.isConstNumber() && entry.outValue().onlyUsedInBlock(end) && exit.isIf()) {
- return extendWithIf(toBeExtended, exit.asIf());
+ return extendWithIf(toBeExtended, exit.asIf(), block);
}
}
// Not an extension of `toBeExtended`.
return setFallthroughBlock(toBeExtended, block);
}
- private IdToTargetMapping extendWithIf(IdToTargetMapping toBeExtended, If theIf) {
+ private IdToTargetMapping extendWithIf(
+ IdToTargetMapping toBeExtended, If theIf, BasicBlock fallthroughBlock) {
If.Type type = theIf.getType();
if (type != If.Type.EQ && type != If.Type.NE) {
// Not an extension of `toBeExtended`.
- return setFallthroughBlock(toBeExtended, theIf.getBlock());
+ return setFallthroughBlock(toBeExtended, fallthroughBlock);
}
// Read the `id` value. This value is known to be a phi, so just look for a phi.
@@ -672,7 +673,7 @@
if (idValue == null || (toBeExtended != null && idValue != toBeExtended.idValue)) {
// Not an extension of `toBeExtended`.
- return setFallthroughBlock(toBeExtended, theIf.getBlock());
+ return setFallthroughBlock(toBeExtended, fallthroughBlock);
}
// Now read the constant value that `id` is being compared to in this if-instruction.
@@ -684,7 +685,7 @@
Value root = other.getAliasedValue();
if (root.isPhi() || !root.definition.isConstNumber()) {
// Not an extension of `toBeExtended`.
- return setFallthroughBlock(toBeExtended, theIf.getBlock());
+ return setFallthroughBlock(toBeExtended, fallthroughBlock);
}
ConstNumber constNumberInstruction = root.definition.asConstNumber();
id = constNumberInstruction.getIntValue();
@@ -701,11 +702,11 @@
}
private IdToTargetMapping extendWithSwitch(
- IdToTargetMapping toBeExtended, IntSwitch theSwitch) {
+ IdToTargetMapping toBeExtended, IntSwitch theSwitch, BasicBlock fallthroughBlock) {
Value switchValue = theSwitch.value();
if (!switchValue.isPhi() || (toBeExtended != null && switchValue != toBeExtended.idValue)) {
// Not an extension of `toBeExtended`.
- return setFallthroughBlock(toBeExtended, theSwitch.getBlock());
+ return setFallthroughBlock(toBeExtended, fallthroughBlock);
}
Phi idValue = switchValue.asPhi();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchRemover.java b/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchRemover.java
index 41f22a4..2e8bf77 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchRemover.java
@@ -77,6 +77,8 @@
BasicBlock block,
StringSwitch theSwitch,
Set<BasicBlock> newBlocks) {
+ int nextBlockNumber = code.getHighestBlockNumber() + 1;
+
BasicBlock fallthroughBlock = theSwitch.fallthroughBlock();
Map<DexString, BasicBlock> stringToTargetMap = new IdentityHashMap<>();
theSwitch.forEachCase(stringToTargetMap::put);
@@ -118,7 +120,7 @@
if (blocksTargetedByMultipleSwitchCases.contains(targetBlock)) {
// Need an intermediate block to avoid critical edges.
BasicBlock intermediateBlock =
- BasicBlock.createGotoBlock(code.blocks.size(), Position.none(), code.metadata());
+ BasicBlock.createGotoBlock(nextBlockNumber++, Position.none(), code.metadata());
intermediateBlock.link(targetBlock);
blockIterator.add(intermediateBlock);
newBlocks.add(intermediateBlock);
@@ -127,7 +129,7 @@
BasicBlock newBlock =
BasicBlock.createIfBlock(
- code.blocks.size(),
+ nextBlockNumber++,
ifInstruction,
code.metadata(),
constStringInstruction,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
index 069015f..3527f62 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
@@ -20,7 +20,6 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
@@ -120,12 +119,6 @@
this.vivifiedSourceFile = appView.dexItemFactory().createString("vivified");
}
- public static boolean isSynthesizedWrapper(DexType type) {
- // Slow path, but more convenient since no instance is needed. Use hasSynthesized(DexType) when
- // possible.
- return type.descriptor.toString().startsWith("L" + WRAPPER_PREFIX);
- }
-
boolean hasSynthesized(DexType type) {
return type.descriptor.startsWith(dexWrapperPrefix);
}
@@ -139,10 +132,14 @@
}
DexType getTypeWrapper(DexType type) {
+ // Force create the reverse wrapper.
+ getWrapper(type, VIVIFIED_TYPE_WRAPPER_SUFFIX, vivifiedTypeWrappers);
return getWrapper(type, TYPE_WRAPPER_SUFFIX, typeWrappers);
}
DexType getVivifiedTypeWrapper(DexType type) {
+ // Force create the reverse wrapper.
+ getWrapper(type, TYPE_WRAPPER_SUFFIX, typeWrappers);
return getWrapper(type, VIVIFIED_TYPE_WRAPPER_SUFFIX, vivifiedTypeWrappers);
}
@@ -249,25 +246,10 @@
new DexEncodedMethod[] {synthesizeConstructor(wrapperField.field), conversionMethod},
virtualMethods,
factory.getSkipNameValidationForTesting(),
- getChecksumSupplier(this, clazz.type),
+ DexProgramClass::checksumFromType,
Collections.emptyList());
}
- private ChecksumSupplier getChecksumSupplier(
- DesugaredLibraryWrapperSynthesizer synthesizer, DexType keyType) {
- return clazz -> {
- // The synthesized type wrappers are constructed lazily, so their lookup must be delayed
- // until the point the checksum is requested (at write time). The presence of a wrapper
- // affects the implementation of the conversion functions, so they must be accounted for in
- // the checksum.
- boolean hasWrapper = synthesizer.typeWrappers.containsKey(keyType);
- boolean hasViviWrapper = synthesizer.vivifiedTypeWrappers.containsKey(keyType);
- return ((long) clazz.type.hashCode())
- + 7 * (long) Boolean.hashCode(hasWrapper)
- + 11 * (long) Boolean.hashCode(hasViviWrapper);
- };
- }
-
private DexEncodedMethod[] synthesizeVirtualMethodsForVivifiedTypeWrapper(
DexClass dexClass, DexEncodedField wrapperField) {
List<DexEncodedMethod> dexMethods = allImplementedMethods(dexClass);
@@ -284,7 +266,14 @@
Set<DexMethod> finalMethods = Sets.newIdentityHashSet();
for (DexEncodedMethod dexEncodedMethod : dexMethods) {
DexClass holderClass = appView.definitionFor(dexEncodedMethod.method.holder);
- assert holderClass != null;
+ boolean isInterface;
+ if (holderClass == null) {
+ assert appView.options().desugaredLibraryConfiguration.getEmulateLibraryInterface()
+ .containsValue(dexEncodedMethod.method.holder);
+ isInterface = true;
+ } else {
+ isInterface = holderClass.isInterface();
+ }
DexMethod methodToInstall =
factory.createMethod(
wrapperField.field.holder,
@@ -302,7 +291,7 @@
methodToInstall,
wrapperField.field,
converter,
- holderClass.isInterface())
+ isInterface)
.generateCfCode();
}
DexEncodedMethod newDexEncodedMethod =
@@ -585,23 +574,25 @@
private DexEncodedMethod generateTypeConversion(DexType type, DexType typeWrapperType) {
DexType reverse = vivifiedTypeWrappers.get(type);
+ assert reverse != null;
return synthesizeConversionMethod(
typeWrapperType,
type,
type,
vivifiedTypeFor(type),
- reverse == null ? null : wrappedValueField(reverse, vivifiedTypeFor(type)));
+ wrappedValueField(reverse, vivifiedTypeFor(type)));
}
private DexEncodedMethod generateVivifiedTypeConversion(
DexType type, DexType vivifiedTypeWrapperType) {
DexType reverse = typeWrappers.get(type);
+ assert reverse != null;
return synthesizeConversionMethod(
vivifiedTypeWrapperType,
type,
vivifiedTypeFor(type),
type,
- reverse == null ? null : wrappedValueField(reverse, type));
+ wrappedValueField(reverse, type));
}
private DexEncodedMethod synthesizeConversionMethod(
@@ -609,7 +600,7 @@
DexType type,
DexType argType,
DexType returnType,
- DexField reverseFieldOrNull) {
+ DexField reverseField) {
DexMethod method =
factory.createMethod(
holder, factory.createProto(returnType, argType), factory.convertMethodName);
@@ -629,7 +620,7 @@
new APIConverterWrapperConversionCfCodeProvider(
appView,
argType,
- reverseFieldOrNull,
+ reverseField,
factory.createField(holder, returnType, factory.wrapperFieldName))
.generateCfCode();
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java
index 2900761..a8161d4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java
@@ -22,7 +22,7 @@
@Override
protected void prepareInstructions() {
// Super constructor call (always java.lang.Object.<init>()).
- DexMethod objectInitMethod = factory().objectMethods.constructor;
+ DexMethod objectInitMethod = factory().objectMembers.constructor;
add(
builder -> {
assert builder.getReceiverValue() != null;
@@ -55,7 +55,7 @@
// be treated as equal, since it only has one call to super constructor,
// which is always java.lang.Object.<init>().
return captures().length == 0
- ? System.identityHashCode(factory().objectMethods.constructor)
+ ? System.identityHashCode(factory().objectMembers.constructor)
: super.hashCode();
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSynthesizedCode.java
index 906e15f..1809d69 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSynthesizedCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSynthesizedCode.java
@@ -22,7 +22,7 @@
@Override
public Consumer<UseRegistry> getRegistryCallback() {
return registry -> {
- registry.registerInvokeDirect(dexItemFactory().objectMethods.constructor);
+ registry.registerInvokeDirect(dexItemFactory().objectMembers.constructor);
DexType[] capturedTypes = captures();
for (int i = 0; i < capturedTypes.length; i++) {
registry.registerInstanceFieldWrite(lambda.getCaptureField(i));
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethodRewrites.java
index 911faa5..de16e98 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethodRewrites.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethodRewrites.java
@@ -35,7 +35,7 @@
DexItemFactory factory,
Set<Value> affectedValues) {
InvokeVirtual getClass =
- new InvokeVirtual(factory.objectMethods.getClass, null, invoke.inValues());
+ new InvokeVirtual(factory.objectMembers.getClass, null, invoke.inValues());
if (invoke.hasOutValue()) {
affectedValues.addAll(invoke.outValue().affectedValues());
invoke.outValue().replaceUsers(invoke.inValues().get(0));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 80c36cc..60813ab 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -26,6 +26,7 @@
import com.android.tools.r8.ir.analysis.equivalence.BasicBlockBehavioralSubsumption;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
+import com.android.tools.r8.ir.analysis.type.TypeUtils;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.code.AlwaysMaterializingNop;
import com.android.tools.r8.ir.code.ArrayLength;
@@ -73,6 +74,7 @@
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.code.Xor;
import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.optimize.controlflow.SwitchCaseAnalyzer;
import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOutputMode;
@@ -240,10 +242,64 @@
// Rewrite 'throw new NullPointerException()' to 'throw null'.
public void rewriteThrowNullPointerException(IRCode code) {
+ boolean shouldRemoveUnreachableBlocks = false;
for (BasicBlock block : code.blocks) {
InstructionListIterator it = block.listIterator(code);
while (it.hasNext()) {
Instruction instruction = it.next();
+
+ // Check for the patterns 'if (x == null) throw null' and
+ // 'if (x == null) throw new NullPointerException()'.
+ if (instruction.isIf()) {
+ if (appView.dexItemFactory().objectsMethods.isRequireNonNullMethod(code.method.method)) {
+ continue;
+ }
+
+ If ifInstruction = instruction.asIf();
+ if (!ifInstruction.isZeroTest()) {
+ continue;
+ }
+
+ Value value = ifInstruction.lhs();
+ if (!value.getTypeLattice().isReference()) {
+ assert value.getTypeLattice().isPrimitive();
+ continue;
+ }
+
+ BasicBlock valueIsNullTarget = ifInstruction.targetFromCondition(0);
+ if (valueIsNullTarget.getPredecessors().size() != 1
+ || !valueIsNullTarget.exit().isThrow()) {
+ continue;
+ }
+
+ Throw throwInstruction = valueIsNullTarget.exit().asThrow();
+ Value exceptionValue = throwInstruction.exception();
+ if (!exceptionValue.isConstZero()
+ && !TypeUtils.isNullPointerException(exceptionValue.getTypeLattice(), appView)) {
+ continue;
+ }
+
+ boolean canDetachValueIsNullTarget = true;
+ for (Instruction i : valueIsNullTarget.instructionsBefore(throwInstruction)) {
+ if (!i.isBlockLocalInstructionWithoutSideEffects(appView, code.method.holder())) {
+ canDetachValueIsNullTarget = false;
+ break;
+ }
+ }
+ if (!canDetachValueIsNullTarget) {
+ continue;
+ }
+
+ rewriteIfToRequireNonNull(
+ block,
+ it,
+ ifInstruction,
+ ifInstruction.targetFromCondition(1),
+ valueIsNullTarget,
+ throwInstruction.getPosition());
+ shouldRemoveUnreachableBlocks = true;
+ }
+
// Check for 'new-instance NullPointerException' with 2 users, not declaring a local and
// not ending the scope of any locals.
if (instruction.isNewInstance()
@@ -297,6 +353,12 @@
}
}
}
+ if (shouldRemoveUnreachableBlocks) {
+ Set<Value> affectedValues = code.removeUnreachableBlocks();
+ if (!affectedValues.isEmpty()) {
+ new TypeAnalysis(appView).narrowing(affectedValues);
+ }
+ }
assert code.isConsistentSSA();
}
@@ -843,7 +905,11 @@
return outliersAsIfSize;
}
- private boolean rewriteSwitch(IRCode code) {
+ public boolean rewriteSwitch(IRCode code) {
+ return rewriteSwitch(code, SwitchCaseAnalyzer.getInstance());
+ }
+
+ public boolean rewriteSwitch(IRCode code, SwitchCaseAnalyzer switchCaseAnalyzer) {
if (!code.metadata().mayHaveIntSwitch()) {
return false;
}
@@ -859,7 +925,7 @@
IntSwitch theSwitch = instruction.asIntSwitch();
if (options.testing.enableDeadSwitchCaseElimination) {
SwitchCaseEliminator eliminator =
- removeUnnecessarySwitchCases(code, theSwitch, iterator);
+ removeUnnecessarySwitchCases(code, theSwitch, iterator, switchCaseAnalyzer);
if (eliminator != null) {
if (eliminator.mayHaveIntroducedUnreachableBlocks()) {
needToRemoveUnreachableBlocks = true;
@@ -1004,7 +1070,10 @@
}
private SwitchCaseEliminator removeUnnecessarySwitchCases(
- IRCode code, IntSwitch theSwitch, InstructionListIterator iterator) {
+ IRCode code,
+ IntSwitch theSwitch,
+ InstructionListIterator iterator,
+ SwitchCaseAnalyzer switchCaseAnalyzer) {
BasicBlock defaultTarget = theSwitch.fallthroughBlock();
SwitchCaseEliminator eliminator = null;
BasicBlockBehavioralSubsumption behavioralSubsumption =
@@ -1016,7 +1085,7 @@
// This switch case can be removed if the behavior of the target block is equivalent to the
// behavior of the default block, or if the switch case is unreachable.
- if (switchCaseIsUnreachable(theSwitch, i)
+ if (switchCaseAnalyzer.switchCaseIsUnreachable(theSwitch, i)
|| behavioralSubsumption.isSubsumedBy(targetBlock, defaultTarget)) {
if (eliminator == null) {
eliminator = new SwitchCaseEliminator(theSwitch, iterator);
@@ -1030,12 +1099,6 @@
return eliminator;
}
- private boolean switchCaseIsUnreachable(IntSwitch theSwitch, int index) {
- Value switchValue = theSwitch.value();
- return switchValue.hasValueRange()
- && !switchValue.getValueRange().containsValue(theSwitch.getKey(index));
- }
-
/**
* Rewrite all branch targets to the destination of trivial goto chains when possible. Does not
* rewrite fallthrough targets as that would require block reordering and the transformation only
@@ -2701,7 +2764,10 @@
throwNullInsnIterator.replaceCurrentInstruction(notReachableThrow);
}
}
- code.removeUnreachableBlocks();
+ Set<Value> affectedValues = code.removeUnreachableBlocks();
+ if (!affectedValues.isEmpty()) {
+ new TypeAnalysis(appView).narrowing(affectedValues);
+ }
assert code.isConsistentSSA();
}
@@ -2850,6 +2916,32 @@
assert block.exit().asGoto().getTarget() == target;
}
+ private void rewriteIfToRequireNonNull(
+ BasicBlock block,
+ InstructionListIterator iterator,
+ If theIf,
+ BasicBlock target,
+ BasicBlock deadTarget,
+ Position position) {
+ deadTarget.unlinkSinglePredecessorSiblingsAllowed();
+ assert theIf == block.exit();
+ iterator.previous();
+ Instruction instruction;
+ if (appView.options().canUseRequireNonNull()) {
+ DexMethod requireNonNullMethod = appView.dexItemFactory().objectsMethods.requireNonNull;
+ instruction = new InvokeStatic(requireNonNullMethod, null, ImmutableList.of(theIf.lhs()));
+ } else {
+ DexMethod getClassMethod = appView.dexItemFactory().objectMembers.getClass;
+ instruction = new InvokeVirtual(getClassMethod, null, ImmutableList.of(theIf.lhs()));
+ }
+ instruction.setPosition(position);
+ iterator.add(instruction);
+ iterator.next();
+ iterator.replaceCurrentInstruction(new Goto());
+ assert block.exit().isGoto();
+ assert block.exit().asGoto().getTarget() == target;
+ }
+
private void rewriteIfWithConstZero(IRCode code, BasicBlock block) {
If theIf = block.exit().asIf();
if (theIf.isZeroTest()) {
@@ -3280,8 +3372,9 @@
iterator = isNotNullBlock.listIterator(code);
iterator.setInsertionPosition(position);
value = code.createValue(TypeLatticeElement.classClassType(appView, definitelyNotNull()));
- iterator.add(new InvokeVirtual(dexItemFactory.objectMethods.getClass, value,
- ImmutableList.of(arguments.get(i))));
+ iterator.add(
+ new InvokeVirtual(
+ dexItemFactory.objectMembers.getClass, value, ImmutableList.of(arguments.get(i))));
iterator.add(new InvokeVirtual(print, null, ImmutableList.of(out, value)));
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
index 06c5dfe..0a314aa 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
@@ -4,9 +4,12 @@
package com.android.tools.r8.ir.optimize;
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
+
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
@@ -62,27 +65,35 @@
ClassTypeLatticeElement dynamicLowerBoundType;
if (current.isInvokeMethod()) {
InvokeMethod invoke = current.asInvokeMethod();
+ DexMethod invokedMethod = invoke.getInvokedMethod();
- DexType staticReturnTypeRaw = invoke.getInvokedMethod().proto.returnType;
+ DexType staticReturnTypeRaw = invokedMethod.proto.returnType;
if (!staticReturnTypeRaw.isReferenceType()) {
continue;
}
- DexEncodedMethod singleTarget =
- invoke.lookupSingleTarget(appView, code.method.method.holder);
- if (singleTarget == null) {
- continue;
- }
+ if (invokedMethod.holder.isArrayType()
+ && invokedMethod.match(appView.dexItemFactory().objectMembers.clone)) {
+ dynamicUpperBoundType =
+ TypeLatticeElement.fromDexType(invokedMethod.holder, definitelyNotNull(), appView);
+ dynamicLowerBoundType = null;
+ } else {
+ DexEncodedMethod singleTarget =
+ invoke.lookupSingleTarget(appView, code.method.method.holder);
+ if (singleTarget == null) {
+ continue;
+ }
- MethodOptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo();
- if (optimizationInfo.returnsArgument()) {
- // Don't insert an assume-instruction since we will replace all usages of the out-value by
- // the corresponding argument.
- continue;
- }
+ MethodOptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo();
+ if (optimizationInfo.returnsArgument()) {
+ // Don't insert an assume-instruction since we will replace all usages of the out-value
+ // by the corresponding argument.
+ continue;
+ }
- dynamicUpperBoundType = optimizationInfo.getDynamicUpperBoundType();
- dynamicLowerBoundType = optimizationInfo.getDynamicLowerBoundType();
+ dynamicUpperBoundType = optimizationInfo.getDynamicUpperBoundType();
+ dynamicLowerBoundType = optimizationInfo.getDynamicLowerBoundType();
+ }
} else if (current.isStaticGet()) {
StaticGet staticGet = current.asStaticGet();
DexEncodedField encodedField = appView.appInfo().resolveField(staticGet.getField());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
index 303b4ea..9cbc694 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
@@ -270,8 +270,12 @@
InstructionListIterator it = entryBlock.listIterator(code);
while (it.hasNext()) {
Instruction current = it.next();
- if (current.hasOutValue() && canonicalizedInvoke.inValues().contains(current.outValue())) {
- numberOfInValuePassed++;
+ if (current.hasOutValue()) {
+ for (Value inValue : canonicalizedInvoke.inValues()) {
+ if (inValue == current.outValue()) {
+ numberOfInValuePassed++;
+ }
+ }
}
if (numberOfInValuePassed == canonicalizedInvoke.inValues().size()) {
// If this invocation uses arguments and this iteration ends in the middle of Arguments,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 0403117..c9bf058 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -757,7 +757,7 @@
DexMethod requireNonNullMethod = appView.dexItemFactory().objectsMethods.requireNonNull;
iterator.add(new InvokeStatic(requireNonNullMethod, null, ImmutableList.of(receiver)));
} else {
- DexMethod getClassMethod = appView.dexItemFactory().objectMethods.getClass;
+ DexMethod getClassMethod = appView.dexItemFactory().objectMembers.getClass;
iterator.add(new InvokeVirtual(getClassMethod, null, ImmutableList.of(receiver)));
}
} else {
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 a28181f..783ed8a 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
@@ -3,12 +3,15 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.optimize;
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexDefinition;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
+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.ir.analysis.type.Nullability;
@@ -20,12 +23,17 @@
import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.IRMetadata;
-import com.android.tools.r8.ir.code.InstanceGet;
+import com.android.tools.r8.ir.code.InstanceFieldInstruction;
+import com.android.tools.r8.ir.code.InstancePut;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.StaticFieldInstruction;
+import com.android.tools.r8.ir.code.StaticGet;
+import com.android.tools.r8.ir.code.StaticPut;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
@@ -195,12 +203,19 @@
iterator.replaceCurrentInstruction(replacement);
} else {
assert lookup.type == RuleType.ASSUME_VALUES;
- if (current.outValue() != null) {
+ BasicBlock block = current.getBlock();
+ Position position = current.getPosition();
+ if (current.hasOutValue()) {
assert replacement.outValue() != null;
current.outValue().replaceUsers(replacement.outValue());
}
- replacement.setPosition(current.getPosition());
- if (current.getBlock().hasCatchHandlers()) {
+ if (current.isStaticGet()) {
+ StaticGet staticGet = current.asStaticGet();
+ replaceStaticFieldInstructionByClinitAccessIfPossible(
+ staticGet, staticGet.getField().holder, code, iterator, code.method.holder());
+ }
+ replacement.setPosition(position);
+ if (block.hasCatchHandlers()) {
iterator.split(code, blocks).listIterator(code).add(replacement);
} else {
iterator.add(replacement);
@@ -342,22 +357,25 @@
affectedValues.addAll(current.outValue().affectedValues());
DexType context = code.method.method.holder;
if (current.instructionMayHaveSideEffects(appView, context)) {
+ BasicBlock block = current.getBlock();
+ Position position = current.getPosition();
+
// All usages are replaced by the replacement value.
current.outValue().replaceUsers(replacement.outValue());
// To preserve side effects, original field-get is replaced by an explicit null-check, if
// the field-get instruction may only fail with an NPE, or the field-get remains as-is.
- Instruction currentOrNullCheck;
if (current.isInstanceGet()) {
- currentOrNullCheck =
- replaceInstanceGetByNullCheckIfPossible(current.asInstanceGet(), iterator, context);
+ replaceInstanceFieldInstructionByNullCheckIfPossible(
+ current.asInstanceGet(), iterator, context);
} else {
- currentOrNullCheck = current;
+ replaceStaticFieldInstructionByClinitAccessIfPossible(
+ current.asStaticGet(), target.holder(), code, iterator, context);
}
// Insert the definition of the replacement.
- replacement.setPosition(currentOrNullCheck.getPosition());
- if (currentOrNullCheck.getBlock().hasCatchHandlers()) {
+ replacement.setPosition(position);
+ if (block.hasCatchHandlers()) {
iterator.split(code, blocks).listIterator(code).add(replacement);
} else {
iterator.add(replacement);
@@ -369,24 +387,78 @@
}
}
- private Instruction replaceInstanceGetByNullCheckIfPossible(
- InstanceGet instruction, InstructionListIterator iterator, DexType context) {
- assert !instruction.outValue().hasAnyUsers();
+ private void replaceInstanceFieldInstructionByNullCheckIfPossible(
+ InstanceFieldInstruction instruction, InstructionListIterator iterator, DexType context) {
+ assert !instruction.hasOutValue() || !instruction.outValue().hasAnyUsers();
if (instruction.instructionMayHaveSideEffects(
appView, context, FieldInstruction.Assumption.RECEIVER_NOT_NULL)) {
- return instruction;
+ return;
}
Value receiver = instruction.object();
+ if (receiver.isNeverNull()) {
+ iterator.removeOrReplaceByDebugLocalRead();
+ return;
+ }
InvokeMethod replacement;
if (appView.options().canUseRequireNonNull()) {
DexMethod requireNonNullMethod = appView.dexItemFactory().objectsMethods.requireNonNull;
replacement = new InvokeStatic(requireNonNullMethod, null, ImmutableList.of(receiver));
} else {
- DexMethod getClassMethod = appView.dexItemFactory().objectMethods.getClass;
+ DexMethod getClassMethod = appView.dexItemFactory().objectMembers.getClass;
replacement = new InvokeVirtual(getClassMethod, null, ImmutableList.of(receiver));
}
iterator.replaceCurrentInstruction(replacement);
- return replacement;
+ }
+
+ private void replaceInstancePutByNullCheckIfNeverRead(
+ IRCode code, InstructionListIterator iterator, InstancePut current) {
+ DexEncodedField target = appView.appInfo().resolveField(current.getField());
+ if (target == null || appView.appInfo().isFieldRead(target)) {
+ return;
+ }
+
+ if (target.isStatic()) {
+ return;
+ }
+
+ replaceInstanceFieldInstructionByNullCheckIfPossible(current, iterator, code.method.holder());
+ }
+
+ private void replaceStaticPutByClinitAccessIfNeverRead(
+ IRCode code, InstructionListIterator iterator, StaticPut current) {
+ DexEncodedField field = appView.appInfo().resolveField(current.getField());
+ if (field == null || appView.appInfo().isFieldRead(field)) {
+ return;
+ }
+
+ if (!field.isStatic()) {
+ return;
+ }
+
+ replaceStaticFieldInstructionByClinitAccessIfPossible(
+ current, field.holder(), code, iterator, code.method.holder());
+ }
+
+ private void replaceStaticFieldInstructionByClinitAccessIfPossible(
+ StaticFieldInstruction instruction,
+ DexType holder,
+ IRCode code,
+ InstructionListIterator iterator,
+ DexType context) {
+ if (instruction.instructionMayHaveSideEffects(
+ appView, context, FieldInstruction.Assumption.CLASS_ALREADY_INITIALIZED)) {
+ return;
+ }
+ DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(holder));
+ if (clazz != null) {
+ DexEncodedField clinitField =
+ clazz.lookupStaticField(appView.dexItemFactory().objectMembers.clinitField);
+ if (clinitField != null) {
+ Value dest = code.createValue(TypeLatticeElement.getInt());
+ StaticGet replacement = new StaticGet(dest, clinitField.field);
+ iterator.replaceCurrentInstruction(replacement);
+ }
+ }
}
/**
@@ -396,7 +468,7 @@
*/
public void rewriteWithConstantValues(IRCode code, DexType callingContext) {
IRMetadata metadata = code.metadata();
- if (!metadata.mayHaveFieldGet() && !metadata.mayHaveInvokeMethod()) {
+ if (!metadata.mayHaveFieldInstruction() && !metadata.mayHaveInvokeMethod()) {
return;
}
@@ -413,6 +485,10 @@
} else if (current.isFieldGet()) {
rewriteFieldGetWithConstantValues(
code, affectedValues, blocks, iterator, current.asFieldInstruction());
+ } else if (current.isInstancePut()) {
+ replaceInstancePutByNullCheckIfNeverRead(code, iterator, current.asInstancePut());
+ } else if (current.isStaticPut()) {
+ replaceStaticPutByClinitAccessIfNeverRead(code, iterator, current.asStaticPut());
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
index 8c07d3d..4fa0312 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
@@ -127,7 +127,7 @@
// Case (5), field-get instructions that are guaranteed to read a non-null value.
FieldInstruction fieldInstruction = current.asFieldInstruction();
DexField field = fieldInstruction.getField();
- if (field.type.isClassType() && isNullableReferenceTypeWithUsers(outValue)) {
+ if (field.type.isReferenceType() && isNullableReferenceTypeWithUsers(outValue)) {
DexEncodedField encodedField = appView.appInfo().resolveField(field);
if (encodedField != null) {
FieldOptimizationInfo optimizationInfo = encodedField.getOptimizationInfo();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
index fa41f21..055eeca 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
@@ -83,7 +83,7 @@
DexItemFactory dexItemFactory = appView.dexItemFactory();
DexMethod invokedMethod = invoke.getInvokedMethod();
// Class<?> Object#getClass() is final and cannot be overridden.
- if (invokedMethod != dexItemFactory.objectMethods.getClass) {
+ if (invokedMethod != dexItemFactory.objectMembers.getClass) {
return null;
}
Value in = invoke.getReceiver();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index 291f4d5..6707fe1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -343,7 +343,7 @@
DexItemFactory dexItemFactory = appView.dexItemFactory();
for (DexEncodedMethod method : clazz.virtualMethods()) {
if (method.method.name == dexItemFactory.finalizeMethodName
- && method.method.proto == dexItemFactory.objectMethods.finalize.proto) {
+ && method.method.proto == dexItemFactory.objectMembers.finalize.proto) {
return EligibilityStatus.HAS_FINALIZER;
}
}
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 5612322..22344de 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
@@ -446,7 +446,7 @@
}
DexMethod invokedMethod = invoke.getInvokedMethod();
- if (invokedMethod == dexItemFactory.objectMethods.constructor) {
+ if (invokedMethod == dexItemFactory.objectMembers.constructor) {
continue;
}
@@ -498,7 +498,7 @@
if (instruction.isInvokeMethodWithReceiver()) {
InvokeMethodWithReceiver invoke = instruction.asInvokeMethodWithReceiver();
DexMethod invokedMethod = invoke.getInvokedMethod();
- if (invokedMethod == dexItemFactory.objectMethods.constructor) {
+ if (invokedMethod == dexItemFactory.objectMembers.constructor) {
continue;
}
@@ -557,7 +557,7 @@
// Remove the call to java.lang.Object.<init>().
if (user.isInvokeDirect()) {
if (root.isNewInstance()
- && invoke.getInvokedMethod() == dexItemFactory.objectMethods.constructor) {
+ && invoke.getInvokedMethod() == dexItemFactory.objectMembers.constructor) {
removeInstruction(invoke);
continue;
}
@@ -743,7 +743,7 @@
// Check that the entire constructor chain can be inlined into the current context.
DexMethod parent = instanceInitializerInfo.getParent();
- while (parent != dexItemFactory.objectMethods.constructor) {
+ while (parent != dexItemFactory.objectMembers.constructor) {
if (parent == null) {
return null;
}
@@ -1177,7 +1177,7 @@
}
private boolean isInstanceInitializerEligibleForClassInlining(DexMethod method) {
- if (method == dexItemFactory.objectMethods.constructor) {
+ if (method == dexItemFactory.objectMembers.constructor) {
return true;
}
DexEncodedMethod encodedMethod = appView.definitionFor(method);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/controlflow/SwitchCaseAnalyzer.java b/src/main/java/com/android/tools/r8/ir/optimize/controlflow/SwitchCaseAnalyzer.java
new file mode 100644
index 0000000..6c22a72
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/controlflow/SwitchCaseAnalyzer.java
@@ -0,0 +1,25 @@
+// 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.controlflow;
+
+import com.android.tools.r8.ir.code.IntSwitch;
+import com.android.tools.r8.ir.code.Value;
+
+public class SwitchCaseAnalyzer {
+
+ private static final SwitchCaseAnalyzer INSTANCE = new SwitchCaseAnalyzer();
+
+ public SwitchCaseAnalyzer() {}
+
+ public static SwitchCaseAnalyzer getInstance() {
+ return INSTANCE;
+ }
+
+ public boolean switchCaseIsUnreachable(IntSwitch theSwitch, int index) {
+ Value switchValue = theSwitch.value();
+ return switchValue.hasValueRange()
+ && !switchValue.getValueRange().containsValue(theSwitch.getKey(index));
+ }
+}
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 ee87965..e96b211 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
@@ -38,6 +38,10 @@
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.conversion.PostMethodProcessor;
import com.android.tools.r8.ir.conversion.PostOptimization;
+import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo;
+import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback.OptimizationInfoFixer;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.Reporter;
@@ -227,21 +231,56 @@
return Reason.ELIGIBLE;
}
- public void unboxEnums(PostMethodProcessor.Builder postBuilder) {
+ public void unboxEnums(
+ PostMethodProcessor.Builder postBuilder,
+ ExecutorService executorService,
+ OptimizationFeedbackDelayed feedback)
+ throws ExecutionException {
// At this point the enumsToUnbox are no longer candidates, they will all be unboxed.
if (enumsUnboxingCandidates.isEmpty()) {
return;
}
ImmutableSet<DexType> enumsToUnbox = ImmutableSet.copyOf(this.enumsUnboxingCandidates.keySet());
- appView.setUnboxedEnums(enumsToUnbox);
NestedGraphLense enumUnboxingLens = new TreeFixer(enumsToUnbox).fixupTypeReferences();
enumUnboxerRewriter = new EnumUnboxingRewriter(appView, enumsToUnbox);
+ appView.setUnboxedEnums(enumUnboxerRewriter.getEnumsToUnbox());
if (enumUnboxingLens != null) {
appView.setGraphLense(enumUnboxingLens);
appView.setAppInfo(
appView
.appInfo()
.rewrittenWithLens(appView.appInfo().app().asDirect(), enumUnboxingLens));
+
+ // Update optimization info.
+ feedback.fixupOptimizationInfos(
+ appView,
+ executorService,
+ new OptimizationInfoFixer() {
+ @Override
+ public void fixup(DexEncodedField field) {
+ FieldOptimizationInfo optimizationInfo = field.getOptimizationInfo();
+ if (optimizationInfo.isMutableFieldOptimizationInfo()) {
+ optimizationInfo
+ .asMutableFieldOptimizationInfo()
+ .fixupAbstractValue(appView, appView.graphLense());
+ } else {
+ assert optimizationInfo.isDefaultFieldOptimizationInfo();
+ }
+ }
+
+ @Override
+ public void fixup(DexEncodedMethod method) {
+ MethodOptimizationInfo optimizationInfo = method.getOptimizationInfo();
+ if (optimizationInfo.isUpdatableMethodOptimizationInfo()) {
+ optimizationInfo
+ .asUpdatableMethodOptimizationInfo()
+ .fixupAbstractReturnValue(appView, appView.graphLense())
+ .fixupInstanceInitializerInfo(appView, appView.graphLense());
+ } else {
+ assert optimizationInfo.isDefaultMethodOptimizationInfo();
+ }
+ }
+ });
}
postBuilder.put(this);
postBuilder.mapDexEncodedMethods(appView);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index ea26434..cbd3824 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -76,6 +76,10 @@
"$enumboxing$ordinal");
}
+ public EnumValueInfoMapCollection getEnumsToUnbox() {
+ return enumsToUnbox;
+ }
+
void rewriteCode(IRCode code) {
// We should not process the enum methods, they will be removed and they may contain invalid
// rewriting rules.
@@ -117,7 +121,7 @@
EnumValueInfo enumValueInfo = enumValueInfoMap.getEnumValueInfo(staticGet.getField());
assert enumValueInfo != null
: "Invalid read to " + staticGet.getField().name + ", error during enum analysis";
- instruction = new ConstNumber(staticGet.outValue(), enumValueInfo.ordinal + 1);
+ instruction = new ConstNumber(staticGet.outValue(), enumValueInfo.convertToInt());
staticGet
.outValue()
.setTypeLattice(PrimitiveTypeLatticeElement.fromNumericType(NumericType.INT));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
index ddf697d..30766c5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfo;
import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap;
@@ -19,6 +20,7 @@
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.ConstString;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.IRMetadata;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.IntSwitch;
@@ -46,7 +48,9 @@
@SuppressWarnings("ConstantConditions")
public void rewriteConstantEnumMethodCalls(IRCode code) {
- if (!code.metadata().mayHaveInvokeMethodWithReceiver()) {
+ IRMetadata metadata = code.metadata();
+ if (!metadata.mayHaveInvokeMethodWithReceiver()
+ && !(metadata.mayHaveInvokeStatic() && metadata.mayHaveArrayLength())) {
return;
}
@@ -54,65 +58,79 @@
while (iterator.hasNext()) {
Instruction current = iterator.next();
- if (!current.isInvokeMethodWithReceiver()) {
- continue;
- }
- InvokeMethodWithReceiver methodWithReceiver = current.asInvokeMethodWithReceiver();
- DexMethod invokedMethod = methodWithReceiver.getInvokedMethod();
- boolean isOrdinalInvoke = invokedMethod == factory.enumMethods.ordinal;
- boolean isNameInvoke = invokedMethod == factory.enumMethods.name;
- boolean isToStringInvoke = invokedMethod == factory.enumMethods.toString;
- if (!isOrdinalInvoke && !isNameInvoke && !isToStringInvoke) {
- continue;
- }
-
- Value receiver = methodWithReceiver.getReceiver().getAliasedValue();
- if (receiver.isPhi()) {
- continue;
- }
- Instruction definition = receiver.getDefinition();
- if (!definition.isStaticGet()) {
- continue;
- }
- DexField enumField = definition.asStaticGet().getField();
-
- EnumValueInfoMap valueInfoMap =
- appView.appInfo().withLiveness().getEnumValueInfoMap(enumField.type);
- if (valueInfoMap == null) {
- continue;
- }
-
- // The receiver value is identified as being from a constant enum field lookup by the fact
- // that it is a static-get to a field whose type is the same as the enclosing class (which
- // is known to be an enum type). An enum may still define a static field using the enum type
- // so ensure the field is present in the ordinal map for final validation.
- EnumValueInfo valueInfo = valueInfoMap.getEnumValueInfo(enumField);
- if (valueInfo == null) {
- continue;
- }
-
- Value outValue = methodWithReceiver.outValue();
- if (isOrdinalInvoke) {
- iterator.replaceCurrentInstruction(new ConstNumber(outValue, valueInfo.ordinal));
- } else if (isNameInvoke) {
- iterator.replaceCurrentInstruction(
- new ConstString(outValue, enumField.name, ThrowingInfo.NO_THROW));
- } else {
- assert isToStringInvoke;
- DexClass enumClazz = appView.appInfo().definitionFor(enumField.type);
- if (!enumClazz.accessFlags.isFinal()) {
+ if (current.isInvokeMethodWithReceiver()) {
+ InvokeMethodWithReceiver methodWithReceiver = current.asInvokeMethodWithReceiver();
+ DexMethod invokedMethod = methodWithReceiver.getInvokedMethod();
+ boolean isOrdinalInvoke = invokedMethod == factory.enumMethods.ordinal;
+ boolean isNameInvoke = invokedMethod == factory.enumMethods.name;
+ boolean isToStringInvoke = invokedMethod == factory.enumMethods.toString;
+ if (!isOrdinalInvoke && !isNameInvoke && !isToStringInvoke) {
continue;
}
- DexEncodedMethod singleTarget =
- appView
- .appInfo()
- .resolveMethodOnClass(valueInfo.type, factory.objectMethods.toString)
- .getSingleTarget();
- if (singleTarget != null && singleTarget.method != factory.enumMethods.toString) {
+
+ Value receiver = methodWithReceiver.getReceiver().getAliasedValue();
+ if (receiver.isPhi()) {
continue;
}
- iterator.replaceCurrentInstruction(
- new ConstString(outValue, enumField.name, ThrowingInfo.NO_THROW));
+ Instruction definition = receiver.getDefinition();
+ if (!definition.isStaticGet()) {
+ continue;
+ }
+ DexField enumField = definition.asStaticGet().getField();
+ EnumValueInfoMap valueInfoMap =
+ appView.appInfo().withLiveness().getEnumValueInfoMap(enumField.type);
+ if (valueInfoMap == null) {
+ continue;
+ }
+
+ // The receiver value is identified as being from a constant enum field lookup by the fact
+ // that it is a static-get to a field whose type is the same as the enclosing class (which
+ // is known to be an enum type). An enum may still define a static field using the enum type
+ // so ensure the field is present in the ordinal map for final validation.
+ EnumValueInfo valueInfo = valueInfoMap.getEnumValueInfo(enumField);
+ if (valueInfo == null) {
+ continue;
+ }
+
+ Value outValue = methodWithReceiver.outValue();
+ if (isOrdinalInvoke) {
+ iterator.replaceCurrentInstruction(new ConstNumber(outValue, valueInfo.ordinal));
+ } else if (isNameInvoke) {
+ iterator.replaceCurrentInstruction(
+ new ConstString(outValue, enumField.name, ThrowingInfo.NO_THROW));
+ } else {
+ assert isToStringInvoke;
+ DexClass enumClazz = appView.appInfo().definitionFor(enumField.type);
+ if (!enumClazz.accessFlags.isFinal()) {
+ continue;
+ }
+ DexEncodedMethod singleTarget =
+ appView
+ .appInfo()
+ .resolveMethodOnClass(valueInfo.type, factory.objectMembers.toString)
+ .getSingleTarget();
+ if (singleTarget != null && singleTarget.method != factory.enumMethods.toString) {
+ continue;
+ }
+ iterator.replaceCurrentInstruction(
+ new ConstString(outValue, enumField.name, ThrowingInfo.NO_THROW));
+ }
+ } else if (current.isArrayLength()) {
+ // Rewrites MyEnum.values().length to a constant int.
+ Instruction arrayDefinition = current.asArrayLength().array().definition;
+ if (arrayDefinition != null && arrayDefinition.isInvokeStatic()) {
+ DexMethod invokedMethod = arrayDefinition.asInvokeStatic().getInvokedMethod();
+ DexProgramClass enumClass = appView.definitionForProgramType(invokedMethod.holder);
+ if (enumClass != null
+ && enumClass.isEnum()
+ && factory.enumMethods.isValuesMethod(invokedMethod, enumClass)) {
+ EnumValueInfoMap enumValueInfoMap =
+ appView.appInfo().withLiveness().getEnumValueInfoMap(invokedMethod.holder);
+ if (enumValueInfoMap != null) {
+ iterator.replaceCurrentInstructionWithConstInt(code, enumValueInfoMap.size());
+ }
+ }
+ }
}
}
assert code.isConsistentSSA();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultFieldOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultFieldOptimizationInfo.java
index a30c24f..8248e52 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultFieldOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultFieldOptimizationInfo.java
@@ -50,6 +50,11 @@
}
@Override
+ public boolean isDead() {
+ return false;
+ }
+
+ @Override
public boolean valueHasBeenPropagated() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/FieldOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/FieldOptimizationInfo.java
index 9103459..33a6e75 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/FieldOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/FieldOptimizationInfo.java
@@ -26,6 +26,8 @@
public abstract TypeLatticeElement getDynamicUpperBoundType();
+ public abstract boolean isDead();
+
public abstract boolean valueHasBeenPropagated();
public boolean isDefaultFieldOptimizationInfo() {
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 b5215f6..84e4fdc 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
@@ -96,7 +96,6 @@
import com.android.tools.r8.utils.Pair;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.Sets;
-import com.google.common.collect.Streams;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.BitSet;
@@ -412,6 +411,8 @@
return null;
}
+ AliasedValueConfiguration aliasesThroughAssumeAndCheckCasts =
+ AssumeAndCheckCastAliasedValueConfiguration.getInstance();
Value receiver = code.getThis();
boolean hasCatchHandler = false;
for (BasicBlock block : code.blocks) {
@@ -487,12 +488,14 @@
if (field == null) {
return null;
}
- if (instancePut.object().getAliasedValue() != receiver
+ Value object =
+ instancePut.object().getAliasedValue(aliasesThroughAssumeAndCheckCasts);
+ if (object != receiver
|| instancePut.instructionInstanceCanThrow(appView, clazz.type).isThrowing()) {
builder.setMayHaveOtherSideEffectsThanInstanceFieldAssignments();
}
- Value value = instancePut.value().getAliasedValue();
+ Value value = instancePut.value().getAliasedValue(aliasesThroughAssumeAndCheckCasts);
// TODO(b/142762134): Replace the use of onlyDependsOnArgument() by
// ValueMayDependOnEnvironmentAnalysis.
if (!value.onlyDependsOnArgument()) {
@@ -518,13 +521,14 @@
}
// java.lang.Enum.<init>() and java.lang.Object.<init>() are considered trivial.
if (invokedMethod == dexItemFactory.enumMethods.constructor
- || invokedMethod == dexItemFactory.objectMethods.constructor) {
+ || invokedMethod == dexItemFactory.objectMembers.constructor) {
builder.setParent(invokedMethod);
break;
}
builder.merge(singleTarget.getOptimizationInfo().getInstanceInitializerInfo());
for (int i = 1; i < invoke.arguments().size(); i++) {
- Value argument = invoke.arguments().get(i).getAliasedValue();
+ Value argument =
+ invoke.arguments().get(i).getAliasedValue(aliasesThroughAssumeAndCheckCasts);
if (argument == receiver) {
// In the analysis of the parent constructor, we don't consider the non-receiver
// arguments as being aliases of the receiver. Therefore, we explicitly mark
@@ -543,7 +547,7 @@
.markAllFieldsAsRead()
.setMayHaveOtherSideEffectsThanInstanceFieldAssignments();
for (Value inValue : invoke.inValues()) {
- if (inValue.getAliasedValue() == receiver) {
+ if (inValue.getAliasedValue(aliasesThroughAssumeAndCheckCasts) == receiver) {
builder.setReceiverMayEscapeOutsideConstructorChain();
break;
}
@@ -559,7 +563,7 @@
builder.setMayHaveOtherSideEffectsThanInstanceFieldAssignments();
}
for (Value argument : invoke.arguments()) {
- if (argument.getAliasedValue() == receiver) {
+ if (argument.getAliasedValue(aliasesThroughAssumeAndCheckCasts) == receiver) {
builder.setReceiverMayEscapeOutsideConstructorChain();
break;
}
@@ -576,7 +580,7 @@
.markAllFieldsAsRead()
.setMayHaveOtherSideEffectsThanInstanceFieldAssignments();
for (Value argument : invoke.arguments()) {
- if (argument.getAliasedValue() == receiver) {
+ if (argument.getAliasedValue(aliasesThroughAssumeAndCheckCasts) == receiver) {
builder.setReceiverMayEscapeOutsideConstructorChain();
break;
}
@@ -783,7 +787,7 @@
if (isInstantiationOfNullPointerException(instr, it, appView.dexItemFactory())) {
it.next(); // Skip call to NullPointerException.<init>.
return InstructionEffect.NO_EFFECT;
- } else if (instr.throwsNpeIfValueIsNull(value, appView.dexItemFactory())) {
+ } else if (instr.throwsNpeIfValueIsNull(value, appView, code.method.holder())) {
// In order to preserve NPE semantic, the exception must not be caught by any handler.
// Therefore, we must ignore this instruction if it is covered by a catch handler.
// Note: this is a conservative approach where we consider that any catch handler could
@@ -961,10 +965,14 @@
// {v0}, T`.
mayHaveSideEffects = true;
} else {
+ mayHaveSideEffects = false;
// Otherwise, check if there is an instruction that has side effects.
- mayHaveSideEffects =
- Streams.stream(code.instructions())
- .anyMatch(instruction -> instruction.instructionMayHaveSideEffects(appView, context));
+ for (Instruction instruction : code.instructions()) {
+ if (instruction.instructionMayHaveSideEffects(appView, context)) {
+ mayHaveSideEffects = true;
+ break;
+ }
+ }
}
if (!mayHaveSideEffects) {
feedback.methodMayNotHaveSideEffects(method);
@@ -981,13 +989,13 @@
ResolutionResult resolutionResult =
appView
.appInfo()
- .resolveMethodOnClass(clazz, appView.dexItemFactory().objectMethods.finalize);
+ .resolveMethodOnClass(clazz, appView.dexItemFactory().objectMembers.finalize);
DexEncodedMethod target = resolutionResult.getSingleTarget();
if (target != null
&& target.method != dexItemFactory.enumMethods.finalize
- && target.method != dexItemFactory.objectMethods.finalize) {
- return true;
+ && target.method != dexItemFactory.objectMembers.finalize) {
+ return true;
}
return false;
} else {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java
index 33c7b2a..a4917f2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java
@@ -7,10 +7,12 @@
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.function.Function;
/**
@@ -22,10 +24,13 @@
*/
public class MutableFieldOptimizationInfo extends FieldOptimizationInfo {
+ private static final int FLAGS_CANNOT_BE_KEPT = 1 << 0;
+ private static final int FLAGS_IS_DEAD = 1 << 1;
+ private static final int FLAGS_VALUE_HAS_BEEN_PROPAGATED = 1 << 2;
+
private AbstractValue abstractValue = UnknownValue.getInstance();
+ private int flags;
private int readBits = 0;
- private boolean cannotBeKept = false;
- private boolean valueHasBeenPropagated = false;
private ClassTypeLatticeElement dynamicLowerBoundType = null;
private TypeLatticeElement dynamicUpperBoundType = null;
@@ -50,8 +55,7 @@
@Override
public MutableFieldOptimizationInfo mutableCopy() {
MutableFieldOptimizationInfo copy = new MutableFieldOptimizationInfo();
- copy.cannotBeKept = cannotBeKept();
- copy.valueHasBeenPropagated = valueHasBeenPropagated();
+ copy.flags = flags;
return copy;
}
@@ -60,10 +64,14 @@
return abstractValue;
}
- public void setAbstractValue(AbstractValue abstractValue) {
+ void setAbstractValue(AbstractValue abstractValue) {
this.abstractValue = abstractValue;
}
+ public void fixupAbstractValue(AppView<AppInfoWithLiveness> appView, GraphLense lens) {
+ abstractValue = abstractValue.rewrittenWithLens(appView, lens);
+ }
+
@Override
public int getReadBits() {
return readBits;
@@ -75,11 +83,11 @@
@Override
public boolean cannotBeKept() {
- return cannotBeKept;
+ return (flags & FLAGS_CANNOT_BE_KEPT) != 0;
}
void markCannotBeKept() {
- cannotBeKept = true;
+ flags |= FLAGS_CANNOT_BE_KEPT;
}
@Override
@@ -101,12 +109,21 @@
}
@Override
+ public boolean isDead() {
+ return (flags & FLAGS_IS_DEAD) != 0;
+ }
+
+ void markAsDead() {
+ flags |= FLAGS_IS_DEAD;
+ }
+
+ @Override
public boolean valueHasBeenPropagated() {
- return valueHasBeenPropagated;
+ return (flags & FLAGS_VALUE_HAS_BEEN_PROPAGATED) != 0;
}
void markAsPropagated() {
- valueHasBeenPropagated = true;
+ flags |= FLAGS_VALUE_HAS_BEEN_PROPAGATED;
}
@Override
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 3c689a7..81d573b 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
@@ -116,6 +116,11 @@
}
@Override
+ public void markFieldAsDead(DexEncodedField field) {
+ getFieldOptimizationInfoForUpdating(field).markAsDead();
+ }
+
+ @Override
public void markFieldAsPropagated(DexEncodedField field) {
getFieldOptimizationInfoForUpdating(field).markAsPropagated();
}
@@ -139,6 +144,8 @@
@Override
public void recordFieldHasAbstractValue(
DexEncodedField field, AppView<AppInfoWithLiveness> appView, AbstractValue abstractValue) {
+ assert appView.appInfo().getFieldAccessInfoCollection().contains(field.field);
+ assert !appView.appInfo().getFieldAccessInfoCollection().get(field.field).hasReflectiveAccess();
if (appView.appInfo().mayPropagateValueFor(field.field)) {
getFieldOptimizationInfoForUpdating(field).setAbstractValue(abstractValue);
}
@@ -177,13 +184,7 @@
public synchronized void methodReturnsAbstractValue(
DexEncodedMethod method, AppView<AppInfoWithLiveness> appView, AbstractValue value) {
if (appView.appInfo().mayPropagateValueFor(method.method)) {
- UpdatableMethodOptimizationInfo info = getMethodOptimizationInfoForUpdating(method);
- assert !info.getAbstractReturnValue().isSingleValue()
- || info.getAbstractReturnValue().asSingleValue() == value
- || appView.graphLense().lookupPrototypeChanges(method.method).getRewrittenReturnInfo()
- != null
- : "return single value changed from " + info.getAbstractReturnValue() + " to " + value;
- info.markReturnsAbstractValue(value);
+ getMethodOptimizationInfoForUpdating(method).markReturnsAbstractValue(value);
}
}
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 ee5a745..66aefb4 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
@@ -34,6 +34,9 @@
public void markFieldCannotBeKept(DexEncodedField field) {}
@Override
+ public void markFieldAsDead(DexEncodedField field) {}
+
+ @Override
public void markFieldAsPropagated(DexEncodedField field) {}
@Override
@@ -120,8 +123,7 @@
}
@Override
- public void setNonNullParamOrThrow(DexEncodedMethod method, BitSet facts) {
- }
+ public void setNonNullParamOrThrow(DexEncodedMethod method, BitSet facts) {}
@Override
public void setNonNullParamOnNormalExits(DexEncodedMethod method, BitSet facts) {
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 ad3080b..b258bdf 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
@@ -36,6 +36,11 @@
}
@Override
+ public void markFieldAsDead(DexEncodedField field) {
+ field.getMutableOptimizationInfo().markAsDead();
+ }
+
+ @Override
public void markFieldAsPropagated(DexEncodedField field) {
field.getMutableOptimizationInfo().markAsPropagated();
}
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 3b2a85b..366b4d2 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
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -14,6 +15,7 @@
import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
import com.android.tools.r8.ir.optimize.info.initializer.DefaultInstanceInitializerInfo;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.BooleanUtils;
import java.util.BitSet;
import java.util.Set;
@@ -160,6 +162,20 @@
}
}
+ public UpdatableMethodOptimizationInfo fixupAbstractReturnValue(
+ AppView<AppInfoWithLiveness> appView, GraphLense lens) {
+ abstractReturnValue = abstractReturnValue.rewrittenWithLens(appView, lens);
+ return this;
+ }
+
+ public UpdatableMethodOptimizationInfo fixupInstanceInitializerInfo(
+ AppView<AppInfoWithLiveness> appView, GraphLense lens) {
+ if (instanceInitializerInfo != null) {
+ instanceInitializerInfo = instanceInitializerInfo.rewrittenWithLens(appView, lens);
+ }
+ return this;
+ }
+
private void setFlag(int flag, boolean value) {
if (value) {
setFlag(flag);
@@ -384,6 +400,8 @@
}
void markReturnsAbstractValue(AbstractValue value) {
+ assert !abstractReturnValue.isSingleValue() || abstractReturnValue.asSingleValue() == value
+ : "return single value changed from " + abstractReturnValue + " to " + value;
abstractReturnValue = value;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/EmptyInstanceFieldInitializationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/EmptyInstanceFieldInitializationInfoCollection.java
index 6b3cf4c..fd7a84f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/EmptyInstanceFieldInitializationInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/EmptyInstanceFieldInitializationInfoCollection.java
@@ -4,8 +4,11 @@
package com.android.tools.r8.ir.optimize.info.field;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.function.BiConsumer;
/**
@@ -40,4 +43,10 @@
public boolean isEmpty() {
return true;
}
+
+ @Override
+ public InstanceFieldInitializationInfoCollection rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLense lens) {
+ return this;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldArgumentInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldArgumentInitializationInfo.java
index b9e7652..757eec8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldArgumentInitializationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldArgumentInitializationInfo.java
@@ -4,6 +4,10 @@
package com.android.tools.r8.ir.optimize.info.field;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+
/**
* Used to represent that a constructor initializes an instance field on the newly created instance
* with argument number {@link #argumentIndex} from the constructor's argument list.
@@ -30,4 +34,13 @@
public InstanceFieldArgumentInitializationInfo asArgumentInitializationInfo() {
return this;
}
+
+ @Override
+ public InstanceFieldInitializationInfo rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLense lens) {
+ // We don't have the context here to determine what should happen. It is the responsibility of
+ // optimizations that change the proto of instance initializers to update the argument
+ // initialization info.
+ return this;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfo.java
index 2e9285a..37757f1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfo.java
@@ -4,7 +4,10 @@
package com.android.tools.r8.ir.optimize.info.field;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.ir.analysis.value.SingleValue;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
/**
* Information about the way a constructor initializes an instance field on the newly created
@@ -42,4 +45,7 @@
default boolean isUnknown() {
return false;
}
+
+ InstanceFieldInitializationInfo rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLense lens);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
index ed1c4d3..6bf4c74 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
@@ -4,9 +4,12 @@
package com.android.tools.r8.ir.optimize.info.field;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.function.BiConsumer;
@@ -32,6 +35,9 @@
public abstract boolean isEmpty();
+ public abstract InstanceFieldInitializationInfoCollection rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLense lens);
+
public static class Builder {
Map<DexField, InstanceFieldInitializationInfo> infos = new IdentityHashMap<>();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java
index e6f84ff..91d1aef 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java
@@ -4,8 +4,12 @@
package com.android.tools.r8.ir.optimize.info.field;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.EnumValueInfoMapCollection;
+import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.Objects;
/**
@@ -43,6 +47,30 @@
}
@Override
+ public InstanceFieldInitializationInfo rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLense lens) {
+ EnumValueInfoMapCollection unboxedEnums = appView.unboxedEnums();
+ if (dynamicLowerBoundType != null
+ && unboxedEnums.containsEnum(dynamicLowerBoundType.getClassType())) {
+ // No point in tracking the type of primitives.
+ return UnknownInstanceFieldInitializationInfo.getInstance();
+ }
+ if (dynamicUpperBoundType.isClassType()
+ && unboxedEnums.containsEnum(
+ dynamicUpperBoundType.asClassTypeLatticeElement().getClassType())) {
+ // No point in tracking the type of primitives.
+ return UnknownInstanceFieldInitializationInfo.getInstance();
+ }
+ return new InstanceFieldTypeInitializationInfo(
+ dynamicLowerBoundType != null
+ ? dynamicLowerBoundType
+ .fixupClassTypeReferences(lens::lookupType, appView.withSubtyping())
+ .asClassTypeLatticeElement()
+ : null,
+ dynamicUpperBoundType.fixupClassTypeReferences(lens::lookupType, appView.withSubtyping()));
+ }
+
+ @Override
public int hashCode() {
return Objects.hash(dynamicLowerBoundType, dynamicUpperBoundType);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java
index 5883be6..7c7a563 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java
@@ -4,9 +4,13 @@
package com.android.tools.r8.ir.optimize.info.field;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.IdentityHashMap;
import java.util.Map;
import java.util.function.BiConsumer;
@@ -47,4 +51,17 @@
public boolean isEmpty() {
return false;
}
+
+ @Override
+ public InstanceFieldInitializationInfoCollection rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLense lens) {
+ Map<DexField, InstanceFieldInitializationInfo> rewrittenInfos = new IdentityHashMap<>();
+ infos.forEach(
+ (field, info) -> {
+ DexField rewrittenField = lens.lookupField(field);
+ InstanceFieldInitializationInfo rewrittenInfo = info.rewrittenWithLens(appView, lens);
+ rewrittenInfos.put(rewrittenField, rewrittenInfo);
+ });
+ return new NonTrivialInstanceFieldInitializationInfoCollection(rewrittenInfos);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/UnknownInstanceFieldInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/UnknownInstanceFieldInitializationInfo.java
index a10f045..b2b6ce9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/UnknownInstanceFieldInitializationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/UnknownInstanceFieldInitializationInfo.java
@@ -4,6 +4,10 @@
package com.android.tools.r8.ir.optimize.info.field;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+
/**
* Represents that no information is known about the way a particular constructor initializes an
* instance field of the newly created instance.
@@ -23,4 +27,10 @@
public boolean isUnknown() {
return true;
}
+
+ @Override
+ public InstanceFieldInitializationInfo rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLense lens) {
+ return this;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java
index 20aff53..4065fb0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java
@@ -4,11 +4,14 @@
package com.android.tools.r8.ir.optimize.info.initializer;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.UnknownFieldSet;
import com.android.tools.r8.ir.optimize.info.field.EmptyInstanceFieldInitializationInfoCollection;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
public class DefaultInstanceInitializerInfo extends InstanceInitializerInfo {
@@ -22,11 +25,6 @@
}
@Override
- public boolean isDefaultInfo() {
- return true;
- }
-
- @Override
public DexMethod getParent() {
return null;
}
@@ -55,4 +53,10 @@
public boolean receiverNeverEscapesOutsideConstructorChain() {
return false;
}
+
+ @Override
+ public InstanceInitializerInfo rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLense lens) {
+ return this;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java
index b27888e..1745e7d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java
@@ -4,9 +4,12 @@
package com.android.tools.r8.ir.optimize.info.initializer;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
public abstract class InstanceInitializerInfo {
@@ -53,7 +56,6 @@
return !receiverNeverEscapesOutsideConstructorChain();
}
- public boolean isDefaultInfo() {
- return false;
- }
+ public abstract InstanceInitializerInfo rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLense lens);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
index 4e19cef..f549927 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
@@ -4,13 +4,16 @@
package com.android.tools.r8.ir.optimize.info.initializer;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.ConcreteMutableFieldSet;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.EmptyFieldSet;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.UnknownFieldSet;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
public final class NonTrivialInstanceInitializerInfo extends InstanceInitializerInfo {
@@ -79,6 +82,16 @@
return (data & RECEIVER_NEVER_ESCAPE_OUTSIDE_CONSTRUCTOR_CHAIN) != 0;
}
+ @Override
+ public NonTrivialInstanceInitializerInfo rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLense lens) {
+ return new NonTrivialInstanceInitializerInfo(
+ data,
+ fieldInitializationInfos.rewrittenWithLens(appView, lens),
+ readSet.rewrittenWithLens(appView, lens),
+ lens.getRenamedMethodSignature(parent));
+ }
+
public static class Builder {
private final InstanceFieldInitializationInfoCollection instanceFieldInitializationInfos;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index cdec947..176db10 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -13,8 +13,10 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
import com.android.tools.r8.ir.analysis.type.DestructivePhiTypeUpdater;
import com.android.tools.r8.ir.code.IRCode;
@@ -150,9 +152,14 @@
Map<InvokeVirtual, InliningInfo> invokesToInline = new IdentityHashMap<>();
for (InvokeVirtual invoke : code.<InvokeVirtual>instructions(Instruction::isInvokeVirtual)) {
- DexType holder = invoke.getInvokedMethod().holder;
+ DexMethod invokedMethod = invoke.getInvokedMethod();
+ DexType holder = invokedMethod.holder;
if (lambdaGroup.containsLambda(holder)) {
- DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method.method.holder);
+ // TODO(b/150685763): Check if we can use simpler lookup.
+ ResolutionResult resolution =
+ appView.appInfo().resolveMethod(holder, invokedMethod, false);
+ assert resolution.isSingleResolution();
+ DexEncodedMethod singleTarget = resolution.getSingleTarget();
assert singleTarget != null;
invokesToInline.put(invoke, new InliningInfo(singleTarget, singleTarget.method.holder));
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
index df6f812..f868855 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
@@ -160,7 +160,7 @@
}
@Override
- int getInstanceInitializerSize(List<DexEncodedField> captures) {
+ int getInstanceInitializerMaxSize(List<DexEncodedField> captures) {
return captures.size() + 2;
}
@@ -170,7 +170,7 @@
throws LambdaStructureError {
if (!(instructions[index] instanceof com.android.tools.r8.code.InvokeDirect
|| instructions[index] instanceof com.android.tools.r8.code.InvokeDirectRange)
- || instructions[index].getMethod() != kotlin.factory.objectMethods.constructor) {
+ || instructions[index].getMethod() != kotlin.factory.objectMembers.constructor) {
throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
}
index++;
@@ -218,7 +218,7 @@
DexMethod method,
Position callerPosition) {
super(lambdaGroupType, idField, fieldGenerator, method, callerPosition);
- this.objectInitializer = factory.objectMethods.constructor;
+ this.objectInitializer = factory.objectMembers.constructor;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
index b5f0eaf..b510e88 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
@@ -166,7 +166,7 @@
}
@Override
- int getInstanceInitializerSize(List<DexEncodedField> captures) {
+ int getInstanceInitializerMaxSize(List<DexEncodedField> captures) {
return captures.size() + 3;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java
index f500c06..3e38f67 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java
@@ -124,7 +124,7 @@
}
}
- abstract int getInstanceInitializerSize(List<DexEncodedField> captures);
+ abstract int getInstanceInitializerMaxSize(List<DexEncodedField> captures);
abstract int validateInstanceInitializerEpilogue(
com.android.tools.r8.code.Instruction[] instructions, int index) throws LambdaStructureError;
@@ -135,7 +135,7 @@
com.android.tools.r8.code.Instruction[] instructions = code.asDexCode().instructions;
int index = 0;
- if (instructions.length != getInstanceInitializerSize(captures)) {
+ if (instructions.length > getInstanceInitializerMaxSize(captures)) {
throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
}
@@ -148,15 +148,21 @@
assert index == instructions.length;
}
- private int validateInstanceInitializerParameterMapping(List<DexEncodedField> captures,
- Instruction[] instructions, int index) throws LambdaStructureError {
+ private int validateInstanceInitializerParameterMapping(
+ List<DexEncodedField> captures, Instruction[] instructions, int index)
+ throws LambdaStructureError {
+ int dead = 0;
int wideFieldsSeen = 0;
for (DexEncodedField field : captures) {
+ if (field.getOptimizationInfo().isDead()) {
+ dead++;
+ continue;
+ }
switch (field.field.type.toShorty()) {
case 'Z':
if (!(instructions[index] instanceof IputBoolean)
|| (instructions[index].getField() != field.field)
- || (((Format22c) instructions[index]).A != (index + 1 + wideFieldsSeen))) {
+ || (((Format22c) instructions[index]).A != (index + 1 + dead + wideFieldsSeen))) {
throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
}
break;
@@ -164,7 +170,7 @@
case 'B':
if (!(instructions[index] instanceof IputByte)
|| (instructions[index].getField() != field.field)
- || (((Format22c) instructions[index]).A != (index + 1 + wideFieldsSeen))) {
+ || (((Format22c) instructions[index]).A != (index + 1 + dead + wideFieldsSeen))) {
throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
}
break;
@@ -172,7 +178,7 @@
case 'S':
if (!(instructions[index] instanceof IputShort)
|| (instructions[index].getField() != field.field)
- || (((Format22c) instructions[index]).A != (index + 1 + wideFieldsSeen))) {
+ || (((Format22c) instructions[index]).A != (index + 1 + dead + wideFieldsSeen))) {
throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
}
break;
@@ -180,7 +186,7 @@
case 'C':
if (!(instructions[index] instanceof IputChar)
|| (instructions[index].getField() != field.field)
- || (((Format22c) instructions[index]).A != (index + 1 + wideFieldsSeen))) {
+ || (((Format22c) instructions[index]).A != (index + 1 + dead + wideFieldsSeen))) {
throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
}
break;
@@ -189,7 +195,7 @@
case 'F':
if (!(instructions[index] instanceof Iput)
|| (instructions[index].getField() != field.field)
- || (((Format22c) instructions[index]).A != (index + 1 + wideFieldsSeen))) {
+ || (((Format22c) instructions[index]).A != (index + 1 + dead + wideFieldsSeen))) {
throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
}
break;
@@ -198,7 +204,7 @@
case 'D':
if (!(instructions[index] instanceof IputWide)
|| (instructions[index].getField() != field.field)
- || (((Format22c) instructions[index]).A != (index + 1 + wideFieldsSeen))) {
+ || (((Format22c) instructions[index]).A != (index + 1 + dead + wideFieldsSeen))) {
throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
}
wideFieldsSeen++;
@@ -207,7 +213,7 @@
case 'L':
if (!(instructions[index] instanceof IputObject)
|| (instructions[index].getField() != field.field)
- || (((Format22c) instructions[index]).A != (index + 1 + wideFieldsSeen))) {
+ || (((Format22c) instructions[index]).A != (index + 1 + dead + wideFieldsSeen))) {
throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
}
break;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java
index d61b6ae..f3d7cc6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java
@@ -34,6 +34,7 @@
public LibraryMethodOptimizer(AppView<?> appView) {
this.appView = appView;
register(new BooleanMethodOptimizer(appView));
+ register(new ObjectMethodOptimizer(appView));
register(new ObjectsMethodOptimizer(appView));
register(new StringMethodOptimizer(appView));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectMethodOptimizer.java
new file mode 100644
index 0000000..1674baf
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectMethodOptimizer.java
@@ -0,0 +1,48 @@
+// 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.library;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Value;
+import java.util.Set;
+
+public class ObjectMethodOptimizer implements LibraryMethodModelCollection {
+
+ private final DexItemFactory dexItemFactory;
+
+ ObjectMethodOptimizer(AppView<?> appView) {
+ this.dexItemFactory = appView.dexItemFactory();
+ }
+
+ @Override
+ public DexType getType() {
+ return dexItemFactory.objectType;
+ }
+
+ @Override
+ public void optimize(
+ IRCode code,
+ InstructionListIterator instructionIterator,
+ InvokeMethod invoke,
+ DexEncodedMethod singleTarget,
+ Set<Value> affectedValues) {
+ if (singleTarget.method == dexItemFactory.objectMembers.getClass) {
+ optimizeGetClass(instructionIterator, invoke);
+ }
+ }
+
+ private void optimizeGetClass(InstructionListIterator instructionIterator, InvokeMethod invoke) {
+ if ((!invoke.hasOutValue() || !invoke.outValue().hasAnyUsers())
+ && invoke.inValues().get(0).isNeverNull()) {
+ instructionIterator.removeOrReplaceByDebugLocalRead();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
index 5af6c6f..8978657 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
@@ -591,7 +591,7 @@
assert user.isInvokeDirect();
if (ignoreSuperClassInitInvoke
&& ListUtils.lastIndexMatching(invoke.inValues(), isAliasedValue) == 0
- && methodReferenced == factory.objectMethods.constructor) {
+ && methodReferenced == factory.objectMembers.constructor) {
// If we are inside candidate constructor and analyzing usages
// of the receiver, we want to ignore invocations of superclass
// constructor which will be removed after staticizing.
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
index 422bc2f..50885dc 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
@@ -253,20 +253,18 @@
instructions.add(new CfReturn(ValueType.OBJECT));
instructions.add(nullDest);
- // This part is skipped if there is no reverse wrapper.
// if (arg instanceOf ReverseWrapper) { return ((ReverseWrapper) arg).wrapperField};
- if (reverseWrapperField != null) {
- CfLabel unwrapDest = new CfLabel();
- instructions.add(new CfLoad(ValueType.fromDexType(argType), 0));
- instructions.add(new CfInstanceOf(reverseWrapperField.holder));
- instructions.add(new CfIf(If.Type.EQ, ValueType.INT, unwrapDest));
- instructions.add(new CfLoad(ValueType.fromDexType(argType), 0));
- instructions.add(new CfCheckCast(reverseWrapperField.holder));
- instructions.add(
- new CfFieldInstruction(Opcodes.GETFIELD, reverseWrapperField, reverseWrapperField));
- instructions.add(new CfReturn(ValueType.fromDexType(reverseWrapperField.type)));
- instructions.add(unwrapDest);
- }
+ assert reverseWrapperField != null;
+ CfLabel unwrapDest = new CfLabel();
+ instructions.add(new CfLoad(ValueType.fromDexType(argType), 0));
+ instructions.add(new CfInstanceOf(reverseWrapperField.holder));
+ instructions.add(new CfIf(If.Type.EQ, ValueType.INT, unwrapDest));
+ instructions.add(new CfLoad(ValueType.fromDexType(argType), 0));
+ instructions.add(new CfCheckCast(reverseWrapperField.holder));
+ instructions.add(
+ new CfFieldInstruction(Opcodes.GETFIELD, reverseWrapperField, reverseWrapperField));
+ instructions.add(new CfReturn(ValueType.fromDexType(reverseWrapperField.type)));
+ instructions.add(unwrapDest);
// return new Wrapper(wrappedValue);
instructions.add(new CfNew(wrapperField.holder));
@@ -317,6 +315,7 @@
public static class APIConverterThrowRuntimeExceptionCfCodeProvider
extends SyntheticCfCodeProvider {
+
DexString message;
public APIConverterThrowRuntimeExceptionCfCodeProvider(
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index d458662..7822316 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -33,7 +33,6 @@
import com.android.tools.r8.graph.DexValue.DexValueMethodType;
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.graph.DexValue.DexValueType;
-import com.android.tools.r8.graph.DexValue.UnknownDexValue;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.NestMemberClassAttribute;
@@ -429,8 +428,6 @@
} else if (value instanceof DexValueType) {
DexValueType ty = (DexValueType) value;
visitor.visit(name, Type.getType(namingLens.lookupDescriptor(ty.value).toString()));
- } else if (value instanceof UnknownDexValue) {
- throw new Unreachable("writeAnnotationElement of UnknownDexValue");
} else {
visitor.visit(name, value.getBoxedValue());
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
index 941d7e2..a1cdd66 100644
--- a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
+++ b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
@@ -83,7 +83,21 @@
.put(factory.boxedNumberType, factory.createType(addKotlinPrefix("Number;")))
.put(factory.comparableType, factory.createType(addKotlinPrefix("Comparable;")))
.put(factory.enumType, factory.createType(addKotlinPrefix("Enum;")))
- // TODO(b/70169921): (Mutable) Collections?
+ // Collections
+ .put(factory.iteratorType, factory.createType(addKotlinPrefix("collections/Iterator;")))
+ .put(
+ factory.collectionType,
+ factory.createType(addKotlinPrefix("collections/Collection;")))
+ .put(factory.listType, factory.createType(addKotlinPrefix("collections/List;")))
+ .put(factory.setType, factory.createType(addKotlinPrefix("collections/Set;")))
+ .put(factory.mapType, factory.createType(addKotlinPrefix("collections/Map;")))
+ .put(
+ factory.listIteratorType,
+ factory.createType(addKotlinPrefix("collections/ListIterator;")))
+ .put(factory.iterableType, factory.createType(addKotlinPrefix("collections/Iterable;")))
+ .put(
+ factory.mapEntryType,
+ factory.createType(addKotlinPrefix("collections/Map$Entry;")))
// .../jvm/functions/FunctionN -> .../FunctionN
.putAll(
IntStream.rangeClosed(0, 22).boxed().collect(Collectors.toMap(
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
index 6085350..4a65b7c 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
@@ -61,14 +61,16 @@
List<KmType> superTypes = kmClass.getSupertypes();
superTypes.clear();
for (DexType itfType : clazz.interfaces.values) {
- KmType kmType = toRenamedKmType(itfType, appView, lens);
+ // TODO(b/129925954): Use GenericSignature.ClassSignature#superInterfaceSignatures
+ KmType kmType = toRenamedKmType(itfType, null, appView, lens);
if (kmType != null) {
superTypes.add(kmType);
}
}
assert clazz.superType != null;
if (clazz.superType != appView.dexItemFactory().objectType) {
- KmType kmTypeForSupertype = toRenamedKmType(clazz.superType, appView, lens);
+ // TODO(b/129925954): Use GenericSignature.ClassSignature#superClassSignature
+ KmType kmTypeForSupertype = toRenamedKmType(clazz.superType, null, appView, lens);
if (kmTypeForSupertype != null) {
superTypes.add(kmTypeForSupertype);
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
index b16d90a..ce685b2 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
@@ -3,12 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
+import static com.android.tools.r8.kotlin.Kotlin.NAME;
import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.toJvmFieldSignature;
import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.toJvmMethodSignature;
+import static com.android.tools.r8.utils.DescriptorUtils.descriptorToKotlinClassifier;
import static com.android.tools.r8.utils.DescriptorUtils.getBinaryNameFromDescriptor;
import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromKmType;
-import static com.android.tools.r8.kotlin.Kotlin.NAME;
-import static com.android.tools.r8.utils.DescriptorUtils.descriptorToKotlinClassifier;
import static kotlinx.metadata.FlagsKt.flagsOf;
import com.android.tools.r8.graph.AppView;
@@ -18,15 +18,24 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GenericSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.TypeSignature;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.List;
+import java.util.function.Consumer;
import kotlinx.metadata.KmConstructor;
import kotlinx.metadata.KmFunction;
import kotlinx.metadata.KmProperty;
import kotlinx.metadata.KmType;
+import kotlinx.metadata.KmTypeProjection;
import kotlinx.metadata.KmValueParameter;
+import kotlinx.metadata.KmVariance;
import kotlinx.metadata.jvm.JvmExtensionsKt;
class KotlinMetadataSynthesizer {
@@ -88,8 +97,20 @@
return descriptorToKotlinClassifier(renamedType.toDescriptorString());
}
+ // TODO(b/148654451): Canonicalization?
static KmType toRenamedKmType(
- DexType type, AppView<AppInfoWithLiveness> appView, NamingLens lens) {
+ DexType type,
+ TypeSignature typeSignature,
+ AppView<AppInfoWithLiveness> appView,
+ NamingLens lens) {
+ if (typeSignature != null
+ && typeSignature.isFieldTypeSignature()
+ && typeSignature.asFieldTypeSignature().isClassTypeSignature()
+ && typeSignature.asFieldTypeSignature().asClassTypeSignature().type() == type) {
+ return toRenamedKmType(
+ typeSignature.asFieldTypeSignature().asClassTypeSignature(), appView, lens);
+ }
+
String classifier = toRenamedClassifier(type, appView, lens);
if (classifier == null) {
return null;
@@ -98,12 +119,87 @@
// and/or why wiping out flags works for KmType but not KmFunction?!
KmType kmType = new KmType(flagsOf());
kmType.visitClass(classifier);
- // TODO(b/70169921): Need to set arguments as type parameter.
- // E.g., for kotlin/Function1<P1, R>, P1 and R are recorded inside KmType.arguments, which
- // enables kotlinc to resolve `this` type and return type of the lambda.
+ // TODO(b/70169921): Can be generalized too, like ArrayTypeSignature.Converter ?
+ // E.g., java.lang.String[] -> KmType(kotlin/Array, KmTypeProjection(OUT, kotlin/String))
+ if (type.isArrayType() && !type.isPrimitiveArrayType()) {
+ DexType elementType = type.toArrayElementType(appView.dexItemFactory());
+ KmType argumentType = toRenamedKmType(elementType, null, appView, lens);
+ kmType.getArguments().add(new KmTypeProjection(KmVariance.OUT, argumentType));
+ }
return kmType;
}
+ static KmType toRenamedKmType(
+ ClassTypeSignature classTypeSignature,
+ AppView<AppInfoWithLiveness> appView,
+ NamingLens lens) {
+ return classTypeSignature.convert(
+ new ClassTypeSignatureToRenamedKmTypeConverter(appView, lens));
+ }
+
+ static class ClassTypeSignatureToRenamedKmTypeConverter
+ implements ClassTypeSignature.Converter<KmType> {
+
+ private final AppView<AppInfoWithLiveness> appView;
+ private final NamingLens lens;
+
+ ClassTypeSignatureToRenamedKmTypeConverter(
+ AppView<AppInfoWithLiveness> appView, NamingLens lens) {
+ this.appView = appView;
+ this.lens = lens;
+ }
+
+ @Override
+ public KmType init() {
+ return new KmType(flagsOf());
+ }
+
+ @Override
+ public KmType visitType(DexType type, KmType result) {
+ if (result == null) {
+ return result;
+ }
+ String classifier = toRenamedClassifier(type, appView, lens);
+ if (classifier == null) {
+ return null;
+ }
+ result.visitClass(classifier);
+ return result;
+ }
+
+ @Override
+ public KmType visitTypeArgument(FieldTypeSignature typeArgument, KmType result) {
+ if (result == null) {
+ return result;
+ }
+ if (typeArgument.isClassTypeSignature()) {
+ KmType argumentType = typeArgument.asClassTypeSignature().convert(this);
+ result.getArguments().add(new KmTypeProjection(KmVariance.INVARIANT, argumentType));
+ }
+ // TODO(b/70169921): for TypeVariableSignature, there is KmType::visitTypeParameter.
+ return result;
+ }
+
+ @Override
+ public KmType visitInnerTypeSignature(ClassTypeSignature innerTypeSignature, KmType result) {
+ // Do nothing
+ return result;
+ }
+ }
+
+ private static KmType setRenamedKmType(
+ DexType type,
+ TypeSignature signature,
+ AppView<AppInfoWithLiveness> appView,
+ NamingLens lens,
+ Consumer<KmType> consumer) {
+ KmType renamedKmType = toRenamedKmType(type, signature, appView, lens);
+ if (renamedKmType != null) {
+ consumer.accept(renamedKmType);
+ }
+ return renamedKmType;
+ }
+
static KmConstructor toRenamedKmConstructor(
DexEncodedMethod method,
AppView<AppInfoWithLiveness> appView,
@@ -115,8 +211,9 @@
}
KmConstructor kmConstructor = new KmConstructor(method.accessFlags.getAsKotlinFlags());
JvmExtensionsKt.setSignature(kmConstructor, toJvmMethodSignature(method.method));
+ MethodTypeSignature signature = GenericSignature.Parser.toMethodTypeSignature(method, appView);
List<KmValueParameter> parameters = kmConstructor.getValueParameters();
- if (!populateKmValueParameters(parameters, method, appView, lens)) {
+ if (!populateKmValueParameters(parameters, method, signature, appView, lens)) {
return null;
}
return kmConstructor;
@@ -145,23 +242,35 @@
: method.accessFlags.getAsKotlinFlags();
KmFunction kmFunction = new KmFunction(flag, renamedMethod.name.toString());
JvmExtensionsKt.setSignature(kmFunction, toJvmMethodSignature(renamedMethod));
- KmType kmReturnType = toRenamedKmType(method.method.proto.returnType, appView, lens);
+
+ // TODO(b/129925954): Should this be integrated as part of DexDefinition instead of parsing it
+ // on demand? That may require utils to map internal encoding of signature back to
+ // corresponding backend definitions, like DexAnnotation (DEX) or Signature attribute (CF).
+ MethodTypeSignature signature = GenericSignature.Parser.toMethodTypeSignature(method, appView);
+
+ DexProto proto = method.method.proto;
+ DexType returnType = proto.returnType;
+ TypeSignature returnSignature = signature.returnType().typeSignature();
+ KmType kmReturnType = setRenamedKmType(
+ returnType, returnSignature, appView, lens, kmFunction::setReturnType);
if (kmReturnType == null) {
return null;
}
- kmFunction.setReturnType(kmReturnType);
+
if (method.isKotlinExtensionFunction()) {
- assert method.method.proto.parameters.values.length > 0
+ assert proto.parameters.values.length > 0
: method.method.toSourceString();
- KmType kmReceiverType =
- toRenamedKmType(method.method.proto.parameters.values[0], appView, lens);
+ DexType receiverType = proto.parameters.values[0];
+ TypeSignature receiverSignature = signature.getParameterTypeSignature(0);
+ KmType kmReceiverType = setRenamedKmType(
+ receiverType, receiverSignature, appView, lens, kmFunction::setReceiverParameterType);
if (kmReceiverType == null) {
return null;
}
- kmFunction.setReceiverParameterType(kmReceiverType);
}
+
List<KmValueParameter> parameters = kmFunction.getValueParameters();
- if (!populateKmValueParameters(parameters, method, appView, lens)) {
+ if (!populateKmValueParameters(parameters, method, signature, appView, lens)) {
return null;
}
return kmFunction;
@@ -170,6 +279,7 @@
private static boolean populateKmValueParameters(
List<KmValueParameter> parameters,
DexEncodedMethod method,
+ MethodTypeSignature signature,
AppView<AppInfoWithLiveness> appView,
NamingLens lens) {
boolean isExtension = method.isKotlinExtensionFunction();
@@ -179,9 +289,15 @@
String parameterName = debugLocalInfo != null ? debugLocalInfo.name.toString() : ("p" + i);
KotlinValueParameterInfo valueParameterInfo =
method.getKotlinMemberInfo().getValueParameterInfo(isExtension ? i - 1 : i);
+ TypeSignature parameterTypeSignature = signature.getParameterTypeSignature(i);
KmValueParameter kmValueParameter =
toRewrittenKmValueParameter(
- valueParameterInfo, parameterType, parameterName, appView, lens);
+ valueParameterInfo,
+ parameterType,
+ parameterTypeSignature,
+ parameterName,
+ appView,
+ lens);
if (kmValueParameter == null) {
return false;
}
@@ -193,28 +309,37 @@
private static KmValueParameter toRewrittenKmValueParameter(
KotlinValueParameterInfo valueParameterInfo,
DexType parameterType,
+ TypeSignature parameterTypeSignature,
String candidateParameterName,
AppView<AppInfoWithLiveness> appView,
NamingLens lens) {
- KmType kmParamType = toRenamedKmType(parameterType, appView, lens);
- if (kmParamType == null) {
- return null;
- }
int flag = valueParameterInfo != null ? valueParameterInfo.flag : flagsOf();
String name = valueParameterInfo != null ? valueParameterInfo.name : candidateParameterName;
KmValueParameter kmValueParameter = new KmValueParameter(flag, name);
- kmValueParameter.setType(kmParamType);
+ KmType kmParamType = setRenamedKmType(
+ parameterType, parameterTypeSignature, appView, lens, kmValueParameter::setType);
+ if (kmParamType == null) {
+ return null;
+ }
+ if (valueParameterInfo != null) {
+ JvmExtensionsKt.getAnnotations(kmParamType).addAll(valueParameterInfo.annotations);
+ }
+
if (valueParameterInfo != null && valueParameterInfo.isVararg) {
if (!parameterType.isArrayType()) {
return null;
}
- DexType elementType = parameterType.toBaseType(appView.dexItemFactory());
- KmType kmElementType = toRenamedKmType(elementType, appView, lens);
+ DexType elementType = parameterType.toArrayElementType(appView.dexItemFactory());
+ TypeSignature elementSignature =
+ parameterTypeSignature != null
+ ? parameterTypeSignature.toArrayElementTypeSignature(appView) : null;
+ KmType kmElementType = setRenamedKmType(
+ elementType, elementSignature, appView, lens, kmValueParameter::setVarargElementType);
if (kmElementType == null) {
return null;
}
- kmValueParameter.setVarargElementType(kmElementType);
}
+
return kmValueParameter;
}
@@ -363,10 +488,10 @@
if (canChangePropertyName && renamedField.name != field.field.name) {
renamedPropertyName = renamedField.name.toString();
}
- kmPropertyType = toRenamedKmType(field.field.type, appView, lens);
- if (kmPropertyType != null) {
- kmProperty.setReturnType(kmPropertyType);
- }
+ FieldTypeSignature signature =
+ GenericSignature.Parser.toFieldTypeSignature(field, appView);
+ kmPropertyType =
+ setRenamedKmType(field.field.type, signature, appView, lens, kmProperty::setReturnType);
JvmExtensionsKt.setFieldSignature(kmProperty, toJvmFieldSignature(renamedField));
}
@@ -376,26 +501,31 @@
}
if (criteria == GetterSetterCriteria.MET) {
- assert getter != null
+ assert getter != null && getter.method.proto.parameters.size() == (isExtension ? 1 : 0)
: "checkGetterCriteria: " + this.toString();
+ MethodTypeSignature signature =
+ GenericSignature.Parser.toMethodTypeSignature(getter, appView);
if (isExtension) {
- assert getter.method.proto.parameters.size() == 1
- : "checkGetterCriteria: " + this.toString();
- kmReceiverType = toRenamedKmType(getter.method.proto.parameters.values[0], appView, lens);
- if (kmReceiverType != null) {
- kmProperty.setReceiverParameterType(kmReceiverType);
- }
+ TypeSignature receiverSignature = signature.getParameterTypeSignature(0);
+ kmReceiverType =
+ setRenamedKmType(
+ getter.method.proto.parameters.values[0],
+ receiverSignature,
+ appView,
+ lens,
+ kmProperty::setReceiverParameterType);
}
+
+ DexType returnType = getter.method.proto.returnType;
+ TypeSignature returnSignature = signature.returnType().typeSignature();
if (kmPropertyType == null) {
// The property type is not set yet.
- kmPropertyType = toRenamedKmType(getter.method.proto.returnType, appView, lens);
- if (kmPropertyType != null) {
- kmProperty.setReturnType(kmPropertyType);
- }
+ kmPropertyType = setRenamedKmType(
+ returnType, returnSignature, appView, lens, kmProperty::setReturnType);
} else {
// If property type is set already (via backing field), make sure it's consistent.
KmType kmPropertyTypeFromGetter =
- toRenamedKmType(getter.method.proto.returnType, appView, lens);
+ toRenamedKmType(returnType, returnSignature, appView, lens);
if (!getDescriptorFromKmType(kmPropertyType)
.equals(getDescriptorFromKmType(kmPropertyTypeFromGetter))) {
return null;
@@ -420,40 +550,45 @@
}
if (criteria == GetterSetterCriteria.MET) {
- assert setter != null && setter.method.proto.parameters.size() >= 1
+ assert setter != null && setter.method.proto.parameters.size() == (isExtension ? 2 : 1)
: "checkSetterCriteria: " + this.toString();
+ MethodTypeSignature signature =
+ GenericSignature.Parser.toMethodTypeSignature(setter, appView);
if (isExtension) {
- assert setter.method.proto.parameters.size() == 2
- : "checkSetterCriteria: " + this.toString();
+ DexType receiverType = setter.method.proto.parameters.values[0];
+ TypeSignature receiverSignature = signature.getParameterTypeSignature(0);
if (kmReceiverType == null) {
kmReceiverType =
- toRenamedKmType(setter.method.proto.parameters.values[0], appView, lens);
- if (kmReceiverType != null) {
- kmProperty.setReceiverParameterType(kmReceiverType);
- }
+ setRenamedKmType(
+ receiverType,
+ receiverSignature,
+ appView,
+ lens,
+ kmProperty::setReceiverParameterType);
} else {
// If the receiver type for the extension property is set already (via getter),
// make sure it's consistent.
KmType kmReceiverTypeFromSetter =
- toRenamedKmType(setter.method.proto.parameters.values[0], appView, lens);
+ toRenamedKmType(receiverType, receiverSignature, appView, lens);
if (!getDescriptorFromKmType(kmReceiverType)
.equals(getDescriptorFromKmType(kmReceiverTypeFromSetter))) {
return null;
}
}
}
+
int valueIndex = isExtension ? 1 : 0;
DexType valueType = setter.method.proto.parameters.values[valueIndex];
+ TypeSignature valueSignature = signature.getParameterTypeSignature(valueIndex);
if (kmPropertyType == null) {
// The property type is not set yet.
- kmPropertyType = toRenamedKmType(valueType, appView, lens);
- if (kmPropertyType != null) {
- kmProperty.setReturnType(kmPropertyType);
- }
+ kmPropertyType =
+ setRenamedKmType(valueType, valueSignature, appView, lens, kmProperty::setReturnType);
} else {
// If property type is set already (via either backing field or getter),
// make sure it's consistent.
- KmType kmPropertyTypeFromSetter = toRenamedKmType(valueType, appView, lens);
+ KmType kmPropertyTypeFromSetter =
+ toRenamedKmType(valueType, valueSignature, appView, lens);
if (!getDescriptorFromKmType(kmPropertyType)
.equals(getDescriptorFromKmType(kmPropertyTypeFromSetter))) {
return null;
@@ -461,8 +596,8 @@
}
KotlinValueParameterInfo valueParameterInfo =
setter.getKotlinMemberInfo().getValueParameterInfo(valueIndex);
- KmValueParameter kmValueParameter =
- toRewrittenKmValueParameter(valueParameterInfo, valueType, "value", appView, lens);
+ KmValueParameter kmValueParameter = toRewrittenKmValueParameter(
+ valueParameterInfo, valueType, valueSignature, "value", appView, lens);
if (kmValueParameter != null) {
kmProperty.setSetterParameter(kmValueParameter);
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
index 95d91f8..0fa9f39 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
@@ -3,7 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import kotlinx.metadata.KmAnnotation;
+import kotlinx.metadata.KmType;
import kotlinx.metadata.KmValueParameter;
+import kotlinx.metadata.jvm.JvmExtensionsKt;
// Provides access to Kotlin information about value parameter.
class KotlinValueParameterInfo {
@@ -14,17 +19,24 @@
final int flag;
// Indicates whether the formal parameter is originally `vararg`.
final boolean isVararg;
+ // TODO(b/70169921): Should we treat them as normal annotations? E.g., shrinking and renaming?
+ // Annotations on the type of value parameter.
+ final List<KmAnnotation> annotations;
- private KotlinValueParameterInfo(String name, int flag, boolean isVararg) {
+ private KotlinValueParameterInfo(
+ String name, int flag, boolean isVararg, List<KmAnnotation> annotations) {
this.name = name;
this.flag = flag;
this.isVararg = isVararg;
+ this.annotations = annotations;
}
static KotlinValueParameterInfo fromKmValueParameter(KmValueParameter kmValueParameter) {
+ KmType kmType = kmValueParameter.getType();
return new KotlinValueParameterInfo(
kmValueParameter.getName(),
kmValueParameter.getFlags(),
- kmValueParameter.getVarargElementType() != null);
+ kmValueParameter.getVarargElementType() != null,
+ kmType != null ? JvmExtensionsKt.getAnnotations(kmType) : ImmutableList.of());
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/ApplyMappingError.java b/src/main/java/com/android/tools/r8/naming/ApplyMappingError.java
index 7b76acb..2ef940e 100644
--- a/src/main/java/com/android/tools/r8/naming/ApplyMappingError.java
+++ b/src/main/java/com/android/tools/r8/naming/ApplyMappingError.java
@@ -14,8 +14,9 @@
"'%s' cannot be mapped to '%s' because it is in conflict with an existing ";
private static final String EXISTING_MESSAGE_END =
". This usually happens when compiling a test application against a source application and "
- + "having short generic names in the test application. Try giving '%s' a more specific "
- + "name or add a keep rule to keep '%s'.";
+ + "there are used classes in the test that was not given a -keep rule when compiling the "
+ + "app. Try either renaming '%s' such that it will not collide or add a keep rule to "
+ + "keep '%s'.";
protected static final String EXISTING_CLASS_MESSAGE =
EXISTING_MESSAGE_START + "class with the same name" + EXISTING_MESSAGE_END;
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index 2e68b4f..9f72270 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -296,7 +296,9 @@
parentState = getOrAllocateMethodNamingStates(holder.superType);
}
}
- MethodReservationState<?> reservationState = reservationStates.get(type);
+ // There can be gaps in the reservation states if a library class extends a program class.
+ // See b/150325706 for more information.
+ MethodReservationState<?> reservationState = findReservationStateInHierarchy(type);
assert reservationState != null : "Could not find reservation state for " + type.toString();
namingState = parentState.createChild(reservationState);
namingStates.put(type, namingState);
@@ -304,6 +306,21 @@
return namingState;
}
+ private MethodReservationState<?> findReservationStateInHierarchy(DexType type) {
+ MethodReservationState<?> reservationState = reservationStates.get(type);
+ if (reservationState != null) {
+ return reservationState;
+ }
+ // If we cannot find the reservation state, which is a result from a library class extending
+ // a program class. The gap is tracked in the frontier state.
+ assert frontiers.containsKey(type);
+ DexType frontierType = frontiers.get(type);
+ reservationState = reservationStates.get(frontierType);
+ assert reservationState != null
+ : "Could not find reservation state for frontier type " + frontierType.toString();
+ return reservationState;
+ }
+
// Shuffles the given methods if assertions are enabled and deterministic debugging is disabled.
// Used to ensure that the generated output is deterministic.
private static Iterable<DexEncodedMethod> shuffleMethods(
diff --git a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
index 734c0b0..609feaa 100644
--- a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
@@ -34,7 +34,7 @@
class MinifiedRenaming extends NamingLens {
- private final AppView<?> appView;
+ final AppView<?> appView;
private final Map<String, String> packageRenaming;
private final Map<DexItem, DexString> renaming = new IdentityHashMap<>();
diff --git a/src/main/java/com/android/tools/r8/naming/NamingLens.java b/src/main/java/com/android/tools/r8/naming/NamingLens.java
index f1a7d84..d1c195f 100644
--- a/src/main/java/com/android/tools/r8/naming/NamingLens.java
+++ b/src/main/java/com/android/tools/r8/naming/NamingLens.java
@@ -41,6 +41,8 @@
*/
public abstract class NamingLens {
+ protected boolean isSortingBeforeWriting;
+
public abstract String lookupPackageName(String packageName);
public abstract DexString lookupDescriptor(DexType type);
@@ -163,6 +165,10 @@
return true;
}
+ public void setIsSortingBeforeWriting(boolean isSorting) {
+ isSortingBeforeWriting = isSorting;
+ }
+
private static class IdentityLens extends NamingLens {
private IdentityLens() {
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
index 471d66f..9f32739 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -36,6 +36,8 @@
import com.google.common.collect.Maps;
import java.util.ArrayDeque;
import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
@@ -87,6 +89,7 @@
public NamingLens run(ExecutorService executorService, Timing timing) throws ExecutionException {
ArrayDeque<Map<DexReference, MemberNaming>> nonPrivateMembers = new ArrayDeque<>();
+ Set<DexReference> notMappedReferences = new HashSet<>();
timing.begin("MappingInterfaces");
Set<DexClass> interfaces = new TreeSet<>((a, b) -> a.type.slowCompareTo(b.type));
@@ -96,7 +99,7 @@
if (dexClass.isInterface()) {
// Only visit top level interfaces because computeMapping will visit the hierarchy.
if (dexClass.interfaces.isEmpty()) {
- computeMapping(dexClass.type, nonPrivateMembers);
+ computeMapping(dexClass.type, nonPrivateMembers, notMappedReferences);
}
interfaces.add(dexClass);
}
@@ -113,7 +116,7 @@
subType -> {
DexClass dexClass = appView.definitionFor(subType);
if (dexClass != null && !dexClass.isInterface()) {
- computeMapping(subType, nonPrivateMembers);
+ computeMapping(subType, nonPrivateMembers, notMappedReferences);
}
});
assert nonPrivateMembers.isEmpty();
@@ -158,7 +161,9 @@
appView.options().reporter.failIfPendingErrors();
- NamingLens lens = new MinifiedRenaming(appView, classRenaming, methodRenaming, fieldRenaming);
+ NamingLens lens =
+ new ProguardMapMinifiedRenaming(
+ appView, classRenaming, methodRenaming, fieldRenaming, notMappedReferences);
timing.begin("MinifyIdentifiers");
new IdentifierMinifier(appView, lens).run(executorService);
@@ -171,7 +176,10 @@
return lens;
}
- private void computeMapping(DexType type, Deque<Map<DexReference, MemberNaming>> buildUpNames) {
+ private void computeMapping(
+ DexType type,
+ Deque<Map<DexReference, MemberNaming>> buildUpNames,
+ Set<DexReference> notMappedReferences) {
ClassNamingForMapApplier classNaming = seedMapper.getClassNaming(type);
DexClass dexClass = appView.definitionFor(type);
@@ -189,16 +197,15 @@
if (dexClass != null) {
KotlinMetadataRewriter.removeKotlinMetadataFromRenamedClass(appView, dexClass);
}
-
classNaming.forAllMemberNaming(
memberNaming -> addMemberNamings(type, memberNaming, nonPrivateMembers, false));
} else {
// We have to ensure we do not rename to an existing member, that cannot be renamed.
if (dexClass == null || !appView.options().isMinifying()) {
- checkAndAddMappedNames(type, type.descriptor, Position.UNKNOWN);
+ notMappedReferences.add(type);
} else if (appView.options().isMinifying()
&& appView.rootSet().mayNotBeMinified(type, appView)) {
- checkAndAddMappedNames(type, type.descriptor, Position.UNKNOWN);
+ notMappedReferences.add(type);
}
}
@@ -247,12 +254,14 @@
buildUpNames.addLast(nonPrivateMembers);
appView
.appInfo()
- .forAllImmediateExtendsSubtypes(type, subType -> computeMapping(subType, buildUpNames));
+ .forAllImmediateExtendsSubtypes(
+ type, subType -> computeMapping(subType, buildUpNames, notMappedReferences));
buildUpNames.removeLast();
} else {
appView
.appInfo()
- .forAllImmediateExtendsSubtypes(type, subType -> computeMapping(subType, buildUpNames));
+ .forAllImmediateExtendsSubtypes(
+ type, subType -> computeMapping(subType, buildUpNames, notMappedReferences));
}
}
@@ -553,4 +562,52 @@
// reporter.error(applyMappingError);
}
}
+
+ public static class ProguardMapMinifiedRenaming extends MinifiedRenaming {
+
+ private final Set<DexReference> unmappedReferences;
+ private final Map<DexString, DexType> classRenamingsMappingToDifferentName;
+
+ ProguardMapMinifiedRenaming(
+ AppView<?> appView,
+ ClassRenaming classRenaming,
+ MethodRenaming methodRenaming,
+ FieldRenaming fieldRenaming,
+ Set<DexReference> unmappedReferences) {
+ super(appView, classRenaming, methodRenaming, fieldRenaming);
+ this.unmappedReferences = unmappedReferences;
+ classRenamingsMappingToDifferentName = new HashMap<>();
+ classRenaming.classRenaming.forEach(
+ (type, dexString) -> {
+ if (type.descriptor != dexString) {
+ classRenamingsMappingToDifferentName.put(dexString, type);
+ }
+ });
+ }
+
+ @Override
+ public DexString lookupDescriptor(DexType type) {
+ if (!isSortingBeforeWriting) {
+ checkForUseOfNotMappedReference(type);
+ }
+ return super.lookupDescriptor(type);
+ }
+
+ private void checkForUseOfNotMappedReference(DexType type) {
+ if (unmappedReferences.contains(type)
+ && classRenamingsMappingToDifferentName.containsKey(type.descriptor)) {
+ // Type is an unmapped reference and there is a mapping from some other type to this one.
+ // We are emitting a warning here, since this will generally be undesired behavior.
+ DexType mappedType = classRenamingsMappingToDifferentName.get(type.descriptor);
+ appView
+ .options()
+ .reporter
+ .error(
+ ApplyMappingError.mapToExistingClass(
+ mappedType.toString(), type.toSourceString(), Position.UNKNOWN));
+ // Remove the type to ensure us only reporting the error once.
+ unmappedReferences.remove(type);
+ }
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
index 317cb3b..b319c6d 100644
--- a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.ir.optimize.MethodPoolCollection;
import com.android.tools.r8.optimize.PublicizerLense.PublicizedLenseBuilder;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.Timing;
import java.util.LinkedHashSet;
import java.util.Set;
@@ -110,6 +111,8 @@
return false;
}
if (!accessFlags.isPrivate() || appView.dexItemFactory().isConstructor(encodedMethod.method)) {
+ // TODO(b/150589374): This should check for dispatch targets or just abandon in
+ // package-private.
accessFlags.promoteToPublic();
return false;
}
@@ -142,6 +145,7 @@
// Although the current method became public, it surely has the single virtual target.
encodedMethod.method.setSingleVirtualMethodCache(
encodedMethod.method.holder, encodedMethod);
+ encodedMethod.setLibraryMethodOverride(OptionalBool.FALSE);
return true;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index a1277ef..44367e0 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -5,11 +5,13 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.graph.GraphLense.rewriteReferenceKeys;
+import static com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult.isOverriding;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexClasspathClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -18,7 +20,6 @@
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.DexTypeList;
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.EnumValueInfoMapCollection;
import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap;
@@ -28,10 +29,14 @@
import com.android.tools.r8.graph.FieldAccessInfoImpl;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
+import com.android.tools.r8.graph.InstantiatedSubTypeInfo;
+import com.android.tools.r8.graph.LookupResult.LookupResultSuccess;
+import com.android.tools.r8.graph.LookupTarget;
import com.android.tools.r8.graph.ObjectAllocationInfoCollection;
import com.android.tools.r8.graph.ObjectAllocationInfoCollectionImpl;
import com.android.tools.r8.graph.PresortedComparable;
import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement;
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
@@ -39,11 +44,10 @@
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.PredicateSet;
import com.android.tools.r8.utils.SetUtils;
-import com.google.common.collect.ImmutableList;
+import com.android.tools.r8.utils.WorkList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.ImmutableSortedSet.Builder;
-import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
@@ -64,7 +68,7 @@
import java.util.stream.Collectors;
/** Encapsulates liveness and reachability information for an application. */
-public class AppInfoWithLiveness extends AppInfoWithSubtyping {
+public class AppInfoWithLiveness extends AppInfoWithSubtyping implements InstantiatedSubTypeInfo {
/** Set of types that are mentioned in the program, but for which no definition exists. */
private final Set<DexType> missingTypes;
@@ -695,6 +699,11 @@
return result;
}
+ public EnumValueInfoMapCollection getEnumValueInfoMapCollection() {
+ assert checkIfObsolete();
+ return enumValueInfoMaps;
+ }
+
public EnumValueInfoMap getEnumValueInfoMap(DexType enumType) {
assert checkIfObsolete();
return enumValueInfoMaps.getEnumValueInfoMap(enumType);
@@ -850,7 +859,9 @@
public boolean mayPropagateValueFor(DexReference reference) {
assert checkIfObsolete();
- return !isPinned(reference) && !neverPropagateValue.contains(reference);
+ return options().enableValuePropagation
+ && !isPinned(reference)
+ && !neverPropagateValue.contains(reference);
}
private boolean isLibraryOrClasspathField(DexEncodedField field) {
@@ -883,7 +894,7 @@
}
@Override
- protected boolean hasAnyInstantiatedLambdas(DexProgramClass clazz) {
+ public boolean hasAnyInstantiatedLambdas(DexProgramClass clazz) {
assert checkIfObsolete();
return instantiatedLambdas.contains(clazz.type);
}
@@ -1076,7 +1087,10 @@
}
public DexEncodedMethod lookupSingleTarget(
- Type type, DexMethod target, DexType invocationContext) {
+ Type type,
+ DexMethod target,
+ DexType invocationContext,
+ LibraryModeledPredicate modeledPredicate) {
assert checkIfObsolete();
DexType holder = target.holder;
if (!holder.isClassType()) {
@@ -1084,9 +1098,9 @@
}
switch (type) {
case VIRTUAL:
- return lookupSingleVirtualTarget(target, invocationContext);
+ return lookupSingleVirtualTarget(target, invocationContext, false, modeledPredicate);
case INTERFACE:
- return lookupSingleInterfaceTarget(target, invocationContext);
+ return lookupSingleVirtualTarget(target, invocationContext, true, modeledPredicate);
case DIRECT:
return lookupDirectTarget(target, invocationContext);
case STATIC:
@@ -1124,318 +1138,129 @@
}
/** For mapping invoke virtual instruction to single target method. */
- public DexEncodedMethod lookupSingleVirtualTarget(DexMethod method, DexType invocationContext) {
+ public DexEncodedMethod lookupSingleVirtualTarget(
+ DexMethod method, DexType invocationContext, boolean isInterface) {
assert checkIfObsolete();
- return lookupSingleVirtualTarget(method, invocationContext, method.holder, null);
+ return lookupSingleVirtualTarget(
+ method, invocationContext, isInterface, type -> false, method.holder, null);
+ }
+
+ /** For mapping invoke virtual instruction to single target method. */
+ public DexEncodedMethod lookupSingleVirtualTarget(
+ DexMethod method,
+ DexType invocationContext,
+ boolean isInterface,
+ LibraryModeledPredicate modeledPredicate) {
+ assert checkIfObsolete();
+ return lookupSingleVirtualTarget(
+ method, invocationContext, isInterface, modeledPredicate, method.holder, null);
}
public DexEncodedMethod lookupSingleVirtualTarget(
DexMethod method,
DexType invocationContext,
+ boolean isInterface,
+ LibraryModeledPredicate modeledPredicate,
DexType refinedReceiverType,
ClassTypeLatticeElement receiverLowerBoundType) {
assert checkIfObsolete();
- // TODO: replace invocationContext by a DexProgramClass typed formal.
+ assert refinedReceiverType != null;
+
DexProgramClass invocationClass = asProgramClassOrNull(definitionFor(invocationContext));
assert invocationClass != null;
- ResolutionResult resolutionResult = resolveMethodOnClass(method.holder, method);
- if (!resolutionResult.isAccessibleForVirtualDispatchFrom(invocationClass, this)) {
+ if (!refinedReceiverType.isClassType()) {
+ // The refined receiver is not of class type and we will not be able to find a single target
+ // (it is either primitive or array).
return null;
}
-
- DexEncodedMethod topTarget = resolutionResult.getSingleTarget();
- if (topTarget == null) {
- // A null target represents a valid target without a known defintion, ie, array clone().
+ DexClass refinedReceiverClass = definitionFor(refinedReceiverType);
+ if (refinedReceiverClass == null) {
+ // The refined receiver is not defined in the program and we cannot determine the target.
return null;
}
-
- // If the target is a private method, then the invocation is a direct access to a nest member.
- if (topTarget.isPrivateMethod()) {
- return topTarget;
+ SingleResolutionResult resolution =
+ resolveMethod(method.holder, method, isInterface).asSingleResolution();
+ if (resolution == null
+ || !resolution.isAccessibleForVirtualDispatchFrom(invocationClass, this)) {
+ return null;
}
-
+ // If the method is modeled, return the resolution.
+ if (modeledPredicate.isModeled(resolution.getResolvedHolder().type)) {
+ if (resolution.getResolvedHolder().isFinal()
+ || (resolution.getResolvedMethod().isFinal()
+ && resolution.getResolvedMethod().accessFlags.isPublic())) {
+ return resolution.getResolvedMethod();
+ }
+ }
// If the lower-bound on the receiver type is the same as the upper-bound, then we have exact
// runtime type information. In this case, the invoke will dispatch to the resolution result
// from the runtime type of the receiver.
- if (receiverLowerBoundType != null) {
- if (receiverLowerBoundType.getClassType() == refinedReceiverType) {
- if (resolutionResult.isSingleResolution() && resolutionResult.isVirtualTarget()) {
- ResolutionResult refinedResolutionResult = resolveMethod(refinedReceiverType, method);
- if (refinedResolutionResult.isSingleResolution()
- && refinedResolutionResult.isVirtualTarget()) {
- return validateSingleVirtualTarget(
- refinedResolutionResult.getSingleTarget(), resolutionResult.getSingleTarget());
- }
+ if (receiverLowerBoundType != null
+ && receiverLowerBoundType.getClassType() == refinedReceiverType) {
+ if (refinedReceiverClass.isProgramClass()) {
+ DexClassAndMethod clazzAndMethod =
+ resolution.lookupVirtualDispatchTarget(refinedReceiverClass.asProgramClass(), this);
+ if (clazzAndMethod == null || isPinned(clazzAndMethod.getMethod().method)) {
+ // TODO(b/150640456): We should maybe only consider program methods.
+ return null;
+ }
+ return clazzAndMethod.getMethod();
+ } else {
+ // TODO(b/150640456): We should maybe only consider program methods.
+ // If we resolved to a method on the refined receiver in the library, then we report the
+ // method as a single target as well. This is a bit iffy since the library could change
+ // implementation, but we use this for library modelling.
+ DexEncodedMethod targetOnReceiver = refinedReceiverClass.lookupVirtualMethod(method);
+ if (targetOnReceiver != null
+ && isOverriding(resolution.getResolvedMethod(), targetOnReceiver)) {
+ return targetOnReceiver;
}
return null;
- } else {
- // We should never hit the case at the moment, but if we start tracking more precise lower-
- // bound type information, we should handle this case as well.
}
}
+ if (refinedReceiverClass.isNotProgramClass()) {
+ // The refined receiver is not defined in the program and we cannot determine the target.
+ return null;
+ }
+ DexClass resolvedHolder = resolution.getResolvedHolder();
+ // TODO(b/148769279): Disable lookup single target on lambda's for now.
+ if (resolvedHolder.isInterface()
+ && resolvedHolder.isProgramClass()
+ && hasAnyInstantiatedLambdas(resolvedHolder.asProgramClass())) {
+ return null;
+ }
- // This implements the logic from
- // https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-6.html#jvms-6.5.invokevirtual
- assert method != null;
- assert isSubtype(refinedReceiverType, method.holder);
- if (method.holder.isArrayType()) {
- return null;
- }
- DexClass holder = definitionFor(method.holder);
- if (holder == null || holder.isNotProgramClass()) {
- return null;
- }
- assert !holder.isInterface();
- boolean refinedReceiverIsStrictSubType = refinedReceiverType != method.holder;
- DexProgramClass refinedHolder =
- (refinedReceiverIsStrictSubType ? definitionFor(refinedReceiverType) : holder)
- .asProgramClass();
- if (refinedHolder == null) {
- return null;
- }
- assert !refinedHolder.isInterface();
if (method.isSingleVirtualMethodCached(refinedReceiverType)) {
return method.getSingleVirtualMethodCache(refinedReceiverType);
}
- // First get the target for the holder type.
- ResolutionResult topMethod = resolveMethodOnClass(holder, method);
- // We might hit none or multiple targets. Both make this fail at runtime.
- if (!topMethod.isSingleResolution() || !topMethod.isVirtualTarget()) {
- method.setSingleVirtualMethodCache(refinedReceiverType, null);
- return null;
- }
- // Now, resolve the target with the refined receiver type.
- ResolutionResult refinedResolutionResult =
- refinedReceiverIsStrictSubType ? resolveMethodOnClass(refinedHolder, method) : topMethod;
- DexEncodedMethod topSingleTarget = refinedResolutionResult.getSingleTarget();
- DexClass topHolder = definitionFor(topSingleTarget.method.holder);
- // We need to know whether the top method is from an interface, as that would allow it to be
- // shadowed by a default method from an interface further down.
- boolean topIsFromInterface = topHolder.isInterface();
- // Now look at all subtypes and search for overrides.
- DexEncodedMethod result =
- validateSingleVirtualTarget(
- findSingleTargetFromSubtypes(
- refinedHolder,
- method,
- topSingleTarget,
- !refinedHolder.accessFlags.isAbstract(),
- topIsFromInterface),
- topMethod.getSingleTarget());
- assert result != DexEncodedMethod.SENTINEL;
- method.setSingleVirtualMethodCache(refinedReceiverType, result);
- return result;
- }
- /**
- * Computes which methods overriding <code>method</code> are visible for the subtypes of type.
- *
- * <p><code>candidate</code> is the definition further up the hierarchy that is visible from the
- * subtypes. If <code>candidateIsReachable</code> is true, the provided candidate is already a
- * target for a type further up the chain, so anything found in subtypes is a conflict. If it is
- * false, the target exists but is not reachable from a live type.
- *
- * <p>Returns <code>null</code> if the given type has no subtypes or all subtypes are abstract.
- * Returns {@link DexEncodedMethod#SENTINEL} if multiple live overrides were found. Returns the
- * single virtual target otherwise.
- */
- private DexEncodedMethod findSingleTargetFromSubtypes(
- DexProgramClass clazz,
- DexMethod method,
- DexEncodedMethod candidate,
- boolean candidateIsReachable,
- boolean checkForInterfaceConflicts) {
- // If the invoke could target a method in a class that is not visible to R8, then give up.
- if (canVirtualMethodBeImplementedInExtraSubclass(clazz, method)) {
- return DexEncodedMethod.SENTINEL;
- }
- // If the candidate is reachable, we already have a previous result.
- DexEncodedMethod result = candidateIsReachable ? candidate : null;
- for (DexType subtype : allImmediateExtendsSubtypes(clazz.type)) {
- DexProgramClass subclass = asProgramClassOrNull(definitionFor(subtype));
- if (subclass == null) {
- // Can't guarantee a single target.
- return DexEncodedMethod.SENTINEL;
- }
- DexEncodedMethod target = subclass.lookupVirtualMethod(method);
- if (target != null && !target.isPrivateMethod()) {
- // We found a method on this class. If this class is not abstract it is a runtime
- // reachable override and hence a conflict.
- if (!subclass.accessFlags.isAbstract()) {
- if (result != null && result != target) {
- // We found a new target on this subtype that does not match the previous one. Fail.
- return DexEncodedMethod.SENTINEL;
- }
- // Add the first or matching target.
- result = target;
- }
- }
- if (checkForInterfaceConflicts) {
- // We have to check whether there are any default methods in implemented interfaces.
- if (interfacesMayHaveDefaultFor(subclass.interfaces, method)) {
- return DexEncodedMethod.SENTINEL;
- }
- }
- DexEncodedMethod newCandidate = target == null ? candidate : target;
- // If we have a new target and did not fail, it is not an override of a reachable method.
- // Whether the target is actually reachable depends on whether this class is abstract.
- // If we did not find a new target, the candidate is reachable if it was before, or if this
- // class is not abstract.
- boolean newCandidateIsReachable =
- !subclass.accessFlags.isAbstract() || ((target == null) && candidateIsReachable);
- DexEncodedMethod subtypeTarget =
- findSingleTargetFromSubtypes(
- subclass, method, newCandidate, newCandidateIsReachable, checkForInterfaceConflicts);
- if (subtypeTarget != null) {
- // We found a target in the subclasses. If we already have a different result, fail.
- if (result != null && result != subtypeTarget) {
- return DexEncodedMethod.SENTINEL;
- }
- // Remember this new result.
- result = subtypeTarget;
- }
- }
- return result;
- }
-
- /**
- * Checks whether any interface in the given list or their super interfaces implement a default
- * method.
- *
- * <p>This method is conservative for unknown interfaces and interfaces from the library.
- */
- private boolean interfacesMayHaveDefaultFor(DexTypeList ifaces, DexMethod method) {
- for (DexType iface : ifaces.values) {
- DexClass clazz = definitionFor(iface);
- if (clazz == null || clazz.isNotProgramClass()) {
- return true;
- }
- DexEncodedMethod candidate = clazz.lookupMethod(method);
- if (candidate != null && !candidate.accessFlags.isAbstract()) {
- return true;
- }
- if (interfacesMayHaveDefaultFor(clazz.interfaces, method)) {
- return true;
- }
- }
- return false;
- }
-
- public DexEncodedMethod lookupSingleInterfaceTarget(DexMethod method, DexType invocationContext) {
- assert checkIfObsolete();
- return lookupSingleInterfaceTarget(method, invocationContext, method.holder, null);
- }
-
- public DexEncodedMethod lookupSingleInterfaceTarget(
- DexMethod method,
- DexType invocationContext,
- DexType refinedReceiverType,
- ClassTypeLatticeElement receiverLowerBoundType) {
- assert checkIfObsolete();
- // Replace DexType invocationContext by DexProgramClass throughout.
- DexProgramClass invocationClass = asProgramClassOrNull(definitionFor(invocationContext));
- assert invocationClass != null;
-
- // If the lower-bound on the receiver type is the same as the upper-bound, then we have exact
- // runtime type information. In this case, the invoke will dispatch to the resolution result
- // from the runtime type of the receiver.
+ DexProgramClass refinedLowerBound = null;
if (receiverLowerBoundType != null) {
- if (receiverLowerBoundType.getClassType() == refinedReceiverType) {
- ResolutionResult resolutionResult = resolveMethod(method.holder, method, true);
- if (resolutionResult.isSingleResolution() && resolutionResult.isVirtualTarget()) {
- ResolutionResult refinedResolutionResult = resolveMethod(refinedReceiverType, method);
- if (refinedResolutionResult.isSingleResolution()
- && refinedResolutionResult.isVirtualTarget()) {
- return validateSingleVirtualTarget(
- refinedResolutionResult.getSingleTarget(), resolutionResult.getSingleTarget());
- }
- }
- return null;
- } else {
- // We should never hit the case at the moment, but if we start tracking more precise lower-
- // bound type information, we should handle this case as well.
+ assert receiverLowerBoundType.isClassType();
+ DexClass refinedLowerBoundClass = definitionFor(receiverLowerBoundType.getClassType());
+ if (refinedLowerBoundClass != null) {
+ refinedLowerBound = refinedLowerBoundClass.asProgramClass();
}
}
- DexProgramClass holder = asProgramClassOrNull(definitionFor(method.holder));
- if (holder == null || !holder.accessFlags.isInterface()) {
- return null;
- }
- // First check that there is a visible and valid target for this invoke-interface to hit.
- // If there is none, this will fail at runtime.
- ResolutionResult topResolution = resolveMethodOnInterface(holder, method);
- if (!topResolution.isAccessibleForVirtualDispatchFrom(invocationClass, this)) {
+ LookupResultSuccess lookupResult =
+ resolution
+ .lookupVirtualDispatchTargets(
+ invocationClass, this, refinedReceiverClass.asProgramClass(), refinedLowerBound)
+ .asLookupResultSuccess();
+
+ if (lookupResult == null || lookupResult.isIncomplete()) {
return null;
}
- DexEncodedMethod topTarget = topResolution.getSingleTarget();
- if (topTarget == null) {
- // An null target represents a valid target with no known defintion, eg, array clone().
- return null;
+ LookupTarget singleTarget = lookupResult.getSingleLookupTarget();
+ DexEncodedMethod singleMethodTarget = null;
+ if (singleTarget != null && singleTarget.isMethodTarget()) {
+ singleMethodTarget = singleTarget.asMethodTarget().getMethod();
}
-
- // If the target is a private method, then the invocation is a direct access to a nest member.
- if (topTarget.isPrivateMethod()) {
- return topTarget;
- }
-
- // If the invoke could target a method in a class that is not visible to R8, then give up.
- if (canVirtualMethodBeImplementedInExtraSubclass(holder, method)) {
- return null;
- }
-
- DexProgramClass refinedReceiverClass = definitionFor(refinedReceiverType).asProgramClass();
- if (refinedReceiverClass == null) {
- return null;
- }
-
- // For functional interfaces that are instantiated by lambdas, we may not have synthesized all
- // the lambda classes yet, and therefore the set of subtypes for the holder may still be
- // incomplete.
- if (hasAnyInstantiatedLambdas(refinedReceiverClass)) {
- return null;
- }
-
- Iterable<DexType> subtypesToExplore =
- isInstantiatedDirectly(refinedReceiverClass)
- ? Iterables.concat(ImmutableList.of(refinedReceiverType), subtypes(refinedReceiverType))
- : subtypes(refinedReceiverType);
-
- // The loop will ignore uninstantiated classes as they will not be a target at runtime.
- DexEncodedMethod result = null;
- for (DexType type : subtypesToExplore) {
- DexProgramClass clazz = asProgramClassOrNull(definitionFor(type));
- if (clazz == null) {
- // Cannot guarantee a single target.
- return null;
- }
-
- // If the invoke could target a method in a class that is not visible to R8, then give up.
- if (canVirtualMethodBeImplementedInExtraSubclass(clazz, method)) {
- return null;
- }
-
- if (!isInstantiatedDirectly(clazz)) {
- // This is not a possible receiver at runtime.
- continue;
- }
-
- // TODO(b/145344105): Abstract classes should never be considered instantiated.
- // assert (!clazz.isAbstract() && !clazz.isInterface()) || clazz.isAnnotation();
-
- DexEncodedMethod resolutionResult = resolveMethod(clazz, method).getSingleTarget();
- if (resolutionResult == null || isInvalidSingleVirtualTarget(resolutionResult, topTarget)) {
- // This will fail at runtime.
- return null;
- }
- if (result != null && result != resolutionResult) {
- return null;
- }
- result = resolutionResult;
- }
- assert result == null || !isInvalidSingleVirtualTarget(result, topTarget);
- return result == null || !result.isVirtualMethod() ? null : result;
+ method.setSingleVirtualMethodCache(refinedReceiverType, singleMethodTarget);
+ return singleMethodTarget;
}
public AppInfoWithLiveness withSwitchMaps(Map<DexField, Int2ReferenceMap<DexField>> switchMaps) {
@@ -1523,4 +1348,44 @@
}
}
}
+
+ @Override
+ public void forEachInstantiatedSubType(
+ DexType type,
+ Consumer<DexProgramClass> subTypeConsumer,
+ Consumer<LambdaDescriptor> callSiteConsumer) {
+ WorkList<DexType> workList = WorkList.newIdentityWorkList();
+ workList.addIfNotSeen(type);
+ while (workList.hasNext()) {
+ DexType subType = workList.next();
+ DexProgramClass clazz = definitionForProgramType(subType);
+ workList.addIfNotSeen(allImmediateSubtypes(subType));
+ if (clazz == null) {
+ continue;
+ }
+ if (isInstantiatedDirectly(clazz)
+ || isPinned(clazz.type)
+ || hasAnyInstantiatedLambdas(clazz)) {
+ subTypeConsumer.accept(clazz);
+ }
+ }
+ }
+
+ public boolean isPinnedNotProgramOrLibraryOverride(DexReference reference) {
+ if (isPinned(reference)) {
+ return true;
+ }
+ if (reference.isDexMethod()) {
+ DexEncodedMethod method = definitionFor(reference.asDexMethod());
+ return method == null
+ || !method.isProgramMethod(this)
+ || method.isLibraryMethodOverride().isPossiblyTrue();
+ } else {
+ assert reference.isDexType();
+ DexClass clazz = definitionFor(reference.asDexType());
+ return clazz == null
+ || clazz.isNotProgramClass()
+ || hasAnyInstantiatedLambdas(clazz.asProgramClass());
+ }
+ }
}
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 dfcee69..f35fb94 100644
--- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -65,13 +65,23 @@
}
@Override
+ public boolean registerInstanceFieldRead(DexField field) {
+ return enqueuer.traceInstanceFieldRead(field, context.getMethod());
+ }
+
+ @Override
+ public boolean registerInstanceFieldReadFromMethodHandle(DexField field) {
+ return enqueuer.traceInstanceFieldReadFromMethodHandle(field, context.getMethod());
+ }
+
+ @Override
public boolean registerInstanceFieldWrite(DexField field) {
return enqueuer.traceInstanceFieldWrite(field, context.getMethod());
}
@Override
- public boolean registerInstanceFieldRead(DexField field) {
- return enqueuer.traceInstanceFieldRead(field, context.getMethod());
+ public boolean registerInstanceFieldWriteFromMethodHandle(DexField field) {
+ return enqueuer.traceInstanceFieldWriteFromMethodHandle(field, context.getMethod());
}
@Override
@@ -85,11 +95,21 @@
}
@Override
+ public boolean registerStaticFieldReadFromMethodHandle(DexField field) {
+ return enqueuer.traceStaticFieldReadFromMethodHandle(field, context.getMethod());
+ }
+
+ @Override
public boolean registerStaticFieldWrite(DexField field) {
return enqueuer.traceStaticFieldWrite(field, context.getMethod());
}
@Override
+ public boolean registerStaticFieldWriteFromMethodHandle(DexField field) {
+ return enqueuer.traceStaticFieldWriteFromMethodHandle(field, context.getMethod());
+ }
+
+ @Override
public boolean registerConstClass(DexType type) {
return enqueuer.traceConstClass(type, context.getMethod());
}
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 36afff6..200562d 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -23,7 +23,6 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
-import com.android.tools.r8.graph.AccessControl;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
@@ -55,8 +54,8 @@
import com.android.tools.r8.graph.FieldAccessInfoCollectionImpl;
import com.android.tools.r8.graph.FieldAccessInfoImpl;
import com.android.tools.r8.graph.InnerClassAttribute;
-import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.LookupResult.LookupResultSuccess;
+import com.android.tools.r8.graph.LookupLambdaTarget;
+import com.android.tools.r8.graph.LookupTarget;
import com.android.tools.r8.graph.ObjectAllocationInfoCollectionImpl;
import com.android.tools.r8.graph.PresortedComparable;
import com.android.tools.r8.graph.ProgramMethod;
@@ -177,9 +176,6 @@
private final EnqueuerUseRegistryFactory useRegistryFactory;
private AnnotationRemover.Builder annotationRemoverBuilder;
- private final Map<DexProgramClass, Set<DexProgramClass>> immediateSubtypesOfLiveTypes =
- new IdentityHashMap<>();
-
private final Map<DexMethod, Set<DexEncodedMethod>> virtualInvokes = new IdentityHashMap<>();
private final Map<DexMethod, Set<DexEncodedMethod>> interfaceInvokes = new IdentityHashMap<>();
private final Map<DexMethod, Set<DexEncodedMethod>> superInvokes = new IdentityHashMap<>();
@@ -225,10 +221,6 @@
private final Map<DexProgramClass, Set<DexProgramClass>> unusedInterfaceTypes =
new IdentityHashMap<>();
- /** Set of all types that are instantiated, directly or indirectly, thus may be abstract. */
- private final Set<DexProgramClass> directAndIndirectlyInstantiatedTypes =
- Sets.newIdentityHashSet();
-
/**
* Set of methods that are the immediate target of an invoke. They might not actually be live but
* are required so that invokes can find the method. If a method is only a target but not live,
@@ -270,14 +262,6 @@
*/
private final Set<DexType> instantiatedAppServices = Sets.newIdentityHashSet();
- /**
- * Set of interface types for which there may be instantiations, such as lambda expressions or
- * explicit keep rules.
- */
- private final Set<DexProgramClass> instantiatedInterfaceTypes;
- /** Subset of the above that are marked instantiated by usages that are not desugared lambdas. */
- private final SetWithReason<DexProgramClass> unknownInstantiatedInterfaceTypes;
-
/** A queue of items that need processing. Different items trigger different actions. */
private final EnqueuerWorklist workList;
@@ -286,11 +270,16 @@
*/
private final Set<DexEncodedMethod> pendingReflectiveUses = Sets.newLinkedHashSet();
- /** A cache for DexMethod that have been marked reachable. */
- private final Map<DexProgramClass, Set<DexEncodedMethod>> reachableVirtualResolutions =
- new IdentityHashMap<>();
-
- private final Map<DexMethod, MarkedResolutionTarget> virtualTargetsMarkedAsReachable =
+ /**
+ * Mapping of types to the reachable method resolutions.
+ *
+ * <p>Primary map key is the initial/static/symbolic type specified by a live invoke.
+ *
+ * <p>The keys of the reachable resolutions are again the static reference, thus methodKey.holder
+ * will be the same as the classKey.type. The value of the reachable resolution is the resolution
+ * target.
+ */
+ private final Map<DexProgramClass, Map<DexMethod, ProgramMethod>> reachableVirtualResolutions =
new IdentityHashMap<>();
/**
@@ -365,8 +354,6 @@
failedResolutionTargets = SetUtils.newIdentityHashSet(2);
liveMethods = new LiveMethodsSet(graphReporter::registerMethod);
liveFields = new SetWithReason<>(graphReporter::registerField);
- unknownInstantiatedInterfaceTypes = new SetWithReason<>(graphReporter::registerInterface);
- instantiatedInterfaceTypes = Sets.newIdentityHashSet();
lambdaRewriter = options.desugarState == DesugarState.ON ? new LambdaRewriter(appView) : null;
objectAllocationInfoCollection =
@@ -518,18 +505,6 @@
pinnedItems.add(item.toReference());
}
- void markInterfaceAsInstantiated(DexProgramClass clazz, KeepReasonWitness witness) {
- assert !clazz.isAnnotation();
- assert clazz.isInterface();
- unknownInstantiatedInterfaceTypes.add(clazz, witness);
- if (!instantiatedInterfaceTypes.add(clazz)) {
- return;
- }
- populateInstantiatedTypesCache(clazz);
- markTypeAsLive(clazz, witness);
- transitionDependentItemsForInstantiatedInterface(clazz);
- }
-
private void enqueueFirstNonSerializableClassInitializer(
DexProgramClass clazz, KeepReason reason) {
assert clazz.isSerializable(appView);
@@ -663,10 +638,8 @@
}
DexEncodedMethod contextMethod = context.getMethod();
- for (DexType lambdaInstantiatedInterface : descriptor.interfaces) {
- markLambdaInstantiated(lambdaInstantiatedInterface, contextMethod);
- }
-
+ markLambdaAsInstantiated(descriptor, contextMethod);
+ transitionMethodsForInstantiatedLambda(descriptor);
if (lambdaRewriter != null) {
assert contextMethod.getCode().isCfCode() : "Unexpected input type with lambdas";
CfCode code = contextMethod.getCode().asCfCode();
@@ -718,8 +691,6 @@
default:
throw new Unreachable();
}
-
- transitionMethodsForInstantiatedLambda(descriptor);
}
boolean traceCheckCast(DexType type, DexEncodedMethod currentMethod) {
@@ -992,6 +963,15 @@
}
boolean traceInstanceFieldRead(DexField field, DexEncodedMethod currentMethod) {
+ return traceInstanceFieldRead(field, currentMethod, false);
+ }
+
+ boolean traceInstanceFieldReadFromMethodHandle(DexField field, DexEncodedMethod currentMethod) {
+ return traceInstanceFieldRead(field, currentMethod, true);
+ }
+
+ private boolean traceInstanceFieldRead(
+ DexField field, DexEncodedMethod currentMethod, boolean fromMethodHandle) {
if (!registerFieldRead(field, currentMethod)) {
return false;
}
@@ -1004,6 +984,10 @@
return false;
}
+ if (fromMethodHandle) {
+ fieldAccessInfoCollection.get(encodedField.field).setReadFromMethodHandle();
+ }
+
DexProgramClass clazz = getProgramClassOrNull(encodedField.field.holder);
if (clazz == null) {
return false;
@@ -1028,6 +1012,15 @@
}
boolean traceInstanceFieldWrite(DexField field, DexEncodedMethod currentMethod) {
+ return traceInstanceFieldWrite(field, currentMethod, false);
+ }
+
+ boolean traceInstanceFieldWriteFromMethodHandle(DexField field, DexEncodedMethod currentMethod) {
+ return traceInstanceFieldWrite(field, currentMethod, true);
+ }
+
+ private boolean traceInstanceFieldWrite(
+ DexField field, DexEncodedMethod currentMethod, boolean fromMethodHandle) {
if (!registerFieldWrite(field, currentMethod)) {
return false;
}
@@ -1040,6 +1033,10 @@
return false;
}
+ if (fromMethodHandle) {
+ fieldAccessInfoCollection.get(encodedField.field).setWrittenFromMethodHandle();
+ }
+
DexProgramClass clazz = getProgramClassOrNull(encodedField.field.holder);
if (clazz == null) {
return false;
@@ -1064,6 +1061,15 @@
}
boolean traceStaticFieldRead(DexField field, DexEncodedMethod currentMethod) {
+ return traceStaticFieldRead(field, currentMethod, false);
+ }
+
+ boolean traceStaticFieldReadFromMethodHandle(DexField field, DexEncodedMethod currentMethod) {
+ return traceStaticFieldRead(field, currentMethod, true);
+ }
+
+ private boolean traceStaticFieldRead(
+ DexField field, DexEncodedMethod currentMethod, boolean fromMethodHandle) {
if (!registerFieldRead(field, currentMethod)) {
return false;
}
@@ -1075,6 +1081,10 @@
return false;
}
+ if (fromMethodHandle) {
+ fieldAccessInfoCollection.get(encodedField.field).setReadFromMethodHandle();
+ }
+
if (!isProgramClass(encodedField.field.holder)) {
// No need to trace into the non-program code.
return false;
@@ -1108,6 +1118,15 @@
}
boolean traceStaticFieldWrite(DexField field, DexEncodedMethod currentMethod) {
+ return traceStaticFieldWrite(field, currentMethod, false);
+ }
+
+ boolean traceStaticFieldWriteFromMethodHandle(DexField field, DexEncodedMethod currentMethod) {
+ return traceStaticFieldWrite(field, currentMethod, true);
+ }
+
+ private boolean traceStaticFieldWrite(
+ DexField field, DexEncodedMethod currentMethod, boolean fromMethodHandle) {
if (!registerFieldWrite(field, currentMethod)) {
return false;
}
@@ -1119,6 +1138,10 @@
return false;
}
+ if (fromMethodHandle) {
+ fieldAccessInfoCollection.get(encodedField.field).setWrittenFromMethodHandle();
+ }
+
if (!isProgramClass(encodedField.field.holder)) {
// No need to trace into the non-program code.
return false;
@@ -1230,19 +1253,6 @@
witness);
}
- private void addImmediateSubtype(DexProgramClass superType, DexProgramClass subType) {
- assert liveTypes.contains(subType);
- assert subType.superType == superType.type
- || Arrays.asList(subType.interfaces.values).contains(superType.type);
- immediateSubtypesOfLiveTypes
- .computeIfAbsent(superType, k -> Sets.newIdentityHashSet())
- .add(subType);
- }
-
- private Set<DexProgramClass> getImmediateLiveSubtypes(DexProgramClass clazz) {
- return immediateSubtypesOfLiveTypes.getOrDefault(clazz, Collections.emptySet());
- }
-
private void markTypeAsLive(
DexProgramClass holder, ScopedDexMethodSet seen, KeepReasonWitness witness) {
if (!liveTypes.add(holder, witness)) {
@@ -1272,10 +1282,6 @@
holder.superType, ignore -> new ScopedDexMethodSet());
seen.setParent(seenForSuper);
markTypeAsLive(holder.superType, reason);
- DexProgramClass superClass = getProgramClassOrNull(holder.superType);
- if (superClass != null) {
- addImmediateSubtype(superClass, holder);
- }
}
// If this is an interface that has just become live, then report previously seen but unreported
@@ -1319,6 +1325,8 @@
rootSet.forEachDependentStaticMember(holder, appView, this::enqueueDependentItem);
compatEnqueueHolderIfDependentNonStaticMember(
holder, rootSet.getDependentKeepClassCompatRule(holder.getType()));
+
+ analyses.forEach(analysis -> analysis.processNewlyLiveClass(holder, workList));
}
private void ensureMethodsContinueToWidenAccess(DexClass clazz) {
@@ -1346,8 +1354,6 @@
return;
}
- addImmediateSubtype(clazz, implementer);
-
if (!appView.options().enableUnusedInterfaceRemoval || mode.isTracingMainDex()) {
markTypeAsLive(clazz, graphReporter.reportClassReferencedFrom(clazz, implementer));
} else {
@@ -1446,7 +1452,7 @@
return encodedField;
}
- private ResolutionResult resolveMethod(DexMethod method, KeepReason reason) {
+ private SingleResolutionResult resolveMethod(DexMethod method, KeepReason reason) {
// Record the references in case they are not program types.
recordTypeReference(method.holder);
recordTypeReference(method.proto.returnType);
@@ -1458,11 +1464,28 @@
reportMissingMethod(method);
markFailedResolutionTargets(method, resolutionResult.asFailedResolution(), reason);
}
- return resolutionResult;
+ return resolutionResult.asSingleResolution();
+ }
+
+ private SingleResolutionResult resolveMethod(
+ DexMethod method, KeepReason reason, boolean interfaceInvoke) {
+ // Record the references in case they are not program types.
+ recordTypeReference(method.holder);
+ recordTypeReference(method.proto.returnType);
+ for (DexType param : method.proto.parameters.values) {
+ recordTypeReference(param);
+ }
+ ResolutionResult resolutionResult =
+ appInfo.resolveMethod(method.holder, method, interfaceInvoke);
+ if (resolutionResult.isFailedResolution()) {
+ reportMissingMethod(method);
+ markFailedResolutionTargets(method, resolutionResult.asFailedResolution(), reason);
+ }
+ return resolutionResult.asSingleResolution();
}
private void handleInvokeOfStaticTarget(DexMethod method, KeepReason reason) {
- SingleResolutionResult resolution = resolveMethod(method, reason).asSingleResolution();
+ SingleResolutionResult resolution = resolveMethod(method, reason);
if (resolution == null || resolution.getResolvedHolder().isNotProgramClass()) {
return;
}
@@ -1563,14 +1586,30 @@
// main dex lists we allow this.
return;
}
-
- if (dontWarnPatterns.matches(context.type)) {
- // Ignore.
- return;
- }
-
DexClass holder = appView.definitionFor(type);
if (holder != null && !holder.isLibraryClass()) {
+ if (forceProguardCompatibility) {
+ // To ensure that the program works correctly we have to pin all super types and members
+ // in the tree.
+ appInfo.forEachSuperType(
+ holder,
+ (dexType, ignored) -> {
+ if (holder.isProgramClass()) {
+ DexReference holderReference = holder.toReference();
+ pinnedItems.add(holderReference);
+ rootSet.shouldNotBeMinified(holderReference);
+ for (DexEncodedMember<?, ?> member : holder.members()) {
+ DexMember<?, ?> memberReference = member.toReference();
+ pinnedItems.add(memberReference);
+ rootSet.shouldNotBeMinified(memberReference);
+ }
+ }
+ });
+ }
+ if (dontWarnPatterns.matches(context.type)) {
+ // Ignore.
+ return;
+ }
Diagnostic message =
new StringDiagnostic(
"Library class "
@@ -1648,13 +1687,10 @@
analyses.forEach(
analysis -> analysis.processNewlyInstantiatedClass(clazz.asProgramClass(), context));
- if (!objectAllocationInfoCollection.recordDirectAllocationSite(
- clazz, context, instantiationReason, keepReason)) {
+ if (!markInstantiatedClass(clazz, context, instantiationReason, keepReason)) {
return;
}
- populateInstantiatedTypesCache(clazz);
-
if (Log.ENABLED) {
Log.verbose(getClass(), "Class `%s` is instantiated, processing...", clazz);
}
@@ -1671,21 +1707,59 @@
transitionDependentItemsForInstantiatedClass(clazz);
}
- private void populateInstantiatedTypesCache(DexProgramClass clazz) {
- if (!directAndIndirectlyInstantiatedTypes.add(clazz)) {
+ // TODO(b/146016987): Make this the single instantiation entry rather than the worklist action.
+ private boolean markInstantiatedClass(
+ DexProgramClass clazz,
+ DexEncodedMethod context,
+ InstantiationReason instantiationReason,
+ KeepReason keepReason) {
+ assert !clazz.isInterface();
+ return objectAllocationInfoCollection.recordDirectAllocationSite(
+ clazz, context, instantiationReason, keepReason, appInfo);
+ }
+
+ void markInterfaceAsInstantiated(DexProgramClass clazz, KeepReasonWitness witness) {
+ assert !clazz.isAnnotation();
+ assert clazz.isInterface();
+ if (!objectAllocationInfoCollection.recordInstantiatedInterface(clazz)) {
return;
}
- if (clazz.superType != null) {
- DexProgramClass superClass = getProgramClassOrNull(clazz.superType);
- if (superClass != null) {
- populateInstantiatedTypesCache(superClass);
+ markTypeAsLive(clazz, witness);
+ transitionDependentItemsForInstantiatedInterface(clazz);
+ }
+
+ private void markLambdaAsInstantiated(LambdaDescriptor descriptor, DexEncodedMethod context) {
+ // Each descriptor is unique, so there is no check for already marking the lambda.
+ for (DexType iface : descriptor.interfaces) {
+ checkLambdaInterface(iface, context);
+ objectAllocationInfoCollection.recordInstantiatedLambdaInterface(iface, descriptor, appInfo);
+ // TODO(b/150277553): Lambdas should be accurately traces and thus not be added here.
+ if (lambdaRewriter == null) {
+ DexProgramClass clazz = getProgramClassOrNull(iface);
+ if (clazz != null) {
+ objectAllocationInfoCollection.recordInstantiatedInterface(clazz);
+ }
}
}
- for (DexType iface : clazz.interfaces.values) {
- DexProgramClass ifaceClass = getProgramClassOrNull(iface);
- if (ifaceClass != null) {
- populateInstantiatedTypesCache(ifaceClass);
- }
+ }
+
+ private void checkLambdaInterface(DexType itf, DexEncodedMethod context) {
+ DexClass clazz = definitionFor(itf);
+ if (clazz == null) {
+ StringDiagnostic message =
+ new StringDiagnostic(
+ "Lambda expression implements missing interface `" + itf.toSourceString() + "`",
+ appInfo.originFor(context.method.holder));
+ options.reporter.warning(message);
+ } else if (!clazz.isInterface()) {
+ StringDiagnostic message =
+ new StringDiagnostic(
+ "Lambda expression expected to implement an interface, but found "
+ + "`"
+ + itf.toSourceString()
+ + "`",
+ appInfo.originFor(context.method.holder));
+ options.reporter.warning(message);
}
}
@@ -1746,50 +1820,52 @@
}
}
- private Set<DexEncodedMethod> getReachableVirtualResolutions(DexProgramClass clazz) {
- return reachableVirtualResolutions.getOrDefault(clazz, Collections.emptySet());
+ private Map<DexMethod, ProgramMethod> getReachableVirtualResolutions(DexProgramClass clazz) {
+ return reachableVirtualResolutions.getOrDefault(clazz, Collections.emptyMap());
}
private void markProgramMethodOverridesAsLive(
InstantiatedObject instantiation,
DexProgramClass superClass,
ScopedDexMethodSet seenMethods) {
- for (DexEncodedMethod resolution : getReachableVirtualResolutions(superClass)) {
- if (seenMethods.addMethod(resolution)) {
- markLiveOverrides(instantiation, superClass, resolution);
- }
- }
+ Map<DexMethod, ProgramMethod> reachableResolution = getReachableVirtualResolutions(superClass);
+ reachableResolution.forEach(
+ (method, resolution) -> {
+ assert method.holder == superClass.type;
+ if (seenMethods.addMethod(resolution.getMethod())) {
+ markLiveOverrides(instantiation, superClass, resolution);
+ }
+ });
}
private void markLiveOverrides(
InstantiatedObject instantiation,
- DexProgramClass reachableHolder,
- DexEncodedMethod reachableMethod) {
- assert reachableHolder.type == reachableMethod.method.holder;
+ DexProgramClass initialHolder,
+ ProgramMethod resolutionMethod) {
// The validity of the reachable method is checked at the point it becomes "reachable" and is
// resolved. If the method is private, then the dispatch is not "virtual" and the method is
// simply marked live on its holder.
- if (reachableMethod.isPrivateMethod()) {
+ if (resolutionMethod.getMethod().isPrivateMethod()) {
markVirtualMethodAsLive(
- reachableHolder,
- reachableMethod,
+ resolutionMethod.getHolder(),
+ resolutionMethod.getMethod(),
graphReporter.reportReachableMethodAsLive(
- reachableMethod.method, new ProgramMethod(reachableHolder, reachableMethod)));
+ resolutionMethod.getMethod().method, resolutionMethod));
return;
}
// Otherwise, we set the initial holder type to be the holder of the reachable method, which
// ensures that access will be generally valid.
- SingleResolutionResult result =
- new SingleResolutionResult(reachableHolder, reachableHolder, reachableMethod);
- DexClassAndMethod lookup = result.lookupVirtualDispatchTarget(instantiation, appView);
- if (lookup == null || !lookup.isProgramMethod() || lookup.getMethod().isAbstract()) {
- return;
+ SingleResolutionResult resolution =
+ new SingleResolutionResult(
+ initialHolder, resolutionMethod.getHolder(), resolutionMethod.getMethod());
+ LookupTarget lookup = resolution.lookupVirtualDispatchTarget(instantiation, appInfo);
+ if (lookup != null) {
+ markVirtualDispatchTargetAsLive(
+ lookup,
+ programMethod ->
+ graphReporter.reportReachableMethodAsLive(
+ resolutionMethod.getMethod().method, programMethod));
}
- ProgramMethod method = lookup.asProgramMethod();
- markVirtualMethodAsLive(
- method.getHolder(),
- method.getMethod(),
- graphReporter.reportReachableMethodAsLive(reachableMethod.method, method));
}
private void markLibraryAndClasspathMethodOverridesAsLive(
@@ -1831,26 +1907,22 @@
InstantiatedObject instantiation,
DexClass libraryOrClasspathClass,
ResolutionResult resolution) {
- DexClassAndMethod lookup = resolution.lookupVirtualDispatchTarget(instantiation, appView);
- if (lookup == null || !lookup.isProgramMethod() || lookup.getMethod().isAbstract()) {
+ LookupTarget lookup = resolution.lookupVirtualDispatchTarget(instantiation, appInfo);
+ if (lookup == null) {
return;
}
- DexProgramClass clazz = lookup.asProgramMethod().getHolder();
- DexEncodedMethod target = lookup.getMethod();
- if (!lookup.getMethod().method.match(resolution.getSingleTarget())) {
- // If the resolution signature does not match the lookup signature, then the lookup must be
- // to the method implemented by a lambda that targets an actual method of another name.
- assert instantiation.isLambda();
- // TODO(b/120959039): Report a clear reason for indirect keep of the lambda target.
- markVirtualMethodAsLive(
- clazz, target, KeepReason.isLibraryMethod(clazz, libraryOrClasspathClass.type));
- } else if (shouldMarkLibraryMethodOverrideAsReachable(clazz, target)) {
- markVirtualMethodAsLive(
- clazz, target, KeepReason.isLibraryMethod(clazz, libraryOrClasspathClass.type));
+ if (!shouldMarkLibraryMethodOverrideAsReachable(lookup)) {
+ return;
}
+ markVirtualDispatchTargetAsLive(
+ lookup,
+ method ->
+ graphReporter.reportLibraryMethodAsLive(
+ instantiation, method, libraryOrClasspathClass));
if (instantiation.isClass()) {
// TODO(b/149976493): We need to mark these for lambdas too!
- markOverridesAsLibraryMethodOverrides(instantiation.asClass(), target.method);
+ markOverridesAsLibraryMethodOverrides(
+ instantiation.asClass(), lookup.asMethodTarget().getMethod().method);
}
}
@@ -1991,39 +2063,6 @@
analyses.forEach(analysis -> analysis.processNewlyLiveField(field));
}
- private void markLambdaInstantiated(DexType itf, DexEncodedMethod method) {
- DexClass clazz = appView.definitionFor(itf);
- if (clazz == null) {
- StringDiagnostic message =
- new StringDiagnostic(
- "Lambda expression implements missing interface `" + itf.toSourceString() + "`",
- appInfo.originFor(method.method.holder));
- options.reporter.warning(message);
- return;
- }
- if (!clazz.isInterface()) {
- StringDiagnostic message =
- new StringDiagnostic(
- "Lambda expression expected to implement an interface, but found "
- + "`"
- + itf.toSourceString()
- + "`",
- appInfo.originFor(method.method.holder));
- options.reporter.warning(message);
- return;
- }
- DexProgramClass programClass = clazz.asProgramClass();
- if (programClass != null) {
- // When not desugaring, we need to mark the instantiation point as unknown for now.
- if (lambdaRewriter == null) {
- unknownInstantiatedInterfaceTypes.add(programClass, KeepReason.instantiatedIn(method));
- }
- if (instantiatedInterfaceTypes.add(programClass)) {
- populateInstantiatedTypesCache(programClass);
- }
- }
- }
-
private void markDirectStaticOrConstructorMethodAsLive(
DexProgramClass clazz, DexEncodedMethod encodedMethod, KeepReason reason) {
assert encodedMethod.method.holder == clazz.type;
@@ -2088,10 +2127,6 @@
: info.isWritten();
}
- private boolean isInstantiatedOrHasInstantiatedSubtype(DexProgramClass clazz) {
- return directAndIndirectlyInstantiatedTypes.contains(clazz);
- }
-
public boolean isMethodLive(DexEncodedMethod method) {
return liveMethods.contains(method);
}
@@ -2128,7 +2163,7 @@
if (encodedField.accessFlags.isStatic()) {
markStaticFieldAsLive(encodedField, reason);
} else {
- if (isInstantiatedOrHasInstantiatedSubtype(clazz)) {
+ if (objectAllocationInfoCollection.isInstantiatedDirectlyOrHasInstantiatedSubtype(clazz)) {
markInstanceFieldAsLive(clazz, encodedField, reason);
} else {
// Add the field to the reachable set if the type later becomes instantiated.
@@ -2177,12 +2212,11 @@
return;
}
+ ProgramMethod resolutionMethod = getReachableVirtualResolutions(holder).get(method);
+
// If the method has already been marked, just report the new reason for the resolved target.
- MarkedResolutionTarget resolution = virtualTargetsMarkedAsReachable.get(method);
- if (resolution != null) {
- if (!resolution.isUnresolved()) {
- graphReporter.registerMethod(resolution.method, reason);
- }
+ if (resolutionMethod != null) {
+ graphReporter.registerMethod(resolutionMethod.getMethod(), reason);
return;
}
@@ -2190,131 +2224,88 @@
Log.verbose(getClass(), "Marking virtual method `%s` as reachable.", method);
}
- // Otherwise, the resolution target is marked and cached, and all possible targets identified.
- resolution = findAndMarkResolutionTarget(method, interfaceInvoke, reason);
- if (contextOrNull != null
- && !resolution.isUnresolved()
- && !AccessControl.isMethodAccessible(
- resolution.method, holder, contextOrNull.getHolder(), appInfo)) {
- // Not accessible from this context, so this call will cause a runtime exception.
- // Note that the resolution is not cached, as another call context may be valid.
+ SingleResolutionResult resolution = resolveMethod(method, reason, interfaceInvoke);
+ if (resolution == null) {
return;
}
- // The resolution is unresolved or accessible, both are context independent, so cache it.
- virtualTargetsMarkedAsReachable.put(method, resolution);
- if (resolution.isUnresolved() || !resolution.method.isVirtualMethod()) {
- // There is no valid resolution, so any call will lead to a runtime exception.
+ if (resolution.getResolvedHolder().isNotProgramClass()) {
+ // TODO(b/70160030): If the resolution is on a library method, then the keep edge needs to go
+ // directly to the target method in the program. Thus this method will need to ensure that
+ // 'reason' is not already reported (eg, must be delayed / non-witness) and report that for
+ // each possible target edge below.
return;
}
- // TODO(b/70160030): If the resolution is on a library method, then the keep edge needs to go
- // directly to the target method in the program. Thus this method will need to ensure that
- // 'reason' is not already reported (eg, must be delayed / non-witness) and report that for
- // each possible target edge below.
- assert resolution.holder.isProgramClass();
+ // We have to mark the resolution targeted, even if it does not become live, we
+ // need at least an abstract version of it so that it can be targeted.
+ DexProgramClass resolvedHolder = resolution.getResolvedHolder().asProgramClass();
+ DexEncodedMethod resolvedMethod = resolution.getResolvedMethod();
+ markMethodAsTargeted(resolvedHolder, resolvedMethod, reason);
- reachableVirtualResolutions
- .computeIfAbsent(resolution.holder.asProgramClass(), k -> Sets.newIdentityHashSet())
- .add(resolution.method);
-
- assert interfaceInvoke == holder.isInterface();
DexProgramClass context = contextOrNull == null ? null : contextOrNull.getHolder();
- LookupResult lookupResult =
- // TODO(b/140214802): Call on the resolution once proper resolution and lookup is resolved.
- new SingleResolutionResult(holder, resolution.holder, resolution.method)
- .lookupVirtualDispatchTargets(context, appView, appInfo, pinnedItems::contains);
- if (!lookupResult.isLookupResultSuccess()) {
+ if (contextOrNull != null && !resolution.isAccessibleForVirtualDispatchFrom(context, appInfo)) {
+ // Not accessible from this context, so this call will cause a runtime exception.
return;
}
- LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
- for (DexEncodedMethod encodedPossibleTarget : lookupResultSuccess.getMethodTargets()) {
- if (encodedPossibleTarget.isAbstract()) {
- continue;
- }
- // TODO(b/139464956): Replace this downwards search once targets are found for live types.
- markPossibleTargetsAsReachable(resolution, encodedPossibleTarget);
+
+ // 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 context == null;
+ return;
}
+
+ // The method resolved and is accessible, so currently live overrides become live.
+ reachableVirtualResolutions
+ .computeIfAbsent(holder, k -> new IdentityHashMap<>())
+ .put(method, new ProgramMethod(resolvedHolder, resolvedMethod));
+
+ resolution
+ .lookupVirtualDispatchTargets(
+ context,
+ appInfo,
+ (type, subTypeConsumer, lambdaConsumer) ->
+ objectAllocationInfoCollection.forEachInstantiatedSubType(
+ type, subTypeConsumer, lambdaConsumer, appInfo),
+ pinnedItems::contains)
+ .forEach(
+ target ->
+ markVirtualDispatchTargetAsLive(
+ target,
+ programMethod ->
+ graphReporter.reportReachableMethodAsLive(
+ resolvedMethod.method, programMethod)));
}
- private void markPossibleTargetsAsReachable(
- MarkedResolutionTarget reason,
- DexEncodedMethod encodedPossibleTarget) {
- assert encodedPossibleTarget.isVirtualMethod();
- assert !encodedPossibleTarget.isAbstract();
- DexMethod possibleTarget = encodedPossibleTarget.method;
- DexProgramClass clazz = getProgramClassOrNull(possibleTarget.holder);
- // If the holder type is uninstantiated (directly or indirectly) the method is not live yet.
- if (clazz == null || !isInstantiatedOrHasInstantiatedSubtype(clazz)) {
- return;
- }
-
- if (objectAllocationInfoCollection.isInstantiatedDirectly(clazz)
- || instantiatedInterfaceTypes.contains(clazz)) {
- markVirtualMethodAsLive(
- clazz,
- encodedPossibleTarget,
- graphReporter.reportReachableMethodAsLive(
- reason.method.method, new ProgramMethod(clazz, encodedPossibleTarget)));
+ private void markVirtualDispatchTargetAsLive(
+ LookupTarget target, Function<ProgramMethod, KeepReasonWitness> reason) {
+ if (target.isMethodTarget()) {
+ markVirtualDispatchTargetAsLive(target.asMethodTarget(), reason);
} else {
- Deque<DexType> worklist =
- new ArrayDeque<>(appInfo.allImmediateSubtypes(possibleTarget.holder));
- while (!worklist.isEmpty()) {
- DexType current = worklist.pollFirst();
- DexProgramClass currentClass = getProgramClassOrNull(current);
- // If this class overrides the virtual, abort the search. Note that, according to
- // the JVM spec, private methods cannot override a virtual method.
- if (currentClass == null || currentClass.lookupVirtualMethod(possibleTarget) != null) {
- continue;
- }
- if (objectAllocationInfoCollection.isInstantiatedDirectly(currentClass)
- || instantiatedInterfaceTypes.contains(currentClass)) {
- markVirtualMethodAsLive(
- clazz,
- encodedPossibleTarget,
- graphReporter.reportReachableMethodAsLive(encodedPossibleTarget, reason));
- break;
- }
- appInfo.allImmediateSubtypes(current).forEach(worklist::addLast);
- }
+ assert target.isLambdaTarget();
+ markVirtualDispatchTargetAsLive(target.asLambdaTarget(), reason);
}
}
- private MarkedResolutionTarget findAndMarkResolutionTarget(
- DexMethod method, boolean interfaceInvoke, KeepReason reason) {
- ResolutionResult resolutionResult =
- appInfo.resolveMethod(method.holder, method, interfaceInvoke);
- if (resolutionResult.isFailedResolution()) {
- // If the resolution fails, mark each dependency causing a failure.
- markFailedResolutionTargets(method, resolutionResult.asFailedResolution(), reason);
- return MarkedResolutionTarget.unresolved();
+ private void markVirtualDispatchTargetAsLive(
+ DexClassAndMethod target, Function<ProgramMethod, KeepReasonWitness> reason) {
+ ProgramMethod programMethod = target.asProgramMethod();
+ if (programMethod != null && !programMethod.getMethod().isAbstract()) {
+ markVirtualMethodAsLive(
+ programMethod.getHolder(), programMethod.getMethod(), reason.apply(programMethod));
}
+ }
- DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
- if (resolutionTarget == null) {
- reportMissingMethod(method);
- return MarkedResolutionTarget.unresolved();
+ private void markVirtualDispatchTargetAsLive(
+ LookupLambdaTarget target, Function<ProgramMethod, KeepReasonWitness> reason) {
+ ProgramMethod implementationMethod = target.getImplementationMethod().asProgramMethod();
+ if (implementationMethod != null) {
+ enqueueMarkMethodLiveAction(
+ implementationMethod.getHolder(),
+ implementationMethod.getMethod(),
+ reason.apply(implementationMethod));
}
-
- DexClass resolutionTargetClass = appInfo.definitionFor(resolutionTarget.method.holder);
- if (resolutionTargetClass == null) {
- reportMissingClass(resolutionTarget.method.holder);
- return MarkedResolutionTarget.unresolved();
- }
-
- if (!options.enableTreeShakingOfLibraryMethodOverrides
- && resolutionTargetClass.isNotProgramClass()) {
- return MarkedResolutionTarget.unresolved();
- }
-
- // We have to mark this as targeted, as even if this specific instance never becomes live, we
- // need at least an abstract version of it so that we have a target for the corresponding
- // invoke. This also ensures preserving the errors detailed below.
- if (resolutionTargetClass.isProgramClass()) {
- markMethodAsTargeted(resolutionTargetClass.asProgramClass(), resolutionTarget, reason);
- }
-
- return new MarkedResolutionTarget(resolutionTargetClass, resolutionTarget);
}
private void markFailedResolutionTargets(
@@ -2356,7 +2347,7 @@
// Package protected due to entry point from worklist.
void markSuperMethodAsReachable(DexMethod method, DexEncodedMethod from) {
KeepReason reason = KeepReason.targetedBySuperFrom(from);
- SingleResolutionResult resolution = resolveMethod(method, reason).asSingleResolution();
+ SingleResolutionResult resolution = resolveMethod(method, reason);
if (resolution == null) {
return;
}
@@ -2471,11 +2462,13 @@
assert fieldAccessInfoCollection.verifyMappingIsOneToOne();
for (ProgramMethod bridge : syntheticInterfaceMethodBridges.values()) {
- appView.appInfo().invalidateTypeCacheFor(bridge.getHolder().type);
- bridge.getHolder().appendVirtualMethod(bridge.getMethod());
- targetedMethods.add(bridge.getMethod(), graphReporter.fakeReportShouldNotBeUsed());
- liveMethods.add(
- bridge.getHolder(), bridge.getMethod(), graphReporter.fakeReportShouldNotBeUsed());
+ DexProgramClass holder = bridge.getHolder();
+ DexEncodedMethod method = bridge.getMethod();
+ appView.appInfo().invalidateTypeCacheFor(holder.type);
+ holder.appendVirtualMethod(method);
+ targetedMethods.add(method, graphReporter.fakeReportShouldNotBeUsed());
+ liveMethods.add(holder, method, graphReporter.fakeReportShouldNotBeUsed());
+ pinnedItems.add(method.method);
}
// Ensure references from various root set collections.
@@ -2558,8 +2551,10 @@
Collections.emptySet(),
Collections.emptyMap(),
EnumValueInfoMapCollection.empty(),
+ // TODO(b/150277553): Remove this once object allocation contains the information.
SetUtils.mapIdentityHashSet(
- unknownInstantiatedInterfaceTypes.getItems(), DexProgramClass::getType),
+ objectAllocationInfoCollection.unknownInstantiatedInterfaceTypes,
+ DexProgramClass::getType),
constClassReferences);
appInfo.markObsolete();
return appInfoWithLiveness;
@@ -2627,7 +2622,8 @@
wrapper,
null,
InstantiationReason.SYNTHESIZED_CLASS,
- graphReporter.fakeReportShouldNotBeUsed());
+ graphReporter.fakeReportShouldNotBeUsed(),
+ appInfo);
// Mark all methods on the wrapper as live and targeted.
for (DexEncodedMethod method : wrapper.methods()) {
targetedMethods.add(method, graphReporter.fakeReportShouldNotBeUsed());
@@ -2666,7 +2662,8 @@
programClass,
null,
InstantiationReason.SYNTHESIZED_CLASS,
- graphReporter.fakeReportShouldNotBeUsed());
+ graphReporter.fakeReportShouldNotBeUsed(),
+ appInfo);
// Register all of the field writes in the lambda constructors.
// This is needed to ensure that the initializers can be optimized.
@@ -2987,8 +2984,16 @@
}
}
- private boolean shouldMarkLibraryMethodOverrideAsReachable(
- DexProgramClass clazz, DexEncodedMethod method) {
+ private boolean shouldMarkLibraryMethodOverrideAsReachable(LookupTarget override) {
+ if (override.isLambdaTarget()) {
+ return true;
+ }
+ ProgramMethod programMethod = override.asMethodTarget().asProgramMethod();
+ if (programMethod == null) {
+ return false;
+ }
+ DexProgramClass clazz = programMethod.getHolder();
+ DexEncodedMethod method = programMethod.getMethod();
assert method.isVirtualMethod();
if (method.isAbstract() || method.isPrivateMethod()) {
@@ -2999,9 +3004,9 @@
return true;
}
- // If there is a subtype of `clazz` that escapes into the library and does not override `method`
- // then we need to mark the method as being reachable.
- Set<DexProgramClass> immediateSubtypes = getImmediateLiveSubtypes(clazz);
+ // If there is an instantiated subtype of `clazz` that escapes into the library and does not
+ // override `method` then we need to mark the method as being reachable.
+ Set<DexProgramClass> immediateSubtypes = getImmediateSubtypesInInstantiatedHierarchy(clazz);
if (immediateSubtypes.isEmpty()) {
return false;
}
@@ -3020,7 +3025,7 @@
return true;
}
- for (DexProgramClass subtype : getImmediateLiveSubtypes(current)) {
+ for (DexProgramClass subtype : getImmediateSubtypesInInstantiatedHierarchy(current)) {
if (visited.add(subtype)) {
worklist.add(subtype);
}
@@ -3030,6 +3035,21 @@
return false;
}
+ private Set<DexProgramClass> getImmediateSubtypesInInstantiatedHierarchy(DexProgramClass clazz) {
+ Set<DexClass> subtypes =
+ objectAllocationInfoCollection.getImmediateSubtypesInInstantiatedHierarchy(clazz.type);
+ if (subtypes == null) {
+ return Collections.emptySet();
+ }
+ Set<DexProgramClass> programClasses = SetUtils.newIdentityHashSet(subtypes.size());
+ for (DexClass subtype : subtypes) {
+ if (subtype.isProgramClass()) {
+ programClasses.add(subtype.asProgramClass());
+ }
+ }
+ return programClasses;
+ }
+
// Package protected due to entry point from worklist.
void markMethodAsLive(DexEncodedMethod method, KeepReason reason) {
assert liveMethods.contains(method);
@@ -3543,100 +3563,6 @@
}
}
- public static class MarkedResolutionTarget {
-
- private static final MarkedResolutionTarget UNRESOLVED = new MarkedResolutionTarget(null, null);
-
- final DexClass holder;
- final DexEncodedMethod method;
-
- public static MarkedResolutionTarget unresolved() {
- return UNRESOLVED;
- }
-
- public MarkedResolutionTarget(DexClass holder, DexEncodedMethod method) {
- assert (holder == null && method == null) || holder.type == method.method.holder;
- this.holder = holder;
- this.method = method;
- }
-
- public boolean isUnresolved() {
- return this == unresolved();
- }
-
- @Override
- public int hashCode() {
- // The encoded method already encodes information of the holder.
- return method.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- // The encoded method already encodes information of the holder.
- return obj instanceof MarkedResolutionTarget
- && ((MarkedResolutionTarget) obj).method.equals(method);
- }
- }
-
- private static class ReachableVirtualMethodsSet {
-
- private final Map<DexEncodedMethod, Set<MarkedResolutionTarget>> methods =
- Maps.newIdentityHashMap();
-
- public Set<DexEncodedMethod> getMethods() {
- return methods.keySet();
- }
-
- public Set<MarkedResolutionTarget> getReasons(DexEncodedMethod method) {
- return methods.get(method);
- }
-
- public boolean add(DexEncodedMethod method, MarkedResolutionTarget reason) {
- Set<MarkedResolutionTarget> reasons = getReasons(method);
- if (reasons == null) {
- reasons = new HashSet<>();
- reasons.add(reason);
- methods.put(method, reasons);
- return true;
- }
- reasons.add(reason);
- return false;
- }
- }
-
- private static final class TargetWithContext<R extends DexMember<?, R>> {
-
- private final R target;
- private final DexEncodedMethod context;
-
- private TargetWithContext(R target, DexEncodedMethod context) {
- this.target = target;
- this.context = context;
- }
-
- public R getTarget() {
- return target;
- }
-
- public DexEncodedMethod getContext() {
- return context;
- }
-
- @Override
- public int hashCode() {
- return target.hashCode() * 31 + context.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof TargetWithContext)) {
- return false;
- }
- TargetWithContext other = (TargetWithContext) obj;
- return (this.target == other.target) && (this.context == other.context);
- }
- }
-
private class AnnotationReferenceMarker implements IndexedItemCollection {
private final DexItem annotationHolder;
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 45e61a4..c795bcc 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
@@ -270,7 +270,7 @@
// TODO(b/142378367): Context is the containing method that is cause of the instantiation.
// Consider updating call sites with the context information to increase precision where possible.
- void enqueueMarkInstantiatedAction(
+ public void enqueueMarkInstantiatedAction(
DexProgramClass clazz,
DexEncodedMethod context,
InstantiationReason instantiationReason,
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 37be4b3..cf863de 100644
--- a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
+++ b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
@@ -30,7 +30,6 @@
import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.references.TypeReference;
-import com.android.tools.r8.shaking.Enqueuer.MarkedResolutionTarget;
import com.android.tools.r8.utils.DequeUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
@@ -259,25 +258,16 @@
return KeepReasonWitness.INSTANCE;
}
- public KeepReasonWitness reportReachableMethodAsLive(
- DexEncodedMethod encodedMethod, MarkedResolutionTarget reason) {
- if (keptGraphConsumer != null) {
+ public KeepReasonWitness reportLibraryMethodAsLive(
+ InstantiatedObject instantiation,
+ ProgramMethod derivedMethod,
+ DexClass libraryOrClasspathClass) {
+ // TODO(b/120959039): Report a clear reason for indirect keep of the lambda target.
+ if (keptGraphConsumer != null && instantiation.isClass()) {
return reportEdge(
- getMethodGraphNode(reason.method.method),
- getMethodGraphNode(encodedMethod.method),
- EdgeKind.OverridingMethod);
- }
- return KeepReasonWitness.INSTANCE;
- }
-
- public KeepReasonWitness reportReachableMethodAsLive(
- DexEncodedMethod encodedMethod, Set<MarkedResolutionTarget> reasons) {
- assert !reasons.isEmpty();
- if (keptGraphConsumer != null) {
- MethodGraphNode target = getMethodGraphNode(encodedMethod.method);
- for (MarkedResolutionTarget reason : reasons) {
- reportEdge(getMethodGraphNode(reason.method.method), target, EdgeKind.OverridingMethod);
- }
+ getClassGraphNode(instantiation.asClass().type),
+ getMethodGraphNode(derivedMethod.getMethod().method),
+ EdgeKind.IsLibraryMethod);
}
return KeepReasonWitness.INSTANCE;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepReason.java b/src/main/java/com/android/tools/r8/shaking/KeepReason.java
index cc86522..c66d4a4 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepReason.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepReason.java
@@ -49,10 +49,6 @@
return new InvokedFromLambdaCreatedIn(method);
}
- public static KeepReason isLibraryMethod(DexProgramClass implementer, DexType libraryType) {
- return new IsLibraryMethod(implementer.type, libraryType);
- }
-
public static KeepReason fieldReferencedIn(DexEncodedMethod method) {
return new ReferencedFrom(method);
}
@@ -223,27 +219,6 @@
}
}
- public static class IsLibraryMethod extends KeepReason {
-
- private final DexType implementer;
- private final DexType libraryType;
-
- private IsLibraryMethod(DexType implementer, DexType libraryType) {
- this.implementer = implementer;
- this.libraryType = libraryType;
- }
-
- @Override
- public EdgeKind edgeKind() {
- return EdgeKind.IsLibraryMethod;
- }
-
- @Override
- public GraphNode getSourceNode(GraphReporter graphReporter) {
- return graphReporter.getClassGraphNode(implementer);
- }
- }
-
private static class ReferencedInAnnotation extends KeepReason {
private final DexItem holder;
diff --git a/src/main/java/com/android/tools/r8/shaking/LibraryModeledPredicate.java b/src/main/java/com/android/tools/r8/shaking/LibraryModeledPredicate.java
new file mode 100644
index 0000000..39ee67c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/LibraryModeledPredicate.java
@@ -0,0 +1,13 @@
+// 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.DexType;
+
+@FunctionalInterface
+public interface LibraryModeledPredicate {
+
+ boolean isModeled(DexType type);
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
index 8b86596..3598fd8 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -70,6 +70,7 @@
private boolean keepRuleSynthesisForRecompilation = false;
private boolean configurationDebugging = false;
private boolean dontUseMixedCaseClassnames = false;
+ private boolean protoShrinking = false;
private int maxRemovedAndroidLogLevel = 1;
private ProguardKeepRule keepAllRule;
@@ -290,6 +291,10 @@
this.dontUseMixedCaseClassnames = dontUseMixedCaseClassnames;
}
+ public void enableProtoShrinking() {
+ protoShrinking = true;
+ }
+
public int getMaxRemovedAndroidLogLevel() {
return maxRemovedAndroidLogLevel;
}
@@ -357,6 +362,7 @@
keepDirectories.build(),
configurationDebugging,
dontUseMixedCaseClassnames,
+ protoShrinking,
maxRemovedAndroidLogLevel,
keepAllRule);
@@ -431,6 +437,7 @@
private final ProguardPathFilter keepDirectories;
private final boolean configurationDebugging;
private final boolean dontUseMixedCaseClassnames;
+ private final boolean protoShrinking;
private final int maxRemovedAndroidLogLevel;
private final ProguardKeepRule keepAllRule;
@@ -473,6 +480,7 @@
ProguardPathFilter keepDirectories,
boolean configurationDebugging,
boolean dontUseMixedCaseClassnames,
+ boolean protoShrinking,
int maxRemovedAndroidLogLevel,
ProguardKeepRule keepAllRule) {
this.parsedConfiguration = parsedConfiguration;
@@ -513,6 +521,7 @@
this.keepDirectories = keepDirectories;
this.configurationDebugging = configurationDebugging;
this.dontUseMixedCaseClassnames = dontUseMixedCaseClassnames;
+ this.protoShrinking = protoShrinking;
this.maxRemovedAndroidLogLevel = maxRemovedAndroidLogLevel;
this.keepAllRule = keepAllRule;
}
@@ -681,6 +690,10 @@
return dontUseMixedCaseClassnames;
}
+ public boolean isProtoShrinkingEnabled() {
+ return protoShrinking;
+ }
+
public int getMaxRemovedAndroidLogLevel() {
return maxRemovedAndroidLogLevel;
}
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 f1a2b0f..7e9ed91 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -71,7 +71,6 @@
// TODO(b/62524562): we may support this later.
"mergeinterfacesaggressively",
"android",
- "shrinkunusedprotofields",
"allowruntypeandignoreoptimizationpasses",
"dontshrinkduringoptimization",
"convert_proto_enum_to_string");
@@ -286,6 +285,8 @@
if (isOptionalArgumentGiven()) {
configurationBuilder.setPrintUsageFile(parseFileName(false));
}
+ } else if (acceptString("shrinkunusedprotofields")) {
+ configurationBuilder.enableProtoShrinking();
} else if (acceptString("verbose")) {
configurationBuilder.setVerbose(true);
} else if (acceptString("ignorewarnings")) {
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 2189be7..928de3a 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -77,7 +77,6 @@
private final DirectMappedDexApplication application;
private final Iterable<? extends ProguardConfigurationRule> rules;
private final Map<DexReference, Set<ProguardKeepRuleBase>> noShrinking = new IdentityHashMap<>();
- private final Set<DexReference> noOptimization = Sets.newIdentityHashSet();
private final Set<DexReference> noObfuscation = Sets.newIdentityHashSet();
private final LinkedHashMap<DexReference, DexReference> reasonAsked = new LinkedHashMap<>();
private final LinkedHashMap<DexReference, DexReference> checkDiscarded = new LinkedHashMap<>();
@@ -313,7 +312,6 @@
alwaysClassInline,
neverMerge,
alwaysInline,
- neverInline,
bypassClinitforInlining);
}
assert Sets.intersection(neverInline, alwaysInline).isEmpty()
@@ -321,7 +319,6 @@
: "A method cannot be marked as both -neverinline and -forceinline/-alwaysinline.";
return new RootSet(
noShrinking,
- noOptimization,
noObfuscation,
ImmutableList.copyOf(reasonAsked.values()),
ImmutableList.copyOf(checkDiscarded.values()),
@@ -410,7 +407,6 @@
neverInline,
neverClassInline,
noShrinking,
- noOptimization,
noObfuscation,
dependentNoShrinking,
dependentKeepClassCompatRule,
@@ -1101,9 +1097,11 @@
context.markAsUsed();
}
if (!modifiers.allowsOptimization) {
- noOptimization.add(item.toReference());
+ // The -dontoptimize flag has only effect through the keep all rule, but we still
+ // need to mark the rule as used.
context.markAsUsed();
}
+
if (!modifiers.allowsObfuscation) {
noObfuscation.add(item.toReference());
context.markAsUsed();
@@ -1252,7 +1250,6 @@
public static class RootSet {
public final Map<DexReference, Set<ProguardKeepRuleBase>> noShrinking;
- public final Set<DexReference> noOptimization;
private final Set<DexReference> noObfuscation;
public final ImmutableList<DexReference> reasonAsked;
public final ImmutableList<DexReference> checkDiscarded;
@@ -1281,7 +1278,6 @@
private RootSet(
Map<DexReference, Set<ProguardKeepRuleBase>> noShrinking,
- Set<DexReference> noOptimization,
Set<DexReference> noObfuscation,
ImmutableList<DexReference> reasonAsked,
ImmutableList<DexReference> checkDiscarded,
@@ -1307,7 +1303,6 @@
Set<ProguardIfRule> ifRules,
List<DelayedRootSetActionItem> delayedRootSetActionItems) {
this.noShrinking = noShrinking;
- this.noOptimization = noOptimization;
this.noObfuscation = noObfuscation;
this.reasonAsked = reasonAsked;
this.checkDiscarded = checkDiscarded;
@@ -1353,7 +1348,6 @@
void addConsequentRootSet(ConsequentRootSet consequentRootSet, boolean addNoShrinking) {
neverInline.addAll(consequentRootSet.neverInline);
neverClassInline.addAll(consequentRootSet.neverClassInline);
- noOptimization.addAll(consequentRootSet.noOptimization);
noObfuscation.addAll(consequentRootSet.noObfuscation);
if (addNoShrinking) {
consequentRootSet.noShrinking.forEach(
@@ -1436,9 +1430,6 @@
if (noShrinking.containsKey(original)) {
noShrinking.put(rewritten, noShrinking.get(original));
}
- if (noOptimization.contains(original)) {
- noOptimization.add(rewritten);
- }
if (noObfuscation.contains(original)) {
noObfuscation.add(rewritten);
}
@@ -1452,7 +1443,6 @@
public void prune(DexReference reference) {
noShrinking.remove(reference);
- noOptimization.remove(reference);
noObfuscation.remove(reference);
noSideEffects.remove(reference);
assumedValues.remove(reference);
@@ -1632,7 +1622,6 @@
builder.append("RootSet");
builder.append("\nnoShrinking: " + noShrinking.size());
- builder.append("\nnoOptimization: " + noOptimization.size());
builder.append("\nnoObfuscation: " + noObfuscation.size());
builder.append("\nreasonAsked: " + reasonAsked.size());
builder.append("\ncheckDiscarded: " + checkDiscarded.size());
@@ -1658,7 +1647,6 @@
final Set<DexMethod> neverInline;
final Set<DexType> neverClassInline;
final Map<DexReference, Set<ProguardKeepRuleBase>> noShrinking;
- final Set<DexReference> noOptimization;
final Set<DexReference> noObfuscation;
final Map<DexReference, Map<DexReference, Set<ProguardKeepRuleBase>>> dependentNoShrinking;
final Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule;
@@ -1668,7 +1656,6 @@
Set<DexMethod> neverInline,
Set<DexType> neverClassInline,
Map<DexReference, Set<ProguardKeepRuleBase>> noShrinking,
- Set<DexReference> noOptimization,
Set<DexReference> noObfuscation,
Map<DexReference, Map<DexReference, Set<ProguardKeepRuleBase>>> dependentNoShrinking,
Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule,
@@ -1676,7 +1663,6 @@
this.neverInline = Collections.unmodifiableSet(neverInline);
this.neverClassInline = Collections.unmodifiableSet(neverClassInline);
this.noShrinking = Collections.unmodifiableMap(noShrinking);
- this.noOptimization = Collections.unmodifiableSet(noOptimization);
this.noObfuscation = Collections.unmodifiableSet(noObfuscation);
this.dependentNoShrinking = Collections.unmodifiableMap(dependentNoShrinking);
this.dependentKeepClassCompatRule = Collections.unmodifiableMap(dependentKeepClassCompatRule);
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 05d281b..1fb713e 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -175,6 +175,7 @@
clazz.removeEnclosingMethod(this::isAttributeReferencingPrunedItem);
rewriteNestAttributes(clazz);
usagePrinter.visited();
+ assert verifyNoDeadFields(clazz);
}
private void rewriteNestAttributes(DexProgramClass clazz) {
@@ -355,4 +356,12 @@
public Collection<DexReference> getMethodsToKeepForConfigurationDebugging() {
return Collections.unmodifiableCollection(methodsToKeepForConfigurationDebugging);
}
+
+ private boolean verifyNoDeadFields(DexProgramClass clazz) {
+ for (DexEncodedField field : clazz.fields()) {
+ assert !field.getOptimizationInfo().isDead()
+ : "Expected field `" + field.field.toSourceString() + "` to be absent";
+ }
+ return true;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 3ba6b3a..43110c2 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -28,7 +28,7 @@
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.GraphLense.GraphLenseLookupResult;
-import com.android.tools.r8.graph.LookupResult;
+import com.android.tools.r8.graph.LookupResult.LookupResultSuccess;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ResolutionResult;
@@ -45,6 +45,7 @@
import com.android.tools.r8.ir.synthetic.AbstractSynthesizedCode;
import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.FieldSignatureEquivalence;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.android.tools.r8.utils.Timing;
@@ -499,8 +500,14 @@
if (!(method.accessFlags.isPublic() || method.accessFlags.isPrivate())) {
return true;
}
+ // Check if the target is overriding and narrowing the access.
+ if (method.accessFlags.isPublic()) {
+ DexEncodedMethod targetOverride = target.lookupVirtualMethod(method.method);
+ if (targetOverride != null && !targetOverride.accessFlags.isPublic()) {
+ return true;
+ }
+ }
}
-
// Check that all accesses from [source] to classes or members from the current package of
// [source] will continue to work. This is guaranteed if the methods of [source] do not access
// any private or protected classes or members from the current package of [source].
@@ -736,26 +743,34 @@
// }
for (DexEncodedMethod method : defaultMethods) {
// Conservatively find all possible targets for this method.
- LookupResult lookupResult =
+ LookupResultSuccess lookupResult =
appInfo
.resolveMethodOnInterface(method.method.holder, method.method)
- .lookupVirtualDispatchTargets(target, appView);
- assert lookupResult.isLookupResultSuccess();
- if (lookupResult.isLookupResultFailure()) {
+ .lookupVirtualDispatchTargets(target, appInfo)
+ .asLookupResultSuccess();
+ assert lookupResult != null;
+ if (lookupResult == null) {
return true;
}
- assert lookupResult.isLookupResultSuccess();
- Set<DexEncodedMethod> interfaceTargets =
- lookupResult.asLookupResultSuccess().getMethodTargets();
- // If [method] is not even an interface-target, then we can safely merge it. Otherwise we
- // need to check for a conflict.
- if (interfaceTargets.remove(method)) {
- for (DexEncodedMethod interfaceTarget : interfaceTargets) {
- DexClass enclosingClass = appInfo.definitionFor(interfaceTarget.method.holder);
- if (enclosingClass != null && enclosingClass.isInterface()) {
- // Found another default method that is different from the one in [source], aborting.
- return true;
- }
+ if (lookupResult.contains(method)) {
+ Box<Boolean> found = new Box<>(false);
+ lookupResult.forEach(
+ interfaceTarget -> {
+ if (interfaceTarget.getMethod() == method) {
+ return;
+ }
+ DexClass enclosingClass = interfaceTarget.getHolder();
+ if (enclosingClass != null && enclosingClass.isInterface()) {
+ // Found a default method that is different from the one in [source], aborting.
+ found.set(true);
+ }
+ },
+ lambdaTarget -> {
+ // The merger should already have excluded lambda implemented interfaces.
+ assert false;
+ });
+ if (found.get()) {
+ return true;
}
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
index 8c5c3b2..60b6f7d 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
@@ -12,36 +12,36 @@
* Android API level description
*/
public enum AndroidApiLevel {
- R(30), // Speculative, this can change.
- Q(29),
- P(28),
- O_MR1(27),
- O(26),
- N_MR1(25),
- N(24),
- M(23),
- L_MR1(22),
- L(21),
- K_WATCH(20),
- K(19),
- J_MR2(18),
- J_MR1(17),
- J(16),
- I_MR1(15),
- I(14),
- H_MR2(13),
- H_MR1(12),
- H(11),
- G_MR1(10),
- G(9),
- F(8),
- E_MR1(7),
- E_0_1(6),
- E(5),
- D(4),
- C(3),
+ B(1),
B_1_1(2),
- B(1);
+ C(3),
+ D(4),
+ E(5),
+ E_0_1(6),
+ E_MR1(7),
+ F(8),
+ G(9),
+ G_MR1(10),
+ H(11),
+ H_MR1(12),
+ H_MR2(13),
+ I(14),
+ I_MR1(15),
+ J(16),
+ J_MR1(17),
+ J_MR2(18),
+ K(19),
+ K_WATCH(20),
+ L(21),
+ L_MR1(22),
+ M(23),
+ N(24),
+ N_MR1(25),
+ O(26),
+ O_MR1(27),
+ P(28),
+ Q(29),
+ R(30); // Speculative, this can change.
public static final AndroidApiLevel LATEST = Q;
diff --git a/src/main/java/com/android/tools/r8/utils/BooleanUtils.java b/src/main/java/com/android/tools/r8/utils/BooleanUtils.java
index cc94778..75d47f6 100644
--- a/src/main/java/com/android/tools/r8/utils/BooleanUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/BooleanUtils.java
@@ -11,6 +11,10 @@
return value ? 1 : 0;
}
+ public static long longValue(boolean value) {
+ return value ? 1L : 0L;
+ }
+
public static Boolean[] values() {
return VALUES;
}
diff --git a/src/main/java/com/android/tools/r8/utils/ExceptionDiagnostic.java b/src/main/java/com/android/tools/r8/utils/ExceptionDiagnostic.java
index 78d47c2..4dc6d48 100644
--- a/src/main/java/com/android/tools/r8/utils/ExceptionDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/utils/ExceptionDiagnostic.java
@@ -18,10 +18,16 @@
public class ExceptionDiagnostic extends DiagnosticWithThrowable {
private final Origin origin;
+ private final Position position;
- public ExceptionDiagnostic(Throwable e, Origin origin) {
+ public ExceptionDiagnostic(Throwable e, Origin origin, Position position) {
super(e);
this.origin = origin;
+ this.position = position;
+ }
+
+ public ExceptionDiagnostic(Throwable e, Origin origin) {
+ this(e, origin, Position.UNKNOWN);
}
public ExceptionDiagnostic(ResourceException e) {
@@ -35,7 +41,7 @@
@Override
public Position getPosition() {
- return Position.UNKNOWN;
+ return position;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
index d12c2a4..79e3818 100644
--- a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
@@ -11,12 +11,14 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.position.Position;
import com.google.common.collect.ObjectArrays;
import java.io.IOException;
import java.nio.file.FileSystemException;
import java.nio.file.Paths;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
+import java.util.function.Supplier;
public abstract class ExceptionUtils {
@@ -68,7 +70,7 @@
} catch (IOException e) {
throw reporter.fatalError(new ExceptionDiagnostic(e, extractIOExceptionOrigin(e)));
} catch (CompilationError e) {
- throw reporter.fatalError(e.toStringDiagnostic());
+ throw reporter.fatalError(new ExceptionDiagnostic(e, e.getOrigin(), e.getPosition()));
} catch (ResourceException e) {
throw reporter.fatalError(new ExceptionDiagnostic(e, e.getOrigin()));
} catch (AssertionError e) {
@@ -132,4 +134,15 @@
throw new RuntimeException(executionException.getMessage(), cause);
}
}
+
+ public static <T> T withOriginAttachmentHandler(
+ Origin origin, Position position, Supplier<T> action) {
+ try {
+ return action.get();
+ } catch (CompilationError e) {
+ throw e.withAdditionalOriginAndPositionInfo(origin, position);
+ } catch (RuntimeException e) {
+ throw new CompilationError(e.getMessage(), e, origin, position);
+ }
+ }
}
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 8d04c47..c3df10f 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -148,6 +148,18 @@
disableAllOptimizations();
}
configurationDebugging = proguardConfiguration.isConfigurationDebugging();
+ if (proguardConfiguration.isProtoShrinkingEnabled()) {
+ enableProtoShrinking();
+ }
+ }
+
+ void enableProtoShrinking() {
+ applyInliningToInlinee = true;
+ enableFieldBitAccessAnalysis = true;
+ enableStringSwitchConversion = true;
+ protoShrinking.enableGeneratedMessageLiteShrinking = true;
+ protoShrinking.enableGeneratedMessageLiteBuilderShrinking = true;
+ protoShrinking.enableGeneratedExtensionRegistryShrinking = true;
}
void disableAllOptimizations() {
@@ -256,6 +268,7 @@
public boolean enableStringSwitchConversion =
System.getProperty("com.android.tools.r8.stringSwitchConversion") != null;
public boolean enableEnumValueOptimization = true;
+ public boolean enableEnumSwitchMapRemoval = true;
public final OutlineOptions outline = new OutlineOptions();
public boolean enableInitializedClassesInInstanceMethodsAnalysis = true;
public boolean enableRedundantFieldLoadElimination = true;
@@ -996,17 +1009,10 @@
public static class ProtoShrinkingOptions {
- public boolean enableGeneratedExtensionRegistryShrinking =
- System.getProperty("com.android.tools.r8.generatedExtensionRegistryShrinking") != null;
-
- public boolean enableGeneratedMessageLiteShrinking =
- System.getProperty("com.android.tools.r8.generatedMessageLiteShrinking") != null;
-
- public boolean enableGeneratedMessageLiteBuilderShrinking =
- System.getProperty("com.android.tools.r8.generatedMessageLiteBuilderShrinking") != null;
-
- public boolean traverseOneOfAndRepeatedProtoFields =
- System.getProperty("com.android.tools.r8.traverseOneOfAndRepeatedProtoFields") == null;
+ public boolean enableGeneratedExtensionRegistryShrinking = false;
+ public boolean enableGeneratedMessageLiteShrinking = false;
+ public boolean enableGeneratedMessageLiteBuilderShrinking = false;
+ public boolean traverseOneOfAndRepeatedProtoFields = false;
public boolean isProtoShrinkingEnabled() {
return enableGeneratedExtensionRegistryShrinking
diff --git a/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java b/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java
index d602a51..3681a77 100644
--- a/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java
+++ b/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java
@@ -4,13 +4,10 @@
package com.android.tools.r8.utils;
import com.android.tools.r8.dex.ApplicationReader.ProgramClassConflictResolver;
-import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.DuplicateTypesDiagnostic;
import com.android.tools.r8.graph.ClassKind;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryWrapperSynthesizer;
import com.android.tools.r8.references.Reference;
import com.google.common.collect.ImmutableList;
import java.util.List;
@@ -85,9 +82,6 @@
private static DexProgramClass mergeClasses(
Reporter reporter, DexProgramClass a, DexProgramClass b) {
- if (DesugaredLibraryWrapperSynthesizer.isSynthesizedWrapper(a.type)) {
- return mergeWrappers(a, b);
- }
if (a.type.isD8R8SynthesizedClassType()) {
assert assertEqualClasses(a, b);
return a;
@@ -100,22 +94,4 @@
assert a.directMethods().size() == b.directMethods().size();
return true;
}
-
- private static DexProgramClass mergeWrappers(DexProgramClass a, DexProgramClass b) {
- DexEncodedMethod aMethod = findConversionMethod(a);
- DexEncodedMethod bMethod = findConversionMethod(b);
- return aMethod.getCode().estimatedSizeForInlining()
- > bMethod.getCode().estimatedSizeForInlining()
- ? a
- : b;
- }
-
- private static DexEncodedMethod findConversionMethod(DexProgramClass clazz) {
- for (DexEncodedMethod dexEncodedMethod : clazz.directMethods()) {
- if (!dexEncodedMethod.isInstanceInitializer()) {
- return dexEncodedMethod;
- }
- }
- throw new CompilationError("A wrapper should have a conversion method.");
- }
}
diff --git a/src/main/java/com/android/tools/r8/utils/WorkList.java b/src/main/java/com/android/tools/r8/utils/WorkList.java
index d389826..3e35802 100644
--- a/src/main/java/com/android/tools/r8/utils/WorkList.java
+++ b/src/main/java/com/android/tools/r8/utils/WorkList.java
@@ -15,6 +15,16 @@
private final Deque<T> workingList = new ArrayDeque<>();
private final Set<T> seen;
+ public static <T> WorkList<T> newEqualityWorkList() {
+ return new WorkList<T>(EqualityTest.HASH);
+ }
+
+ public static <T> WorkList<T> newEqualityWorkList(Iterable<T> items) {
+ WorkList<T> workList = new WorkList<>(EqualityTest.HASH);
+ workList.addIfNotSeen(items);
+ return workList;
+ }
+
public static <T> WorkList<T> newIdentityWorkList() {
return new WorkList<T>(EqualityTest.IDENTITY);
}
@@ -43,10 +53,12 @@
}
}
- public void addIfNotSeen(T item) {
+ public boolean addIfNotSeen(T item) {
if (seen.add(item)) {
workingList.addLast(item);
+ return true;
}
+ return false;
}
public boolean hasNext() {
diff --git a/src/test/examples/shaking1/print-mapping.ref b/src/test/examples/shaking1/print-mapping-cf.ref
similarity index 64%
rename from src/test/examples/shaking1/print-mapping.ref
rename to src/test/examples/shaking1/print-mapping-cf.ref
index d346c04..87a7bc0 100644
--- a/src/test/examples/shaking1/print-mapping.ref
+++ b/src/test/examples/shaking1/print-mapping-cf.ref
@@ -1,6 +1,5 @@
shaking1.Shaking -> shaking1.Shaking:
shaking1.Used -> a.a:
- java.lang.String name -> a
1:1:java.lang.String method():17:17 -> a
1:1:void main(java.lang.String[]):8:8 -> main
- 1:2:void <init>(java.lang.String):12:13 -> <init>
+ 1:1:void <init>(java.lang.String):12:12 -> <init>
diff --git a/src/test/examples/shaking1/print-mapping-dex.ref b/src/test/examples/shaking1/print-mapping-dex.ref
new file mode 100644
index 0000000..602e5f8
--- /dev/null
+++ b/src/test/examples/shaking1/print-mapping-dex.ref
@@ -0,0 +1,5 @@
+shaking1.Shaking -> shaking1.Shaking:
+shaking1.Used -> a.a:
+ java.lang.String method() -> a
+ 1:1:void main(java.lang.String[]):8:8 -> main
+ 1:1:void <init>(java.lang.String):12:12 -> <init>
diff --git a/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java b/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java
index 1defe48..ce6cbcc 100644
--- a/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java
+++ b/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java
@@ -132,7 +132,6 @@
// java.util.concurrent.Flow$Subscriber present in JDK9+.
testBuilder
.run(parameters.getRuntime(), "MySubscriber")
- .disassemble()
.assertSuccessWithOutputLines("Got : 1", "Got : 2", "Got : 3", "Done");
}
}
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index 14d460e..57fc2ec 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -81,11 +81,6 @@
instanceof DexFilePerClassFileConsumer);
}
- @Test(expected = CompilationFailedException.class)
- public void disallowClassFileConsumer() throws Throwable {
- D8Command.builder().setProgramConsumer(ClassFileConsumer.emptyConsumer()).build();
- }
-
@Test
public void desugaredLibraryKeepRuleConsumer() throws Exception {
StringConsumer stringConsumer = StringConsumer.emptyConsumer();
diff --git a/src/test/java/com/android/tools/r8/D8TestCompileResult.java b/src/test/java/com/android/tools/r8/D8TestCompileResult.java
index 80b23ac..8b91d57 100644
--- a/src/test/java/com/android/tools/r8/D8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/D8TestCompileResult.java
@@ -3,14 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
-import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.utils.AndroidApp;
public class D8TestCompileResult extends TestCompileResult<D8TestCompileResult, D8TestRunResult> {
D8TestCompileResult(TestState state, AndroidApp app, OutputMode outputMode) {
super(state, app, outputMode);
- assert ToolHelper.verifyValidOutputMode(Backend.DEX, outputMode);
}
@Override
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index f847f34..569c222 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -427,6 +427,18 @@
return self();
}
+ public T enableProtoShrinking() {
+ return enableProtoShrinking(true);
+ }
+
+ public T enableProtoShrinking(boolean traverseOneOfAndRepeatedProtoFields) {
+ if (traverseOneOfAndRepeatedProtoFields) {
+ addOptionsModification(
+ options -> options.protoShrinking().traverseOneOfAndRepeatedProtoFields = true);
+ }
+ return addKeepRules("-shrinkunusedprotofields");
+ }
+
public T enableSideEffectAnnotations() {
if (!enableSideEffectAnnotations) {
enableSideEffectAnnotations = true;
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index 4f64c5b..78dee67 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -237,6 +237,11 @@
return self();
}
+ public T setEnableDesugaring(boolean enableDesugaring) {
+ builder.setEnableDesugaring(enableDesugaring);
+ return self();
+ }
+
public OutputMode getOutputMode() {
if (programConsumer instanceof DexIndexedConsumer) {
return OutputMode.DexIndexed;
diff --git a/src/test/java/com/android/tools/r8/TestParametersBuilder.java b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
index b29d851..8c6b864 100644
--- a/src/test/java/com/android/tools/r8/TestParametersBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
@@ -7,9 +7,12 @@
import com.android.tools.r8.TestRuntime.NoneRuntime;
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.utils.AndroidApiLevel;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -147,8 +150,10 @@
private static final AndroidApiLevel lowestCompilerApiLevel = AndroidApiLevel.B;
private boolean enableApiLevels = false;
+ private boolean enableApiLevelsForCf = false;
private Predicate<AndroidApiLevel> apiLevelFilter = param -> false;
+ private List<AndroidApiLevel> explicitApiLevels = new ArrayList<>();
private TestParametersBuilder withApiFilter(Predicate<AndroidApiLevel> filter) {
enableApiLevels = true;
@@ -160,7 +165,13 @@
return withApiFilter(api -> true);
}
+ public TestParametersBuilder withAllApiLevelsAlsoForCf() {
+ enableApiLevelsForCf = true;
+ return withAllApiLevels();
+ }
+
public TestParametersBuilder withApiLevel(AndroidApiLevel api) {
+ explicitApiLevels.add(api);
return withApiFilter(api::equals);
}
@@ -190,9 +201,17 @@
}
public Stream<TestParameters> createParameters(TestRuntime runtime) {
- if (!enableApiLevels || !runtime.isDex()) {
+ if (!enableApiLevels) {
return Stream.of(new TestParameters(runtime));
}
+ if (!runtime.isDex()) {
+ if (!enableApiLevelsForCf) {
+ return Stream.of(new TestParameters(runtime));
+ }
+ return Stream.of(
+ new TestParameters(runtime, AndroidApiLevel.B),
+ new TestParameters(runtime, AndroidApiLevel.LATEST));
+ }
List<AndroidApiLevel> sortedApiLevels =
AndroidApiLevel.getAndroidApiLevelsSorted().stream()
.filter(apiLevelFilter)
@@ -210,9 +229,15 @@
AndroidApiLevel highestApplicable = sortedApiLevels.get(i);
if (highestApplicable.getLevel() <= vmLevel.getLevel()
&& lowestApplicable != highestApplicable) {
- return Stream.of(
- new TestParameters(runtime, lowestApplicable),
- new TestParameters(runtime, highestApplicable));
+ Set<AndroidApiLevel> set = new TreeSet<>();
+ set.add(lowestApplicable);
+ set.add(highestApplicable);
+ for (AndroidApiLevel explicitApiLevel : explicitApiLevels) {
+ if (explicitApiLevel.getLevel() <= vmLevel.getLevel()) {
+ set.add(explicitApiLevel);
+ }
+ }
+ return set.stream().map(api -> new TestParameters(runtime, api));
}
}
}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index f9c1086..9c144c8 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -184,8 +184,7 @@
}
public static boolean verifyValidOutputMode(Backend backend, OutputMode outputMode) {
- return (backend == Backend.CF && outputMode == OutputMode.ClassFile)
- || (backend == Backend.DEX && outputMode != OutputMode.ClassFile);
+ return (backend == Backend.CF && outputMode == OutputMode.ClassFile) || backend == Backend.DEX;
}
public static StringConsumer consumeString(Consumer<String> consumer) {
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 b9883ef..2cb3e95 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/NoRelaxationForSerializableTest.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/NoRelaxationForSerializableTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
@@ -32,7 +33,8 @@
@NeverMerge
class MySerializable implements Serializable {
- transient int value;
+
+ @NeverPropagateValue transient int value;
MySerializable(int value) {
this.value = value;
@@ -169,6 +171,7 @@
testForR8(parameters.getBackend())
.addProgramClasses(CLASSES)
.enableInliningAnnotations()
+ .enableMemberValuePropagationAnnotations()
.addKeepRuleFiles(configuration)
.setMinApi(parameters.getApiLevel())
.compile();
diff --git a/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java b/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
new file mode 100644
index 0000000..6f2e729
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
@@ -0,0 +1,268 @@
+// 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.AppView;
+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.LookupResult;
+import com.android.tools.r8.graph.LookupResult.LookupResultSuccess;
+import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.ProguardConfigurationRule;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+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 KeptTargetsIncompleteDiamondTest extends TestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public KeptTargetsIncompleteDiamondTest(TestParameters parameters) {
+ // Empty to satisfy construction of none-runtime.
+ }
+
+ private AppView<AppInfoWithLiveness> computeAppViewWithLiveness(
+ Class<?> methodToBeKept, Class<?> classToBeKept) throws Exception {
+ return computeAppViewWithLiveness(
+ buildClasses(I.class, J.class, K.class, L.class, A.class).build(),
+ factory -> {
+ List<ProguardConfigurationRule> rules = new ArrayList<>();
+ rules.addAll(buildKeepRuleForClassAndMethods(methodToBeKept, factory));
+ rules.addAll(buildKeepRuleForClass(classToBeKept, factory));
+ return rules;
+ });
+ }
+
+ @Test
+ public void testRefinedReceiverOnStaticTarget() throws Exception {
+ // I { <-- kept
+ // foo() <-- kept, initial
+ // }
+ //
+ // J {
+ // default foo() { ... } <-- kept, resolved
+ // }
+ //
+ // K { }
+ //
+ // L extends I, K { } <-- upperbound
+ //
+ // A implements L { <-- lowerbound
+ //
+ // }
+ AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(I.class, I.class);
+ DexMethod method = buildNullaryVoidMethod(I.class, "foo", appView.dexItemFactory());
+ ResolutionResult resolutionResult = appView.appInfo().resolveMethod(method.holder, method);
+ DexType typeI = buildType(I.class, appView.dexItemFactory());
+ DexType typeL = buildType(L.class, appView.dexItemFactory());
+ DexType typeA = buildType(A.class, appView.dexItemFactory());
+ DexProgramClass classI = appView.definitionForProgramType(typeI);
+ DexProgramClass classL = appView.definitionForProgramType(typeL);
+ DexProgramClass classA = appView.definitionForProgramType(typeA);
+ LookupResult lookupResult =
+ resolutionResult.lookupVirtualDispatchTargets(classI, appView.appInfo(), classL, classA);
+ assertTrue(lookupResult.isLookupResultSuccess());
+ LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
+ Set<String> targets = new HashSet<>();
+ lookupResultSuccess.forEach(
+ methodTarget -> targets.add(methodTarget.asMethodTarget().getMethod().qualifiedName()),
+ lambdaTarget -> {
+ assert false;
+ });
+ Set<String> expected = ImmutableSet.of(J.class.getTypeName() + ".foo");
+ assertEquals(expected, targets);
+ assertTrue(lookupResultSuccess.isComplete());
+ }
+
+ @Test
+ public void testRefinedReceiverOnStaticTargetInRange() throws Exception {
+ // I { <-- kept
+ // foo() <-- kept, initial, upperbound
+ // }
+ //
+ // J {
+ // default foo() { ... } <-- kept, resolved
+ // }
+ //
+ // K { }
+ //
+ // L extends I, K { }
+ //
+ // A implements L { <-- lowerbound
+ //
+ // }
+ AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(I.class, I.class);
+ DexMethod method = buildNullaryVoidMethod(I.class, "foo", appView.dexItemFactory());
+ ResolutionResult resolutionResult = appView.appInfo().resolveMethod(method.holder, method);
+ DexType typeI = buildType(I.class, appView.dexItemFactory());
+ DexType typeL = buildType(L.class, appView.dexItemFactory());
+ DexType typeA = buildType(A.class, appView.dexItemFactory());
+ DexProgramClass classI = appView.definitionForProgramType(typeI);
+ DexProgramClass classA = appView.definitionForProgramType(typeA);
+ LookupResult lookupResult =
+ resolutionResult.lookupVirtualDispatchTargets(classI, appView.appInfo(), classI, classA);
+ assertTrue(lookupResult.isLookupResultSuccess());
+ LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
+ Set<String> targets = new HashSet<>();
+ lookupResultSuccess.forEach(
+ methodTarget -> targets.add(methodTarget.asMethodTarget().getMethod().qualifiedName()),
+ lambdaTarget -> {
+ assert false;
+ });
+ Set<String> expected = ImmutableSet.of(J.class.getTypeName() + ".foo");
+ assertEquals(expected, targets);
+ assertTrue(lookupResultSuccess.isComplete());
+ }
+
+ @Test
+ public void testRefinedReceiverWithKeptDispatchTarget() throws Exception {
+ // I {
+ // foo() <-- initial
+ // }
+ //
+ // J { <-- kept
+ // default foo() { ... } <-- kept, resolved
+ // }
+ //
+ // K { }
+ //
+ // L extends I, K { }
+ //
+ // A implements L { <-- lowerbound, upperbound
+ //
+ // }
+ AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(J.class, J.class);
+ DexMethod method = buildNullaryVoidMethod(I.class, "foo", appView.dexItemFactory());
+ ResolutionResult resolutionResult = appView.appInfo().resolveMethod(method.holder, method);
+ DexType typeI = buildType(I.class, appView.dexItemFactory());
+ DexType typeB = buildType(A.class, appView.dexItemFactory());
+ DexProgramClass classI = appView.definitionForProgramType(typeI);
+ DexProgramClass classA = appView.definitionForProgramType(typeB);
+ LookupResult lookupResult =
+ resolutionResult.lookupVirtualDispatchTargets(classI, appView.appInfo(), classA, classA);
+ assertTrue(lookupResult.isLookupResultSuccess());
+ LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
+ Set<String> targets = new HashSet<>();
+ lookupResultSuccess.forEach(
+ methodTarget -> targets.add(methodTarget.asMethodTarget().getMethod().qualifiedName()),
+ lambdaTarget -> {
+ assert false;
+ });
+ Set<String> expected = ImmutableSet.of(J.class.getTypeName() + ".foo");
+ assertEquals(expected, targets);
+ assertTrue(lookupResultSuccess.isIncomplete());
+ }
+
+ @Test
+ public void testRefinedReceiverWithKeptRefinedReceiver() throws Exception {
+ // I {
+ // foo() <-- initial
+ // }
+ //
+ // J {
+ // default foo() { ... } <-- kept, resolved
+ // }
+ //
+ // K { }
+ //
+ // A implements I, K { <-- kept, lowerbound, upperbound
+ //
+ // }
+ AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(J.class, A.class);
+ DexMethod method = buildNullaryVoidMethod(I.class, "foo", appView.dexItemFactory());
+ ResolutionResult resolutionResult = appView.appInfo().resolveMethod(method.holder, method);
+ DexType typeI = buildType(I.class, appView.dexItemFactory());
+ DexType typeB = buildType(A.class, appView.dexItemFactory());
+ DexProgramClass classI = appView.definitionForProgramType(typeI);
+ DexProgramClass classA = appView.definitionForProgramType(typeB);
+ LookupResult lookupResult =
+ resolutionResult.lookupVirtualDispatchTargets(classI, appView.appInfo(), classA, classA);
+ assertTrue(lookupResult.isLookupResultSuccess());
+ LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
+ Set<String> targets = new HashSet<>();
+ lookupResultSuccess.forEach(
+ methodTarget -> targets.add(methodTarget.asMethodTarget().getMethod().qualifiedName()),
+ lambdaTarget -> {
+ assert false;
+ });
+ Set<String> expected = ImmutableSet.of(J.class.getTypeName() + ".foo");
+ assertEquals(expected, targets);
+ assertTrue(lookupResultSuccess.isIncomplete());
+ }
+
+ @Test
+ public void testRefinedReceiverWithKeptClassOnInitial() throws Exception {
+ // I { <-- kept
+ // foo() <-- kept, initial
+ // }
+ //
+ // J {
+ // default foo() { ... } <-- resolved
+ // }
+ //
+ // K { }
+ //
+ // L extends I, K { }
+ //
+ // A implements L { <-- lowerbound, upperbound
+ //
+ // }
+ AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(I.class, I.class);
+ DexMethod method = buildNullaryVoidMethod(I.class, "foo", appView.dexItemFactory());
+ ResolutionResult resolutionResult = appView.appInfo().resolveMethod(method.holder, method);
+ DexType typeI = buildType(I.class, appView.dexItemFactory());
+ DexType typeB = buildType(A.class, appView.dexItemFactory());
+ DexProgramClass classI = appView.definitionForProgramType(typeI);
+ DexProgramClass classA = appView.definitionForProgramType(typeB);
+ LookupResult lookupResult =
+ resolutionResult.lookupVirtualDispatchTargets(classI, appView.appInfo(), classA, classA);
+ assertTrue(lookupResult.isLookupResultSuccess());
+ LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
+ Set<String> targets = new HashSet<>();
+ lookupResultSuccess.forEach(
+ methodTarget -> targets.add(methodTarget.asMethodTarget().getMethod().qualifiedName()),
+ lambdaTarget -> {
+ assert false;
+ });
+ Set<String> expected = ImmutableSet.of(J.class.getTypeName() + ".foo");
+ assertEquals(expected, targets);
+ assertTrue(lookupResultSuccess.isComplete());
+ }
+
+ public interface I {
+ void foo();
+ }
+
+ public interface J extends I {
+ @Override
+ default void foo() {
+ System.out.println("K.foo");
+ }
+ }
+
+ public interface K extends J {}
+
+ public interface L extends I, K {}
+
+ public static class A implements L {}
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarToClassFile.java b/src/test/java/com/android/tools/r8/desugar/DesugarToClassFile.java
new file mode 100644
index 0000000..ec742ee
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/DesugarToClassFile.java
@@ -0,0 +1,101 @@
+// 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;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.nio.file.Path;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class DesugarToClassFile extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ }
+
+ public DesugarToClassFile(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private void checkSomething(CodeInspector inspector) {
+ ClassSubject classSubject = inspector.clazz(TestClass.class);
+ assertThat(classSubject, isPresent());
+ }
+
+ private void checkDiagnostics(TestDiagnosticMessages messages) {
+ messages.assertOnlyWarnings();
+ messages.assertWarningsCount(1);
+ assertThat(
+ messages.getWarnings().get(0).getDiagnosticMessage(),
+ containsString("not officially supported"));
+ }
+
+ @Test
+ public void test() throws Exception {
+ // Use D8 to desugar with Java classfile output.
+ Path jar =
+ testForD8()
+ .addInnerClasses(DesugarToClassFile.class)
+ .setMinApi(parameters.getApiLevel())
+ .setOutputMode(OutputMode.ClassFile)
+ .compile()
+ .inspectDiagnosticMessages(this::checkDiagnostics)
+ .inspect(this::checkSomething)
+ .writeToZip();
+
+ if (parameters.getRuntime().isCf()) {
+ // Run on the JVM
+ testForJvm()
+ .addProgramFiles(jar)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello, world!", "I::foo");
+ } else {
+ assert parameters.getRuntime().isDex();
+ // Convert to DEX without desugaring.
+ testForD8()
+ .addProgramFiles(jar)
+ .setEnableDesugaring(false)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello, world!", "I::foo");
+ }
+ }
+
+ interface I {
+ default void foo() {
+ System.out.println("I::foo");
+ }
+ }
+
+ static class A implements I {}
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ Runnable runnable =
+ () -> {
+ System.out.println("Hello, world!");
+ };
+ runnable.run();
+
+ new A().foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
index 190e7ee..523a018 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
@@ -17,13 +17,13 @@
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import it.unimi.dsi.fastutil.ints.Int2IntAVLTreeMap;
+import it.unimi.dsi.fastutil.ints.Int2IntSortedMap;
import java.io.IOException;
import java.nio.file.Path;
import java.util.HashSet;
import java.util.List;
-import java.util.NavigableMap;
import java.util.Set;
-import java.util.TreeMap;
import org.junit.Assert;
import org.junit.Test;
@@ -33,7 +33,7 @@
private final Class<?> testClass;
private final Path testJar;
private final String testClassName;
- private final NavigableMap<AndroidApiLevel, Integer> invokeStaticCounts = new TreeMap<>();
+ private final Int2IntSortedMap invokeStaticCounts = new Int2IntAVLTreeMap();
private final Set<String> ignoredInvokes = new HashSet<>();
AbstractBackportTest(TestParameters parameters, Class<?> targetClass,
@@ -64,11 +64,16 @@
}
// Assume all method calls will be rewritten on the lowest API level.
- invokeStaticCounts.put(AndroidApiLevel.B, 0);
+ invokeStaticCounts.put(AndroidApiLevel.B.getLevel(), 0);
}
void registerTarget(AndroidApiLevel apiLevel, int invokeStaticCount) {
- invokeStaticCounts.put(apiLevel, invokeStaticCount);
+ invokeStaticCounts.put(apiLevel.getLevel(), invokeStaticCount);
+ }
+
+ private int getTargetInvokesCount(AndroidApiLevel apiLevel) {
+ int key = invokeStaticCounts.headMap(apiLevel.getLevel() + 1).lastIntKey();
+ return invokeStaticCounts.get(key);
}
void ignoreInvokes(String methodName) {
@@ -119,7 +124,7 @@
.collect(toList());
AndroidApiLevel apiLevel = parameters.getApiLevel();
- long expectedTargetInvokes = invokeStaticCounts.ceilingEntry(apiLevel).getValue();
+ long expectedTargetInvokes = getTargetInvokesCount(apiLevel);
long actualTargetInvokes = javaInvokeStatics.size();
assertEquals("Expected "
+ expectedTargetInvokes
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
index 732c0b9..3cb6218 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
@@ -9,6 +9,7 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -32,7 +33,12 @@
@Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
public static List<Object[]> data() {
return buildParameters(
- BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ BooleanUtils.values(),
+ getTestParameters()
+ .withDexRuntimes()
+ .withAllApiLevels()
+ .withApiLevel(AndroidApiLevel.N)
+ .build());
}
public JavaTimeTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FunctionConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FunctionConversionTest.java
index 687a673..3d1b999 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FunctionConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FunctionConversionTest.java
@@ -4,16 +4,12 @@
package com.android.tools.r8.desugar.desugaredlibrary.conversiontests;
-import static junit.framework.TestCase.assertEquals;
-
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.ir.desugar.DesugaredLibraryWrapperSynthesizer;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import java.nio.file.Path;
import java.util.List;
import java.util.function.BiFunction;
@@ -25,7 +21,6 @@
import java.util.function.IntSupplier;
import java.util.function.LongConsumer;
import java.util.function.LongSupplier;
-import java.util.stream.Collectors;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.BeforeClass;
@@ -75,7 +70,6 @@
.addLibraryClasses(CustomLibClass.class)
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
- .inspect(this::assertSingleWrappers)
.addDesugaredCoreLibraryRunClassPath(
this::buildDesugaredLibrary,
parameters.getApiLevel(),
@@ -107,26 +101,6 @@
.assertSuccessWithOutput(EXPECTED_RESULT);
}
- private void assertSingleWrappers(CodeInspector i) {
- List<FoundClassSubject> intSupplierWrapperClasses =
- i.allClasses().stream()
- .filter(c -> c.getOriginalName().contains("IntSupplier"))
- .collect(Collectors.toList());
- assertEquals(
- "Expected 1 IntSupplier wrapper but got " + intSupplierWrapperClasses,
- 1,
- intSupplierWrapperClasses.size());
-
- List<FoundClassSubject> doubleSupplierWrapperClasses =
- i.allClasses().stream()
- .filter(c -> c.getOriginalName().contains("DoubleSupplier"))
- .collect(Collectors.toList());
- assertEquals(
- "Expected 1 DoubleSupplier wrapper but got " + doubleSupplierWrapperClasses,
- 1,
- doubleSupplierWrapperClasses.size());
- }
-
@Test
public void testWrapperWithChecksum() throws Exception {
Assume.assumeTrue(
@@ -142,7 +116,7 @@
.inspect(
inspector -> {
Assert.assertEquals(
- 8,
+ 9,
inspector.allClasses().stream()
.filter(
clazz ->
@@ -151,7 +125,7 @@
.contains(DesugaredLibraryWrapperSynthesizer.TYPE_WRAPPER_SUFFIX))
.count());
Assert.assertEquals(
- 6,
+ 9,
inspector.allClasses().stream()
.filter(
clazz ->
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperMergeConflictTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperMergeConflictTest.java
deleted file mode 100644
index 26f4629..0000000
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperMergeConflictTest.java
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.desugar.desugaredlibrary.conversiontests;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.TestRuntime.DexRuntime;
-import com.android.tools.r8.ToolHelper.DexVm;
-import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryWrapperSynthesizer;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.InstructionSubject;
-import java.nio.file.Path;
-import java.util.function.IntSupplier;
-import org.junit.Test;
-
-public class WrapperMergeConflictTest extends DesugaredLibraryTestBase {
-
- @Test
- public void testWrapperMergeConflict() throws Exception {
- Path customLib =
- testForD8()
- .addProgramClasses(CustomLibClass.class)
- .setMinApi(AndroidApiLevel.B)
- .compile()
- .writeToZip();
- Path path1 =
- testForD8()
- .addProgramClasses(Executor1.class)
- .addLibraryClasses(CustomLibClass.class)
- .setMinApi(AndroidApiLevel.B)
- .enableCoreLibraryDesugaring(AndroidApiLevel.B)
- .compile()
- .inspect(i -> this.assertWrappers(i, 2))
- .writeToZip();
- Path path2 =
- testForD8()
- .addProgramClasses(Executor2.class)
- .addLibraryClasses(CustomLibClass.class)
- .setMinApi(AndroidApiLevel.B)
- .enableCoreLibraryDesugaring(AndroidApiLevel.B)
- .compile()
- .inspect(i -> this.assertWrappers(i, 1))
- .writeToZip();
- testForD8()
- .addProgramFiles(path1, path2)
- .addLibraryClasses(CustomLibClass.class)
- .compile()
- .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
- .inspect(this::assertBigWrappersPresent)
- .addRunClasspathFiles(customLib)
- .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor1.class)
- .assertSuccessWithOutput(StringUtils.lines("3", "5"));
- }
-
- private void assertBigWrappersPresent(CodeInspector inspector) {
- assertWrappers(inspector, 2);
- inspector.allClasses().stream()
- .filter(
- c -> c.getOriginalName().startsWith(DesugaredLibraryWrapperSynthesizer.WRAPPER_PREFIX))
- .forEach(
- c ->
- assertTrue(
- c.uniqueMethodWithName("convert")
- .streamInstructions()
- .anyMatch(InstructionSubject::isInstanceOf)));
- }
-
- private void assertWrappers(CodeInspector inspector, int num) {
- assertEquals(
- num,
- inspector.allClasses().stream()
- .filter(
- c ->
- c.getOriginalName()
- .startsWith(DesugaredLibraryWrapperSynthesizer.WRAPPER_PREFIX))
- .count());
- }
-
- static class Executor1 {
-
- public static void main(String[] args) {
- // Both wrappers are present.
- CustomLibClass.printInt(() -> 3);
- System.out.println(CustomLibClass.intSupplier().getAsInt());
- }
- }
-
- static class Executor2 {
-
- public static void main(String[] args) {
- // One wrapper is present.
- System.out.println(CustomLibClass.intSupplier().getAsInt());
- }
- }
-
- @SuppressWarnings("WeakerAccess")
- static class CustomLibClass {
-
- public static IntSupplier intSupplier() {
- return () -> 5;
- }
-
- public static void printInt(IntSupplier supplier) {
- System.out.println(supplier.getAsInt());
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11TimeTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11TimeTests.java
index f6a4285..6288598 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11TimeTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11TimeTests.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.D8TestRunResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import java.nio.file.Paths;
@@ -35,6 +36,7 @@
getTestParameters()
.withDexRuntimesStartingFromIncluding(Version.V5_1_1)
.withAllApiLevels()
+ .withApiLevel(AndroidApiLevel.N)
.build());
}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMethodInlinedTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMethodInlinedTest.java
index 4b5e452..4fcfea3 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMethodInlinedTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMethodInlinedTest.java
@@ -44,7 +44,6 @@
testForRuntime(parameters)
.addProgramFiles(classesMatching("NestPvtMethodCallInlined"))
.run(parameters.getRuntime(), getMainClass("pvtCallInlined"))
- .disassemble()
.assertSuccessWithOutput(getExpectedResult("pvtCallInlined"));
}
@@ -74,25 +73,19 @@
int nbDispatchInlining = 0;
int nbNotInlinedPvtCall = 0;
for (FoundClassSubject subj : inspector.allClasses()) {
- // TODO (b/133745203): inline invokeinterface accessed through nest access control.
- // Remove the if.
- if (!subj.getDexClass().isInterface()) {
- assertTrue(
- "nestPvtCallToInline should be inlined (from " + subj.getOriginalName() + ")",
- subj.allMethods().stream()
- .noneMatch(
- method ->
- method.toString().contains("nestPvtCallToInline")
- || method.toString().contains("methodWithPvtCallToInline")));
- }
+ assertTrue(
+ "nestPvtCallToInline should be inlined (from " + subj.getOriginalName() + ")",
+ subj.allMethods().stream()
+ .noneMatch(
+ method ->
+ method.toString().contains("nestPvtCallToInline")
+ || method.toString().contains("methodWithPvtCallToInline")));
// Inlining nest access should transform virtual/ift invokes -> direct.
MethodSubject methodSubject = subj.uniqueMethodWithName("dispatchInlining");
if (methodSubject.isPresent()) {
nbDispatchInlining++;
assertTrue(
methodSubject.streamInstructions().noneMatch(InstructionSubject::isInvokeVirtual));
- // TODO (b/133745203): inline invokeinterface accessed through nest access control.
- // Also assert no invokeInterface
}
methodSubject = subj.uniqueMethodWithName("notInlinedPvtCall");
if (methodSubject.isPresent()) {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java
index 399b6b0..1b56b1b 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java
@@ -68,6 +68,7 @@
void enableEnumOptions(InternalOptions options, boolean enumValueOptimization) {
options.enableEnumUnboxing = true;
options.enableEnumValueOptimization = enumValueOptimization;
+ options.enableEnumSwitchMapRemoval = enumValueOptimization;
options.testing.enableEnumUnboxingDebugLogs = true;
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/SwitchEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/SwitchEnumUnboxingTest.java
index 4bfa568..b7c7005 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/SwitchEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/SwitchEnumUnboxingTest.java
@@ -50,7 +50,13 @@
.setMinApi(parameters.getApiLevel())
.compile()
.inspectDiagnosticMessages(
- m -> assertEnumIsBoxed(ENUM_CLASS, classToTest.getSimpleName(), m))
+ m -> {
+ if (enumValueOptimization) {
+ assertEnumIsUnboxed(ENUM_CLASS, classToTest.getSimpleName(), m);
+ } else {
+ assertEnumIsBoxed(ENUM_CLASS, classToTest.getSimpleName(), m);
+ }
+ })
.run(parameters.getRuntime(), classToTest)
.assertSuccess();
assertLines2By2Correct(run.getStdOut());
diff --git a/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java b/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
index 710e8da..775f030 100644
--- a/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -15,6 +16,7 @@
import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.GenericSignature.Parser;
+import com.android.tools.r8.graph.GenericSignature.ReturnType;
import com.android.tools.r8.graph.GenericSignature.TypeSignature;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.AndroidApp;
@@ -50,17 +52,25 @@
assertThat(yy, isPresent());
ClassSubject zz = inspector.clazz(A.Y.ZZ.class);
assertThat(zz, isPresent());
+ ClassSubject b = inspector.clazz(B.class);
+ assertThat(b, isPresent());
ClassSubject cy = inspector.clazz(CY.class);
assertThat(cy, isPresent());
ClassSubject cyy = inspector.clazz(CYY.class);
assertThat(cyy, isPresent());
+ DexEncodedMethod method;
+
ClassSignature classSignature;
ClassTypeSignature classTypeSignature;
FieldTypeSignature fieldTypeSignature;
MethodTypeSignature methodTypeSignature;
List<FieldTypeSignature> typeArguments;
FieldTypeSignature typeArgument;
+ TypeSignature parameterSignature;
+ TypeSignature elementSignature;
+ ReturnType returnType;
+ TypeSignature returnTypeSignature;
//
// Testing ClassSignature
@@ -103,22 +113,35 @@
// Testing MethodTypeSignature
//
- // A$Y$YY newYY()
+ // A$Y$YY newYY([B<T>)
MethodSubject newYY = zz.uniqueMethodWithName("newYY");
assertThat(newYY, isPresent());
- DexEncodedMethod method = newYY.getMethod();
+ method = newYY.getMethod();
assertNotNull(method);
methodTypeSignature = Parser.toMethodTypeSignature(method, appView);
assertNotNull(methodTypeSignature);
- assertTrue(methodTypeSignature.typeSignatures.isEmpty());
-
// return type: A$Y$YY
- TypeSignature returnType = methodTypeSignature.returnType();
- assertTrue(returnType.isFieldTypeSignature());
- assertTrue(returnType.asFieldTypeSignature().isClassTypeSignature());
- check_A_Y_YY(a, y, yy, returnType.asFieldTypeSignature().asClassTypeSignature());
+ returnType = methodTypeSignature.returnType();
+ assertFalse(returnType.isVoidDescriptor());
+ returnTypeSignature = returnType.typeSignature();
+ assertTrue(returnTypeSignature.isFieldTypeSignature());
+ assertTrue(returnTypeSignature.asFieldTypeSignature().isClassTypeSignature());
+ check_A_Y_YY(a, y, yy, returnTypeSignature.asFieldTypeSignature().asClassTypeSignature());
+
+ // type of 1st argument: [B<T>
+ assertEquals(1, methodTypeSignature.typeSignatures.size());
+ parameterSignature = methodTypeSignature.getParameterTypeSignature(0);
+ assertNotNull(parameterSignature);
+ assertTrue(parameterSignature.isFieldTypeSignature());
+ assertTrue(parameterSignature.asFieldTypeSignature().isArrayTypeSignature());
+ elementSignature =
+ parameterSignature.asFieldTypeSignature().asArrayTypeSignature().elementSignature;
+ assertTrue(elementSignature.isFieldTypeSignature());
+ assertTrue(elementSignature.asFieldTypeSignature().isClassTypeSignature());
+ classTypeSignature = elementSignature.asFieldTypeSignature().asClassTypeSignature();
+ assertEquals(b.getDexClass().type, classTypeSignature.type);
// Function<A$Y$ZZ<TT>, A$Y$YY> convertToYY(Supplier<A$Y$ZZ<TT>>
MethodSubject convertToYY = zz.uniqueMethodWithName("convertToYY");
@@ -131,9 +154,11 @@
// return type: Function<A$Y$ZZ<TT>, A$Y$YY>
returnType = methodTypeSignature.returnType();
- assertTrue(returnType.isFieldTypeSignature());
- assertTrue(returnType.asFieldTypeSignature().isClassTypeSignature());
- classTypeSignature = returnType.asFieldTypeSignature().asClassTypeSignature();
+ assertFalse(returnType.isVoidDescriptor());
+ returnTypeSignature = returnType.typeSignature();
+ assertTrue(returnTypeSignature.isFieldTypeSignature());
+ assertTrue(returnTypeSignature.asFieldTypeSignature().isClassTypeSignature());
+ classTypeSignature = returnTypeSignature.asFieldTypeSignature().asClassTypeSignature();
DexType functionType =
factory.createType(DescriptorUtils.javaTypeToDescriptor(Function.class.getTypeName()));
assertEquals(functionType, classTypeSignature.type);
@@ -151,19 +176,25 @@
// type of 1st argument: Supplier<A$Y$ZZ<TT>>
assertEquals(1, methodTypeSignature.typeSignatures.size());
- TypeSignature parameterSignature = methodTypeSignature.getParameterTypeSignature(0);
- assertNotNull(parameterSignature);
- assertTrue(parameterSignature.isFieldTypeSignature());
- assertTrue(parameterSignature.asFieldTypeSignature().isClassTypeSignature());
- classTypeSignature = parameterSignature.asFieldTypeSignature().asClassTypeSignature();
- DexType supplierType =
- factory.createType(DescriptorUtils.javaTypeToDescriptor(Supplier.class.getTypeName()));
- assertEquals(supplierType, classTypeSignature.type);
- typeArguments = classTypeSignature.typeArguments;
- assertEquals(1, typeArguments.size());
- typeArgument = typeArguments.get(0);
- assertTrue(typeArgument.isClassTypeSignature());
- check_A_Y_ZZ(a, y, zz, typeArgument.asClassTypeSignature());
+ parameterSignature = methodTypeSignature.getParameterTypeSignature(0);
+ check_supplier(factory, a, y, zz, parameterSignature);
+
+ // void boo(Supplier<A$Y$ZZ<TT>>)
+ MethodSubject boo = zz.uniqueMethodWithName("boo");
+ assertThat(boo, isPresent());
+ method = boo.getMethod();
+ assertNotNull(method);
+
+ // return type: void
+ methodTypeSignature = Parser.toMethodTypeSignature(method, appView);
+ assertNotNull(methodTypeSignature);
+ returnType = methodTypeSignature.returnType();
+ assertTrue(returnType.isVoidDescriptor());
+
+ // type of 1st argument: Supplier<A$Y$ZZ<TT>>
+ assertEquals(1, methodTypeSignature.typeSignatures.size());
+ parameterSignature = methodTypeSignature.getParameterTypeSignature(0);
+ check_supplier(factory, a, y, zz, parameterSignature);
}
private void check_A_Y(ClassSubject a, ClassSubject y, ClassTypeSignature signature) {
@@ -193,6 +224,26 @@
assertTrue(typeArgument.isTypeVariableSignature());
assertEquals("TT", typeArgument.asTypeVariableSignature().typeVariable);
}
+
+ private void check_supplier(
+ DexItemFactory factory,
+ ClassSubject a,
+ ClassSubject y,
+ ClassSubject zz,
+ TypeSignature signature) {
+ assertNotNull(signature);
+ assertTrue(signature.isFieldTypeSignature());
+ assertTrue(signature.asFieldTypeSignature().isClassTypeSignature());
+ ClassTypeSignature classTypeSignature = signature.asFieldTypeSignature().asClassTypeSignature();
+ DexType supplierType =
+ factory.createType(DescriptorUtils.javaTypeToDescriptor(Supplier.class.getTypeName()));
+ assertEquals(supplierType, classTypeSignature.type);
+ List<FieldTypeSignature> typeArguments = classTypeSignature.typeArguments;
+ assertEquals(1, typeArguments.size());
+ FieldTypeSignature typeArgument = typeArguments.get(0);
+ assertTrue(typeArgument.isClassTypeSignature());
+ check_A_Y_ZZ(a, y, zz, typeArgument.asClassTypeSignature());
+ }
}
//
@@ -209,7 +260,7 @@
class ZZ<TT> extends YY {
public YY yy;
- YY newYY() {
+ YY newYY(B... bs) {
return new YY();
}
@@ -222,6 +273,10 @@
}
};
}
+
+ void boo(Supplier<ZZ<TT>> zzSupplier) {
+ convertToYY(zzSupplier).apply(this);
+ }
}
ZZ<T> zz() {
diff --git a/src/test/java/com/android/tools/r8/internal/Gmail18082615TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/Gmail18082615TreeShakeJarVerificationTest.java
index 7d04499..183ee3e 100644
--- a/src/test/java/com/android/tools/r8/internal/Gmail18082615TreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/Gmail18082615TreeShakeJarVerificationTest.java
@@ -45,13 +45,14 @@
.addKeepRuleFiles(
Paths.get(base).resolve(BASE_PG_CONF),
Paths.get(ToolHelper.PROGUARD_SETTINGS_FOR_INTERNAL_APPS, PG_CONF))
- .allowDiagnosticInfoMessages()
+ .allowDiagnosticMessages()
.allowUnusedProguardConfigurationRules()
.compile()
.assertAllInfoMessagesMatch(
anyOf(
equalTo("Ignoring option: -optimizations"),
- containsString("Proguard configuration rule does not match anything")));
+ containsString("Proguard configuration rule does not match anything")))
+ .assertAllWarningMessagesMatch(containsString("Ignoring option:"));
int appSize = compileResult.app.applicationSize();
assertTrue("Expected max size of " + MAX_SIZE+ ", got " + appSize, appSize < MAX_SIZE);
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
index 88251be..741be70 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
@@ -4,7 +4,10 @@
package com.android.tools.r8.internal;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import com.android.tools.r8.StringResource;
import com.android.tools.r8.TestBase;
@@ -18,6 +21,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.LookupResult;
+import com.android.tools.r8.graph.LookupResult.LookupResultSuccess;
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
@@ -25,7 +29,6 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.junit.Before;
@@ -70,28 +73,49 @@
ResolutionResult resolutionResult = appInfo().resolveMethodOnClass(clazz, method.method);
LookupResult lookupResult =
resolutionResult.lookupVirtualDispatchTargets(
- clazz, appView, appInfo(), dexReference -> false);
+ clazz, appInfo(), appInfo(), dexReference -> false);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<DexEncodedMethod> targets = lookupResult.asLookupResultSuccess().getMethodTargets();
- assertTrue(targets.contains(method));
+ assertTrue(lookupResult.asLookupResultSuccess().contains(method));
+ }
+
+ private static class Counter {
+ int count = 0;
+
+ void inc() {
+ count++;
+ }
}
private void testInterfaceLookup(DexProgramClass clazz, DexEncodedMethod method) {
- LookupResult lookupResult =
+ LookupResultSuccess lookupResult =
appInfo()
.resolveMethodOnInterface(clazz, method.method)
- .lookupVirtualDispatchTargets(clazz, appView, appInfo(), dexReference -> false);
- assertTrue(lookupResult.isLookupResultSuccess());
- Set<DexEncodedMethod> targets = lookupResult.asLookupResultSuccess().getMethodTargets();
+ .lookupVirtualDispatchTargets(clazz, appInfo(), appInfo(), dexReference -> false)
+ .asLookupResultSuccess();
+ assertNotNull(lookupResult);
+ assertFalse(lookupResult.hasLambdaTargets());
if (appInfo().subtypes(method.method.holder).stream()
.allMatch(t -> appInfo().definitionFor(t).isInterface())) {
- assertEquals(
- 0,
- targets.stream()
- .filter(m -> m.accessFlags.isAbstract() || !m.accessFlags.isBridge())
- .count());
+ Counter counter = new Counter();
+ lookupResult.forEach(
+ target -> {
+ DexEncodedMethod m = target.getMethod();
+ if (m.accessFlags.isAbstract() || !m.accessFlags.isBridge()) {
+ counter.inc();
+ }
+ },
+ l -> fail());
+ assertEquals(0, counter.count);
} else {
- assertEquals(0, targets.stream().filter(m -> m.accessFlags.isAbstract()).count());
+ Counter counter = new Counter();
+ lookupResult.forEach(
+ target -> {
+ if (target.getMethod().isAbstract()) {
+ counter.inc();
+ }
+ },
+ lambda -> fail());
+ assertEquals(0, counter.count);
}
}
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeCompilationBase.java b/src/test/java/com/android/tools/r8/internal/YouTubeCompilationBase.java
index 57f4359..a6dd6b0 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeCompilationBase.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeCompilationBase.java
@@ -21,6 +21,7 @@
static final String PG_JAR = "YouTubeRelease_proguard.jar";
static final String PG_MAP = "YouTubeRelease_proguard.map";
static final String PG_CONF = "YouTubeRelease_proguard.config";
+ static final String PG_PROTO_CONF = "YouTubeRelease_proto_safety.pgconf";
final String base;
@@ -34,9 +35,14 @@
}
protected List<Path> getKeepRuleFiles() {
- return ImmutableList.of(
- Paths.get(base).resolve(PG_CONF),
- Paths.get(ToolHelper.PROGUARD_SETTINGS_FOR_INTERNAL_APPS).resolve(PG_CONF));
+ ImmutableList.Builder<Path> builder = ImmutableList.builder();
+ builder.add(Paths.get(base).resolve(PG_CONF));
+ builder.add(Paths.get(ToolHelper.PROGUARD_SETTINGS_FOR_INTERNAL_APPS).resolve(PG_CONF));
+ Path config = Paths.get(base).resolve(PG_PROTO_CONF);
+ if (config.toFile().exists()) {
+ builder.add(config);
+ }
+ return builder.build();
}
protected List<Path> getLibraryFiles() {
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1419TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1419TreeShakeJarVerificationTest.java
index f20a827..48acfdb 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1419TreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1419TreeShakeJarVerificationTest.java
@@ -46,23 +46,6 @@
R8TestCompileResult compileResult =
testForR8(parameters.getBackend())
.addKeepRuleFiles(getKeepRuleFiles())
- .addOptionsModification(
- options -> {
- assert !options.enableFieldBitAccessAnalysis;
- options.enableFieldBitAccessAnalysis = true;
-
- assert !options.protoShrinking().enableGeneratedExtensionRegistryShrinking;
- options.protoShrinking().enableGeneratedExtensionRegistryShrinking = true;
-
- assert !options.protoShrinking().enableGeneratedMessageLiteShrinking;
- options.protoShrinking().enableGeneratedMessageLiteShrinking = true;
-
- assert options.protoShrinking().traverseOneOfAndRepeatedProtoFields;
- options.protoShrinking().traverseOneOfAndRepeatedProtoFields = false;
-
- assert !options.enableStringSwitchConversion;
- options.enableStringSwitchConversion = true;
- })
.allowUnusedProguardConfigurationRules()
.compile();
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1444TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1444TreeShakeJarVerificationTest.java
index ab63ba7..b2498b5 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1444TreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1444TreeShakeJarVerificationTest.java
@@ -59,26 +59,6 @@
.addLibraryFiles(librarySanitizer.getSanitizedLibrary())
.addKeepRuleFiles(getKeepRuleFiles())
.addMainDexRuleFiles(getMainDexRuleFiles())
- .addOptionsModification(
- options -> {
- assert !options.applyInliningToInlinee;
- options.applyInliningToInlinee = true;
-
- assert !options.enableFieldBitAccessAnalysis;
- options.enableFieldBitAccessAnalysis = true;
-
- assert !options.protoShrinking().enableGeneratedExtensionRegistryShrinking;
- options.protoShrinking().enableGeneratedExtensionRegistryShrinking = true;
-
- assert !options.protoShrinking().enableGeneratedMessageLiteShrinking;
- options.protoShrinking().enableGeneratedMessageLiteShrinking = true;
-
- assert options.protoShrinking().traverseOneOfAndRepeatedProtoFields;
- options.protoShrinking().traverseOneOfAndRepeatedProtoFields = false;
-
- assert !options.enableStringSwitchConversion;
- options.enableStringSwitchConversion = true;
- })
.setMinApi(AndroidApiLevel.H_MR2)
.allowDiagnosticMessages()
.allowUnusedProguardConfigurationRules()
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1508TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1508TreeShakeJarVerificationTest.java
index 01120c8..e63b126 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1508TreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1508TreeShakeJarVerificationTest.java
@@ -59,26 +59,6 @@
.addLibraryFiles(librarySanitizer.getSanitizedLibrary())
.addKeepRuleFiles(getKeepRuleFiles())
.addMainDexRuleFiles(getMainDexRuleFiles())
- .addOptionsModification(
- options -> {
- assert !options.applyInliningToInlinee;
- options.applyInliningToInlinee = true;
-
- assert !options.enableFieldBitAccessAnalysis;
- options.enableFieldBitAccessAnalysis = true;
-
- assert !options.protoShrinking().enableGeneratedExtensionRegistryShrinking;
- options.protoShrinking().enableGeneratedExtensionRegistryShrinking = true;
-
- assert !options.protoShrinking().enableGeneratedMessageLiteShrinking;
- options.protoShrinking().enableGeneratedMessageLiteShrinking = true;
-
- assert options.protoShrinking().traverseOneOfAndRepeatedProtoFields;
- options.protoShrinking().traverseOneOfAndRepeatedProtoFields = false;
-
- assert !options.enableStringSwitchConversion;
- options.enableStringSwitchConversion = true;
- })
.allowCheckDiscardedErrors()
.allowDiagnosticMessages()
.allowUnusedProguardConfigurationRules()
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
index 0788985..23dc574 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
@@ -72,30 +72,11 @@
.addProgramFiles(PROGRAM_FILES)
.addKeepMainRules(mains)
.addKeepRuleFiles(PROTOBUF_LITE_PROGUARD_RULES)
- .addOptionsModification(
- options -> {
- assert !options.applyInliningToInlinee;
- options.applyInliningToInlinee = true;
-
- assert !options.enableFieldBitAccessAnalysis;
- options.enableFieldBitAccessAnalysis = true;
-
- assert !options.protoShrinking().enableGeneratedExtensionRegistryShrinking;
- options.protoShrinking().enableGeneratedExtensionRegistryShrinking = true;
-
- assert !options.protoShrinking().enableGeneratedMessageLiteShrinking;
- options.protoShrinking().enableGeneratedMessageLiteShrinking = true;
-
- assert !options.protoShrinking().enableGeneratedMessageLiteBuilderShrinking;
- options.protoShrinking().enableGeneratedMessageLiteBuilderShrinking = true;
-
- assert !options.enableStringSwitchConversion;
- options.enableStringSwitchConversion = true;
- })
.allowAccessModification()
.allowDiagnosticMessages()
.allowUnusedProguardConfigurationRules()
.enableInliningAnnotations()
+ .enableProtoShrinking()
.setMinApi(parameters.getApiLevel())
.compile()
.assertAllInfoMessagesMatch(
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
index 1473e03..cd743a9 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
@@ -82,17 +82,10 @@
.addKeepMainRule("proto2.TestClass")
.addKeepRuleFiles(PROTOBUF_LITE_PROGUARD_RULES)
.addKeepRules(allGeneratedMessageLiteSubtypesAreInstantiatedRule())
- .addOptionsModification(
- options -> {
- options.enableFieldBitAccessAnalysis = true;
- options.protoShrinking().enableGeneratedExtensionRegistryShrinking = true;
- options.protoShrinking().enableGeneratedMessageLiteShrinking = true;
- options.protoShrinking().enableGeneratedMessageLiteBuilderShrinking = true;
- options.enableStringSwitchConversion = true;
- })
.allowAccessModification(allowAccessModification)
.allowDiagnosticMessages()
.allowUnusedProguardConfigurationRules()
+ .enableProtoShrinking()
.minification(enableMinification)
.setMinApi(parameters.getApiLevel())
.compile()
@@ -357,15 +350,10 @@
.addKeepRules(keepAllProtosRule())
// Retain the signature of dynamicMethod() and newMessageInfo().
.addKeepRules(keepDynamicMethodSignatureRule(), keepNewMessageInfoSignatureRule())
- // Enable the dynamicMethod() rewritings.
- .addOptionsModification(
- options -> {
- assert !options.protoShrinking().enableGeneratedMessageLiteShrinking;
- options.protoShrinking().enableGeneratedMessageLiteShrinking = true;
- })
.allowAccessModification(allowAccessModification)
.allowDiagnosticMessages()
.allowUnusedProguardConfigurationRules()
+ .enableProtoShrinking()
.minification(enableMinification)
.setMinApi(parameters.getApiLevel())
.compile()
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java
index 7a261ff..429c340 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java
@@ -58,17 +58,10 @@
.addProgramFiles(PROGRAM_FILES)
.addKeepMainRule("proto3.TestClass")
.addKeepRuleFiles(PROTOBUF_LITE_PROGUARD_RULES)
- .addOptionsModification(
- options -> {
- options.enableFieldBitAccessAnalysis = true;
- options.protoShrinking().enableGeneratedExtensionRegistryShrinking = true;
- options.protoShrinking().enableGeneratedMessageLiteShrinking = true;
- options.protoShrinking().enableGeneratedMessageLiteBuilderShrinking = true;
- options.enableStringSwitchConversion = true;
- })
.allowAccessModification(allowAccessModification)
.allowDiagnosticMessages()
.allowUnusedProguardConfigurationRules()
+ .enableProtoShrinking()
.minification(enableMinification)
.setMinApi(parameters.getApiLevel())
.compile()
@@ -113,15 +106,10 @@
.addKeepRules(keepAllProtosRule())
// Retain the signature of dynamicMethod() and newMessageInfo().
.addKeepRules(keepDynamicMethodSignatureRule(), keepNewMessageInfoSignatureRule())
- // Enable the dynamicMethod() rewritings.
- .addOptionsModification(
- options -> {
- assert !options.protoShrinking().enableGeneratedMessageLiteShrinking;
- options.protoShrinking().enableGeneratedMessageLiteShrinking = true;
- })
.allowAccessModification(allowAccessModification)
.allowDiagnosticMessages()
.allowUnusedProguardConfigurationRules()
+ .enableProtoShrinking()
.minification(enableMinification)
.setMinApi(parameters.getApiLevel())
.compile()
diff --git a/src/test/java/com/android/tools/r8/internal/proto/YouTubeProtoRewritingTest.java b/src/test/java/com/android/tools/r8/internal/proto/YouTubeProtoRewritingTest.java
index cff1e45..6088600 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/YouTubeProtoRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/YouTubeProtoRewritingTest.java
@@ -46,12 +46,6 @@
.addKeepRules(keepAllProtosRule())
// Retain the signature of dynamicMethod() and newMessageInfo().
.addKeepRules(keepDynamicMethodSignatureRule(), keepNewMessageInfoSignatureRule())
- // Enable the dynamicMethod() rewritings.
- .addOptionsModification(
- options -> {
- assert !options.protoShrinking().enableGeneratedMessageLiteShrinking;
- options.protoShrinking().enableGeneratedMessageLiteShrinking = true;
- })
.allowUnusedProguardConfigurationRules()
.compile()
.inspect(
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/PutObjectWithFinalizeTest.java b/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/PutObjectWithFinalizeTest.java
index d133adf..c4d321a 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/PutObjectWithFinalizeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/PutObjectWithFinalizeTest.java
@@ -32,7 +32,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public PutObjectWithFinalizeTest(TestParameters parameters) {
@@ -48,7 +48,7 @@
.addOptionsModification(options -> options.enableClassStaticizer = false)
.enableInliningAnnotations()
.enableMergeAnnotations()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile()
.inspect(
inspector -> {
@@ -70,7 +70,7 @@
"otherArrayInstanceWithoutFinalizer");
for (String name : presentFields) {
FieldSubject fieldSubject = classSubject.uniqueFieldWithName(name);
- assertThat(fieldSubject, isPresent());
+ assertThat(name, fieldSubject, isPresent());
assertTrue(
mainSubject
.streamInstructions()
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/SingletonClassInitializerPatternCanBePostponedTest.java b/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/SingletonClassInitializerPatternCanBePostponedTest.java
index 7813a2d..9461c6b 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/SingletonClassInitializerPatternCanBePostponedTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/SingletonClassInitializerPatternCanBePostponedTest.java
@@ -8,6 +8,7 @@
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -37,6 +38,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(SingletonClassInitializerPatternCanBePostponedTest.class)
.addKeepMainRule(TestClass.class)
+ .enableMemberValuePropagationAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
@@ -65,7 +67,7 @@
static A INSTANCE = new A(" world!");
- final String message;
+ @NeverPropagateValue final String message;
A(String message) {
this.message = message;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/ObjectsRequireNonNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/ObjectsRequireNonNullTest.java
index a963edf..a1c69fa 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/ObjectsRequireNonNullTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/ObjectsRequireNonNullTest.java
@@ -191,7 +191,8 @@
unknownArg(instance);
try {
- unknownArg(null);
+ Foo alwaysNull = System.currentTimeMillis() > 0 ? null : instance;
+ unknownArg(alwaysNull);
throw new AssertionError("Expected NullPointerException");
} catch (NullPointerException npe) {
System.out.println("Expected NPE");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CheckCastInterfaceArrayTest.java b/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CheckCastInterfaceArrayTest.java
index 819c573..a8ef7a1 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CheckCastInterfaceArrayTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CheckCastInterfaceArrayTest.java
@@ -40,7 +40,6 @@
.addInnerClasses(CheckCastInterfaceArrayTest.class)
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), TestClass.class)
- .disassemble()
.assertSuccessWithOutput(EXPECTED);
}
}
@@ -55,7 +54,6 @@
.noMinification()
.noTreeShaking()
.compile()
- .disassemble()
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED);
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/ifs/IfThrowNullPointerExceptionTest.java b/src/test/java/com/android/tools/r8/ir/optimize/ifs/IfThrowNullPointerExceptionTest.java
new file mode 100644
index 0000000..2264a29
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/ifs/IfThrowNullPointerExceptionTest.java
@@ -0,0 +1,129 @@
+// 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.ifs;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+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.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.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 org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class IfThrowNullPointerExceptionTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection params() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public IfThrowNullPointerExceptionTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8()
+ .addInnerClasses(IfThrowNullPointerExceptionTest.class)
+ .release()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Caught NPE", "Caught NPE");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(IfThrowNullPointerExceptionTest.class)
+ .addKeepClassAndMembersRules(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Caught NPE", "Caught NPE");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject classSubject = inspector.clazz(TestClass.class);
+ assertThat(classSubject, isPresent());
+
+ for (String methodName : ImmutableList.of("testThrowNPE", "testThrowNull")) {
+ MethodSubject methodSubject = classSubject.uniqueMethodWithName(methodName);
+ assertThat(methodSubject, isPresent());
+
+ IRCode code = methodSubject.buildIR();
+ assertEquals(1, code.blocks.size());
+
+ BasicBlock entryBlock = code.entryBlock();
+ assertEquals(3, entryBlock.getInstructions().size());
+ assertTrue(entryBlock.getInstructions().getFirst().isArgument());
+ assertTrue(entryBlock.getInstructions().getLast().isReturn());
+
+ Instruction nullCheckInstruction = entryBlock.getInstructions().get(1);
+ if (parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.K)) {
+ assertTrue(nullCheckInstruction.isInvokeVirtual());
+ assertEquals(
+ "java.lang.Class java.lang.Object.getClass()",
+ nullCheckInstruction.asInvokeVirtual().getInvokedMethod().toSourceString());
+ } else {
+ assertTrue(nullCheckInstruction.isInvokeStatic());
+ assertEquals(
+ "java.lang.Object java.util.Objects.requireNonNull(java.lang.Object)",
+ nullCheckInstruction.asInvokeStatic().getInvokedMethod().toSourceString());
+ }
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ testThrowNPE(new Object());
+ testThrowNull(new Object());
+
+ try {
+ testThrowNPE(null);
+ } catch (NullPointerException e) {
+ System.out.println("Caught NPE");
+ }
+ try {
+ testThrowNull(null);
+ } catch (NullPointerException e) {
+ System.out.println("Caught NPE");
+ }
+ }
+
+ static void testThrowNPE(Object x) {
+ if (x == null) {
+ throw new NullPointerException();
+ }
+ }
+
+ static void testThrowNull(Object x) {
+ if (x == null) {
+ throw null;
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FinalFieldWithDefaultValueAssignmentPropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FinalFieldWithDefaultValueAssignmentPropagationTest.java
index 8710a6a..97dd34c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FinalFieldWithDefaultValueAssignmentPropagationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FinalFieldWithDefaultValueAssignmentPropagationTest.java
@@ -7,7 +7,7 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
@@ -59,8 +59,7 @@
MethodSubject configConstructorSubject = configClassSubject.init();
assertThat(configConstructorSubject, isPresent());
- // TODO(b/147799637): Should be true.
- assertFalse(
+ assertTrue(
configConstructorSubject.streamInstructions().noneMatch(InstructionSubject::isInstancePut));
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationTest.java
index 2c8f4b3..0745620 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationTest.java
@@ -6,7 +6,6 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.NeverClassInline;
@@ -88,9 +87,7 @@
ClassSubject aClassSubject = inspector.clazz(A.class);
assertThat(aClassSubject, isPresent());
- // TODO(b/125282093): Need to remove the instance-put instructions in A.<init>(). This can not
- // be done safely by the time we process A.<init>(), so some kind of post-processing is needed.
- assertEquals(3, aClassSubject.allInstanceFields().size());
+ assertTrue(aClassSubject.allInstanceFields().isEmpty());
}
static class TestClass {
@@ -125,7 +122,7 @@
String s = "Hello world!";
}
- enum MyEnum {
+ public enum MyEnum {
A,
B
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/MemberValuePropagationWithClassInitializationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/MemberValuePropagationWithClassInitializationTest.java
index 9c32e92..adc1f0b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/MemberValuePropagationWithClassInitializationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/MemberValuePropagationWithClassInitializationTest.java
@@ -56,7 +56,10 @@
assertThat(aClassSubject, isPresent());
FieldSubject fieldSubject = aClassSubject.uniqueFieldWithName("field");
- assertThat(fieldSubject, isPresent());
+ assertThat(fieldSubject, not(isPresent()));
+
+ FieldSubject clinitFieldSubject = aClassSubject.uniqueFieldWithName("$r8$clinit");
+ assertThat(clinitFieldSubject, isPresent());
// B.method() is present.
ClassSubject bClassSubject = inspector.clazz(B.class);
@@ -81,7 +84,7 @@
mainMethodSubject
.streamInstructions()
.filter(InstructionSubject::isStaticGet)
- .anyMatch(x -> x.getField() == fieldSubject.getField().field));
+ .anyMatch(x -> x.getField() == clinitFieldSubject.getField().field));
assertTrue(
mainMethodSubject
.streamInstructions()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonFinalFieldWithDefaultValueAssignmentPropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonFinalFieldWithDefaultValueAssignmentPropagationTest.java
index f68a131..433ee13 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonFinalFieldWithDefaultValueAssignmentPropagationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonFinalFieldWithDefaultValueAssignmentPropagationTest.java
@@ -7,7 +7,7 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
@@ -61,8 +61,7 @@
MethodSubject configConstructorSubject = configClassSubject.init();
assertThat(configConstructorSubject, isPresent());
- // TODO(b/147799637): Should be true.
- assertFalse(
+ assertTrue(
configConstructorSubject.streamInstructions().noneMatch(InstructionSubject::isInstancePut));
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/LambdaInstantiatedTypeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/LambdaInstantiatedTypeTest.java
index 575580d..1b3e93e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/LambdaInstantiatedTypeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/LambdaInstantiatedTypeTest.java
@@ -8,7 +8,9 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
-import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersBuilder;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -24,29 +26,36 @@
@RunWith(Parameterized.class)
public class LambdaInstantiatedTypeTest extends TestBase {
- private final Backend backend;
+ private final TestParameters parameters;
- @Parameters(name = "Backend: {0}")
- public static Backend[] data() {
- return ToolHelper.getBackends();
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return TestParametersBuilder.builder().withAllRuntimesAndApiLevels().build();
}
- public LambdaInstantiatedTypeTest(Backend backend) {
- this.backend = backend;
+ public LambdaInstantiatedTypeTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ String expected = StringUtils.joinLines("In TestClass.live()");
+
+ @Test
+ public void testReference() throws Exception {
+ testForRuntime(parameters)
+ .addInnerClasses(LambdaInstantiatedTypeTest.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(expected);
}
@Test
public void test() throws Exception {
- String expected = StringUtils.joinLines("In TestClass.live()");
-
- testForJvm().addTestClasspath().run(TestClass.class).assertSuccessWithOutput(expected);
-
CodeInspector inspector =
- testForR8(backend)
+ testForR8(parameters.getBackend())
.addInnerClasses(LambdaInstantiatedTypeTest.class)
.addKeepMainRule(TestClass.class)
.enableInliningAnnotations()
- .run(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(expected)
.inspector();
diff --git a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
index 483b3aa..e29373a 100644
--- a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
import org.junit.Test;
@@ -38,9 +39,12 @@
private void test(
Collection<String> rules, ThrowableConsumer<R8FullTestBuilder> consumer) throws Exception {
testForR8(parameters.getBackend())
- .addLibraryFiles(ToolHelper.getDefaultAndroidJar(), ToolHelper.getKotlinStdlibJar())
+ .addLibraryFiles(ToolHelper.getMostRecentAndroidJar(), ToolHelper.getKotlinStdlibJar())
.addProgramFiles(ToolHelper.getKotlinReflectJar())
.addKeepRules(rules)
+ .addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
+ .addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
+ .addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
.apply(consumer)
.compile();
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
index 956c37b..4c45d3c 100644
--- a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
import org.junit.Test;
@@ -39,6 +40,9 @@
testForR8(parameters.getBackend())
.addProgramFiles(ToolHelper.getKotlinStdlibJar())
.addKeepRules(rules)
+ .addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
+ .addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
+ .addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
.apply(consumer)
.compile();
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java b/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
index 562fdbb..1720b55 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
@@ -15,4 +15,14 @@
static final String PKG = KotlinMetadataTestBase.class.getPackage().getName();
static final String PKG_PREFIX = DescriptorUtils.getBinaryNameFromJavaType(PKG);
+
+ static final String KT_ARRAY = "Lkotlin/Array;";
+ static final String KT_CHAR_SEQUENCE = "Lkotlin/CharSequence;";
+ static final String KT_STRING = "Lkotlin/String;";
+ static final String KT_LONG = "Lkotlin/Long;";
+ static final String KT_LONG_ARRAY = "Lkotlin/LongArray;";
+ static final String KT_MAP = "Lkotlin/collections/Map;";
+ static final String KT_UNIT = "Lkotlin/Unit;";
+
+ static final String KT_FUNCTION1 = "Lkotlin/Function1;";
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
index d03fcd1..7b9380f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+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.KmClassSubject;
@@ -32,6 +33,7 @@
@RunWith(Parameterized.class)
public class MetadataRewriteInClasspathTypeTest extends KotlinMetadataTestBase {
+ private static final String EXPECTED = StringUtils.lines("Impl::foo");
private final TestParameters parameters;
@@ -97,7 +99,7 @@
.addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), baseLibJar, libJar)
.addClasspath(output)
.run(parameters.getRuntime(), PKG + ".classpath_app.MainKt")
- .assertSuccessWithOutputLines("Impl::foo");
+ .assertSuccessWithOutput(EXPECTED);
}
private void inspectMerged(CodeInspector inspector) {
@@ -163,7 +165,7 @@
.addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), baseLibJar, libJar)
.addClasspath(output)
.run(parameters.getRuntime(), PKG + ".classpath_app.MainKt")
- .assertSuccessWithOutputLines("Impl::foo");
+ .assertSuccessWithOutput(EXPECTED);
}
private void inspectRenamed(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
index 1f8fee7..85a1869 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
@@ -7,7 +7,6 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
@@ -17,12 +16,15 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+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.KmClassSubject;
import com.android.tools.r8.utils.codeinspector.KmFunctionSubject;
import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
+import com.android.tools.r8.utils.codeinspector.KmTypeProjectionSubject;
import com.android.tools.r8.utils.codeinspector.KmTypeSubject;
+import com.android.tools.r8.utils.codeinspector.KmValueParameterSubject;
import java.nio.file.Path;
import java.util.Collection;
import java.util.HashMap;
@@ -35,6 +37,7 @@
@RunWith(Parameterized.class)
public class MetadataRewriteInExtensionFunctionTest extends KotlinMetadataTestBase {
+ private static final String EXPECTED = StringUtils.lines("do stuff", "do stuff", "do stuff");
private final TestParameters parameters;
@@ -76,6 +79,9 @@
// to be called with Kotlin syntax from other kotlin code.
.addKeepRules("-keep class **.BKt { <methods>; }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
+ .addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
+ .addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
.compile()
.inspect(this::inspectMerged)
.writeToZip();
@@ -91,7 +97,7 @@
.addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
.addClasspath(output)
.run(parameters.getRuntime(), PKG + ".extension_function_app.MainKt")
- .assertSuccessWithOutputLines("do stuff", "do stuff");
+ .assertSuccessWithOutput(EXPECTED);
}
private void inspectMerged(CodeInspector inspector) {
@@ -127,6 +133,9 @@
// to be called with Kotlin syntax from other kotlin code.
.addKeepRules("-keep class **.BKt { <methods>; }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
+ .addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
+ .addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
.compile()
.inspect(this::inspectRenamed)
.writeToZip();
@@ -142,7 +151,7 @@
.addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
.addClasspath(output)
.run(parameters.getRuntime(), PKG + ".extension_function_app.MainKt")
- .assertSuccessWithOutputLines("do stuff", "do stuff");
+ .assertSuccessWithOutput(EXPECTED);
}
private void inspectRenamed(CodeInspector inspector) {
@@ -190,23 +199,34 @@
kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("csHash");
assertThat(kmFunction, isExtensionFunction());
kmTypeSubject = kmFunction.receiverParameterType();
- assertEquals("Lkotlin/CharSequence;", kmTypeSubject.descriptor());
+ assertEquals(KT_CHAR_SEQUENCE, kmTypeSubject.descriptor());
kmTypeSubject = kmFunction.returnType();
- assertEquals("Lkotlin/Long;", kmTypeSubject.descriptor());
+ assertEquals(KT_LONG, kmTypeSubject.descriptor());
kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("longArrayHash");
assertThat(kmFunction, isExtensionFunction());
kmTypeSubject = kmFunction.receiverParameterType();
- assertEquals("Lkotlin/LongArray;", kmTypeSubject.descriptor());
+ assertEquals(KT_LONG_ARRAY, kmTypeSubject.descriptor());
kmTypeSubject = kmFunction.returnType();
- assertEquals("Lkotlin/Long;", kmTypeSubject.descriptor());
+ assertEquals(KT_LONG, kmTypeSubject.descriptor());
+ // fun B.myApply(apply: B.() -> Unit): Unit
+ // https://github.com/JetBrains/kotlin/blob/master/spec-docs/function-types.md#extension-functions
kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("myApply");
assertThat(kmFunction, isExtensionFunction());
kmTypeSubject = kmFunction.receiverParameterType();
assertEquals(impl.getFinalDescriptor(), kmTypeSubject.descriptor());
- // TODO(b/70169921): Check param[0] has type kotlin/Function1<(renamed) B, Unit>
- String desc = kmFunction.signature().getDesc();
- assertThat(desc, containsString("kotlin/jvm/functions/Function1"));
+
+ List<KmValueParameterSubject> valueParameters = kmFunction.valueParameters();
+ assertEquals(1, valueParameters.size());
+
+ KmValueParameterSubject valueParameter = valueParameters.get(0);
+ assertEquals(KT_FUNCTION1, valueParameter.type().descriptor());
+ List<KmTypeProjectionSubject> typeArguments = valueParameter.type().typeArguments();
+ assertEquals(2, typeArguments.size());
+ KmTypeSubject typeArgument = typeArguments.get(0).type();
+ assertEquals(impl.getFinalDescriptor(), typeArgument.descriptor());
+ typeArgument = typeArguments.get(1).type();
+ assertEquals(KT_UNIT, typeArgument.descriptor());
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
index 5fb5150..06fcd3a 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+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.KmClassSubject;
@@ -35,6 +36,7 @@
@RunWith(Parameterized.class)
public class MetadataRewriteInExtensionPropertyTest extends KotlinMetadataTestBase {
+ private static final String EXPECTED = StringUtils.lines("do stuff", "do stuff");
private final TestParameters parameters;
@@ -91,7 +93,7 @@
.addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
.addClasspath(output)
.run(parameters.getRuntime(), PKG + ".extension_property_app.MainKt")
- .assertSuccessWithOutputLines("do stuff", "do stuff");
+ .assertSuccessWithOutput(EXPECTED);
}
private void inspectMerged(CodeInspector inspector) {
@@ -155,7 +157,7 @@
.addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
.addClasspath(output)
.run(parameters.getRuntime(), PKG + ".extension_property_app.MainKt")
- .assertSuccessWithOutputLines("do stuff", "do stuff");
+ .assertSuccessWithOutput(EXPECTED);
}
private void inspectRenamed(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
index eb98862..e72c314 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+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.KmClassSubject;
@@ -34,6 +35,7 @@
@RunWith(Parameterized.class)
public class MetadataRewriteInFunctionTest extends KotlinMetadataTestBase {
+ private static final String EXPECTED = StringUtils.lines("do stuff", "do stuff");
private final TestParameters parameters;
@@ -89,7 +91,7 @@
.addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
.addClasspath(output)
.run(parameters.getRuntime(), PKG + ".function_app.MainKt")
- .assertSuccessWithOutputLines("do stuff", "do stuff");
+ .assertSuccessWithOutput(EXPECTED);
}
private void inspectMerged(CodeInspector inspector) {
@@ -151,7 +153,7 @@
.addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
.addClasspath(output)
.run(parameters.getRuntime(), PKG + ".function_app.MainKt")
- .assertSuccessWithOutputLines("do stuff", "do stuff");
+ .assertSuccessWithOutput(EXPECTED);
}
private void inspectRenamed(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java
index 54675ca..0e41755 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java
@@ -7,22 +7,23 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+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.KmFunctionSubject;
import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
+import com.android.tools.r8.utils.codeinspector.KmTypeProjectionSubject;
+import com.android.tools.r8.utils.codeinspector.KmTypeSubject;
import com.android.tools.r8.utils.codeinspector.KmValueParameterSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.nio.file.Path;
@@ -37,6 +38,7 @@
@RunWith(Parameterized.class)
public class MetadataRewriteInFunctionWithDefaultValueTest extends KotlinMetadataTestBase {
+ private static final String EXPECTED = StringUtils.lines("a", "b", "c");
private final TestParameters parameters;
@@ -75,27 +77,25 @@
// Keep LibKt and applyMap function, along with applyMap$default
.addKeepRules("-keep class **.LibKt { *** applyMap*(...); }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
+ .addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
+ .addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
.compile()
.inspect(this::inspect)
.writeToZip();
- ProcessResult kotlinTestCompileResult =
+ Path output =
kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/default_value_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- // TODO(b/70169921): update to just .compile() once fixed.
- .compileRaw();
+ .compile();
- // TODO(b/70169921): should be able to compile!
- assertNotEquals(0, kotlinTestCompileResult.exitCode);
- assertThat(
- kotlinTestCompileResult.stderr,
- containsString("type mismatch: inferred type is kotlin.collections.Map<String, String"));
- assertThat(
- kotlinTestCompileResult.stderr, containsString("but java.util.Map<K, V> was expected"));
- assertThat(
- kotlinTestCompileResult.stderr, not(containsString("no value passed for parameter 'p2'")));
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".default_value_app.MainKt")
+ .assertSuccessWithOutput(EXPECTED);
}
private void inspect(CodeInspector inspector) {
@@ -116,13 +116,24 @@
KmPackageSubject kmPackage = libKt.getKmPackage();
assertThat(kmPackage, isPresent());
+ // String applyMap(Map<String, String>, (default) String)
KmFunctionSubject kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("applyMap");
assertThat(kmFunction, isExtensionFunction());
List<KmValueParameterSubject> valueParameters = kmFunction.valueParameters();
assertEquals(2, valueParameters.size());
- // TODO(b/70169921): inspect 1st arg is Map with correct type parameter.
- KmValueParameterSubject valueParameter = valueParameters.get(1);
+
+ KmValueParameterSubject valueParameter = valueParameters.get(0);
+ assertFalse(valueParameter.declaresDefaultValue());
+ assertEquals(KT_MAP, valueParameter.type().descriptor());
+ List<KmTypeProjectionSubject> typeArguments = valueParameter.type().typeArguments();
+ assertEquals(2, typeArguments.size());
+ KmTypeSubject typeArgument = typeArguments.get(0).type();
+ assertEquals(KT_STRING, typeArgument.descriptor());
+ typeArgument = typeArguments.get(1).type();
+ assertEquals(KT_STRING, typeArgument.descriptor());
+
+ valueParameter = valueParameters.get(1);
assertTrue(valueParameter.declaresDefaultValue());
- assertEquals("Lkotlin/String;", valueParameter.type().descriptor());
+ assertEquals(KT_STRING, valueParameter.type().descriptor());
}
}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
index 1bf7552..68714f6 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
@@ -7,21 +7,23 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+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.KmFunctionSubject;
import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
+import com.android.tools.r8.utils.codeinspector.KmTypeProjectionSubject;
+import com.android.tools.r8.utils.codeinspector.KmTypeSubject;
import com.android.tools.r8.utils.codeinspector.KmValueParameterSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.nio.file.Path;
@@ -36,6 +38,7 @@
@RunWith(Parameterized.class)
public class MetadataRewriteInFunctionWithVarargTest extends KotlinMetadataTestBase {
+ private static final String EXPECTED = StringUtils.lines("SomeClass::R8");
private final TestParameters parameters;
@@ -75,24 +78,25 @@
// Keep LibKt, along with bar function.
.addKeepRules("-keep class **.LibKt { *** bar(...); }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
+ .addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
+ .addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
.compile()
.inspect(this::inspect)
.writeToZip();
- ProcessResult kotlinTestCompileResult =
+ Path output =
kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/vararg_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- // TODO(b/70169921): update to just .compile() once fixed.
- .compileRaw();
+ .compile();
- // TODO(b/70169921): should be able to compile!
- assertNotEquals(0, kotlinTestCompileResult.exitCode);
- assertThat(
- kotlinTestCompileResult.stderr,
- not(containsString("type mismatch: inferred type is String but Array<T> was expected")));
- assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: foo"));
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".vararg_app.MainKt")
+ .assertSuccessWithOutput(EXPECTED);
}
private void inspect(CodeInspector inspector) {
@@ -118,13 +122,32 @@
KmPackageSubject kmPackage = libKt.getKmPackage();
assertThat(kmPackage, isPresent());
+ // Unit bar(vararg String, (SomeClass, String) -> Unit)
KmFunctionSubject kmFunction = kmPackage.kmFunctionWithUniqueName("bar");
assertThat(kmFunction, not(isExtensionFunction()));
List<KmValueParameterSubject> valueParameters = kmFunction.valueParameters();
assertEquals(2, valueParameters.size());
+
KmValueParameterSubject valueParameter = valueParameters.get(0);
assertTrue(valueParameter.isVararg());
- assertEquals("Lkotlin/String;", valueParameter.varargElementType().descriptor());
- // TODO(b/70169921): inspect 2nd arg is lambda with correct type parameter.
+ assertEquals(KT_STRING, valueParameter.varargElementType().descriptor());
+
+ assertEquals(KT_ARRAY, valueParameter.type().descriptor());
+ List<KmTypeProjectionSubject> typeArguments = valueParameter.type().typeArguments();
+ assertEquals(1, typeArguments.size());
+ KmTypeSubject typeArgument = typeArguments.get(0).type();
+ assertEquals(KT_STRING, typeArgument.descriptor());
+
+ valueParameter = valueParameters.get(1);
+ assertFalse(valueParameter.isVararg());
+ typeArguments = valueParameter.type().typeArguments();
+ assertEquals(3, typeArguments.size());
+
+ typeArgument = typeArguments.get(0).type();
+ assertEquals(cls.getFinalDescriptor(), typeArgument.descriptor());
+ typeArgument = typeArguments.get(1).type();
+ assertEquals(KT_STRING, typeArgument.descriptor());
+ typeArgument = typeArguments.get(2).type();
+ assertEquals(KT_UNIT, typeArgument.descriptor());
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java
index ba6b08f..0bca5ee 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+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.KmPackageSubject;
@@ -30,6 +31,7 @@
@RunWith(Parameterized.class)
public class MetadataRewriteInLibraryTypeTest extends KotlinMetadataTestBase {
+ private static final String EXPECTED = StringUtils.lines("Sub::foo", "Sub::boo", "true");
private final TestParameters parameters;
@@ -104,7 +106,7 @@
.addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), baseLibJarMap.get(targetVersion))
.addClasspath(out)
.run(parameters.getRuntime(), main)
- .assertSuccessWithOutputLines("Sub::foo", "Sub::boo", "true");
+ .assertSuccessWithOutput(EXPECTED);
}
private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java
index 7ecf894..0e734ea 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+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.KmClassSubject;
@@ -30,6 +31,8 @@
@RunWith(Parameterized.class)
public class MetadataRewriteInNestedClassTest extends KotlinMetadataTestBase {
+ private static final String EXPECTED =
+ StringUtils.lines("Inner::inner", "42", "Nested::nested", "42");
private final TestParameters parameters;
@@ -88,7 +91,7 @@
.addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
.addClasspath(output)
.run(parameters.getRuntime(), PKG + ".nested_app.MainKt")
- .assertSuccessWithOutputLines("Inner::inner", "42", "Nested::nested", "42");
+ .assertSuccessWithOutput(EXPECTED);
}
private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java
index 6dcbc1a..05acc76 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+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.KmClassSubject;
@@ -29,6 +30,7 @@
@RunWith(Parameterized.class)
public class MetadataRewriteInParameterTypeTest extends KotlinMetadataTestBase {
+ private static final String EXPECTED = StringUtils.lines("Impl::bar", "Program::bar");
private final TestParameters parameters;
@@ -83,7 +85,7 @@
.addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
.addClasspath(output)
.run(parameters.getRuntime(), PKG + ".parametertype_app.MainKt")
- .assertSuccessWithOutputLines("Impl::bar", "Program::bar");
+ .assertSuccessWithOutput(EXPECTED);
}
private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java
index 8aba4f7..4fc0029 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+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.KmClassSubject;
@@ -29,6 +30,7 @@
@RunWith(Parameterized.class)
public class MetadataRewriteInPropertyTypeTest extends KotlinMetadataTestBase {
+ private static final String EXPECTED = StringUtils.lines("Impl::8");
private final TestParameters parameters;
@@ -81,7 +83,7 @@
.addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
.addClasspath(output)
.run(parameters.getRuntime(), PKG + ".propertytype_app.MainKt")
- .assertSuccessWithOutputLines("Impl::8");
+ .assertSuccessWithOutput(EXPECTED);
}
private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java
index 1e33e7c..21fcbb6 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+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.KmClassSubject;
@@ -29,6 +30,8 @@
@RunWith(Parameterized.class)
public class MetadataRewriteInReturnTypeTest extends KotlinMetadataTestBase {
+ private static final String EXPECTED = StringUtils.lines("Impl::foo", "Program::foo", "true");
+
private final TestParameters parameters;
@Parameterized.Parameters(name = "{0} target: {1}")
@@ -82,7 +85,7 @@
.addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
.addClasspath(output)
.run(parameters.getRuntime(), PKG + ".returntype_app.MainKt")
- .assertSuccessWithOutputLines("Impl::foo", "Program::foo", "true");
+ .assertSuccessWithOutput(EXPECTED);
}
private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
index 57cd84c..373dc40 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+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.KmClassSubject;
@@ -37,6 +38,7 @@
@RunWith(Parameterized.class)
public class MetadataRewriteInSealedClassTest extends KotlinMetadataTestBase {
+ private static final String EXPECTED = StringUtils.lines("6");
private final TestParameters parameters;
@@ -93,7 +95,7 @@
.addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
.addClasspath(output)
.run(parameters.getRuntime(), PKG + ".sealed_app.ValidKt")
- .assertSuccessWithOutputLines("6");
+ .assertSuccessWithOutput(EXPECTED);
}
private void inspectValid(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java
index ba59f4b..9de95b4 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+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.KmClassSubject;
@@ -29,6 +30,7 @@
@RunWith(Parameterized.class)
public class MetadataRewriteInSuperTypeTest extends KotlinMetadataTestBase {
+ private static final String EXPECTED = StringUtils.lines("Impl::foo", "Program::foo");
private final TestParameters parameters;
@@ -83,7 +85,7 @@
.addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
.addClasspath(output)
.run(parameters.getRuntime(), PKG + ".supertype_app.MainKt")
- .assertSuccessWithOutputLines("Impl::foo", "Program::foo");
+ .assertSuccessWithOutput(EXPECTED);
}
private void inspectMerged(CodeInspector inspector) {
@@ -130,7 +132,7 @@
.addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
.addClasspath(output)
.run(parameters.getRuntime(), PKG + ".supertype_app.MainKt")
- .assertSuccessWithOutputLines("Impl::foo", "Program::foo");
+ .assertSuccessWithOutput(EXPECTED);
}
private void inspectRenamed(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/default_value_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/default_value_app/main.kt
index 9cb6039..08551b5 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/default_value_app/main.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/default_value_app/main.kt
@@ -7,6 +7,6 @@
fun main() {
val m = mapOf("A" to "a", "B" to "b", "C" to "c")
- val s = listOf("A", "B", "C").joinToString(separator = "\n")
+ val s = listOf("A", "B", "C").joinToString(separator = System.lineSeparator())
println(s.applyMap(m))
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/default_value_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/default_value_lib/lib.kt
index c76b5be..dafbdbf 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/default_value_lib/lib.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/default_value_lib/lib.kt
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata.default_value_lib
-fun String.applyMap(map: Map<String, String>, separator: String = "\n") =
+fun String.applyMap(map: Map<String, String>, separator: String = System.lineSeparator()) =
this.split(separator).joinToString(separator = separator) {
if (map.containsKey(it)) map.getValue(it) else it
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/extension_function_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/extension_function_app/main.kt
index d85c992..72e86b4 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/extension_function_app/main.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/extension_function_app/main.kt
@@ -15,6 +15,5 @@
"R8".csHash()
longArrayOf(42L).longArrayHash()
- // TODO(b/70169921): Need to set arguments as type parameter.
- // B().myApply { this.doStuff() }
+ B().myApply { this.doStuff() }
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/optimize/switches/KotlinEnumSwitchTest.java b/src/test/java/com/android/tools/r8/kotlin/optimize/switches/KotlinEnumSwitchTest.java
index 374f91d..d7eeae4 100644
--- a/src/test/java/com/android/tools/r8/kotlin/optimize/switches/KotlinEnumSwitchTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/optimize/switches/KotlinEnumSwitchTest.java
@@ -42,7 +42,10 @@
.addProgramFiles(Paths.get(ToolHelper.EXAMPLES_KOTLIN_BUILD_DIR, "enumswitch.jar"))
.addKeepMainRule("enumswitch.EnumSwitchKt")
.addOptionsModification(
- options -> options.enableEnumValueOptimization = enableSwitchMapRemoval)
+ options -> {
+ options.enableEnumValueOptimization = enableSwitchMapRemoval;
+ options.enableEnumSwitchMapRemoval = enableSwitchMapRemoval;
+ })
.setMinApi(parameters.getRuntime())
.noMinification()
.compile()
diff --git a/src/test/java/com/android/tools/r8/naming/ClassNameMinifierOriginalClassNameTest.java b/src/test/java/com/android/tools/r8/naming/ClassNameMinifierOriginalClassNameTest.java
new file mode 100644
index 0000000..9d4c4fb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/ClassNameMinifierOriginalClassNameTest.java
@@ -0,0 +1,125 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.naming.testclasses.A;
+import com.android.tools.r8.naming.testclasses.B;
+import com.android.tools.r8.utils.FileUtils;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.concurrent.ExecutionException;
+import java.util.function.Function;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** This test is designed to test the workaround for b/149946708. */
+@RunWith(Parameterized.class)
+public class ClassNameMinifierOriginalClassNameTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public ClassNameMinifierOriginalClassNameTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private static Function<TestParameters, R8TestCompileResult> compilationResults =
+ memoizeFunction(ClassNameMinifierOriginalClassNameTest::compile);
+
+ private static R8TestCompileResult compile(TestParameters parameters)
+ throws CompilationFailedException, IOException, ExecutionException {
+ // Adding the obfuscation dictionary just ensures that we assign a name to B that will collide
+ // independent of minification scheme.
+ Path dictionary = getStaticTemp().newFolder().toPath().resolve("dictionary.txt");
+ FileUtils.writeTextFile(dictionary, "A");
+ return testForR8(getStaticTemp(), parameters.getBackend())
+ .addProgramClasses(A.class, B.class)
+ .addKeepClassAndMembersRulesWithAllowObfuscation(B.class)
+ .addKeepRules("-classobfuscationdictionary " + dictionary.toString(), "-keeppackagenames")
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ assertEquals(1, inspector.allClasses().size());
+ assertThat(inspector.clazz(B.class), isRenamed());
+ assertEquals(A.class.getTypeName(), inspector.clazz(B.class).getFinalName());
+ });
+ }
+
+ @Test
+ public void testR8() throws ExecutionException, CompilationFailedException, IOException {
+ R8TestCompileResult libraryCompileResult = compilationResults.apply(parameters);
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .noMinification()
+ .addClasspathClasses(A.class, B.class)
+ .addApplyMapping(libraryCompileResult.getProguardMap())
+ .compile()
+ .addRunClasspathFiles(libraryCompileResult.writeToZip())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("B.foo");
+ }
+
+ @Test
+ public void testR8WithReferenceToNotMapped() {
+ assumeTrue(parameters.isDexRuntime());
+ R8TestCompileResult libraryCompileResult = compilationResults.apply(parameters);
+ assertThrows(
+ CompilationFailedException.class,
+ () ->
+ testForR8(parameters.getBackend())
+ .addProgramClasses(MainWithReferenceToNotMapped.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(MainWithReferenceToNotMapped.class)
+ .noMinification()
+ .addClasspathClasses(A.class, B.class)
+ .addApplyMapping(libraryCompileResult.getProguardMap())
+ .allowDiagnosticWarningMessages()
+ .compileWithExpectedDiagnostics(
+ diagnosticMessages ->
+ diagnosticMessages.assertAllWarningMessagesMatch(
+ containsString(
+ "'"
+ + B.class.getTypeName()
+ + "' cannot be mapped to '"
+ + A.class.getTypeName()
+ + "' because it is in conflict"))));
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new B().foo();
+ }
+ }
+
+ public static class MainWithReferenceToNotMapped {
+
+ public static void main(String[] args) {
+ System.out.println(new A());
+ new B().foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/FieldMinificationCollisionTest.java b/src/test/java/com/android/tools/r8/naming/FieldMinificationCollisionTest.java
index 69b4b03..180541a 100644
--- a/src/test/java/com/android/tools/r8/naming/FieldMinificationCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/naming/FieldMinificationCollisionTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -29,6 +30,7 @@
.addKeepMainRule(TestClass.class)
.addKeepRules(
"-keep class " + B.class.getTypeName() + " { public java.lang.String f2; }")
+ .enableMemberValuePropagationAnnotations()
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
.enableMergeAnnotations()
@@ -55,7 +57,7 @@
@NeverMerge
static class A {
- public String f1;
+ @NeverPropagateValue public String f1;
public A(String f1) {
this.f1 = f1;
@@ -65,7 +67,7 @@
@NeverMerge
static class B extends A {
- public String f2;
+ @NeverPropagateValue public String f2;
public B(String f1, String f2) {
super(f1);
@@ -76,7 +78,7 @@
@NeverClassInline
static class C extends B {
- public String f3;
+ @NeverPropagateValue public String f3;
public C(String f1, String f2, String f3) {
super(f1, f2);
diff --git a/src/test/java/com/android/tools/r8/naming/LibraryClassInheritingFromProgramClassNamingTest.java b/src/test/java/com/android/tools/r8/naming/LibraryClassInheritingFromProgramClassNamingTest.java
new file mode 100644
index 0000000..a6c49e4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/LibraryClassInheritingFromProgramClassNamingTest.java
@@ -0,0 +1,102 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming;
+
+import static org.hamcrest.CoreMatchers.containsString;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+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 LibraryClassInheritingFromProgramClassNamingTest extends TestBase {
+
+ private final TestParameters parameters;
+ private final Class[] LIBRARY_CLASSES =
+ new Class[] {AndroidTestCase.class, ApplicationTestCase.class};
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public LibraryClassInheritingFromProgramClassNamingTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws ExecutionException, CompilationFailedException, IOException {
+ R8TestCompileResult libraryResult =
+ testForR8(parameters.getBackend())
+ .setMinApi(parameters.getApiLevel())
+ .addProgramClasses(LIBRARY_CLASSES)
+ .addProgramClasses(
+ I.class, Assert.class, TestCase.class, ApplicationTestCaseInProgram.class)
+ .addKeepAllClassesRule()
+ .compile();
+ testForR8Compat(parameters.getBackend())
+ .addLibraryClasses(LIBRARY_CLASSES)
+ .addLibraryFiles(runtimeJar(parameters.getBackend()))
+ .addProgramClasses(
+ I.class, Assert.class, TestCase.class, ApplicationTestCaseInProgram.class, Main.class)
+ .addKeepClassAndMembersRulesWithAllowObfuscation(
+ I.class, Assert.class, TestCase.class, ApplicationTestCaseInProgram.class)
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .allowDiagnosticWarningMessages()
+ .compile()
+ .assertAllWarningMessagesMatch(
+ containsString(
+ "Library class "
+ + AndroidTestCase.class.getTypeName()
+ + " extends program class "
+ + TestCase.class.getTypeName()))
+ .addRunClasspathFiles(libraryResult.writeToZip())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("TestCase.foo");
+ }
+
+ public interface I {
+
+ void foo();
+ }
+
+ public static class Assert implements I {
+
+ @Override
+ public void foo() {
+ System.out.println("Assert.foo");
+ }
+ }
+
+ public static class TestCase extends Assert {
+
+ @Override
+ public void foo() {
+ System.out.println("TestCase.foo");
+ }
+ }
+
+ public static class AndroidTestCase extends TestCase {}
+
+ public static class ApplicationTestCase extends AndroidTestCase {}
+
+ public static class ApplicationTestCaseInProgram extends AndroidTestCase {}
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new ApplicationTestCaseInProgram().foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/ReservedFieldNameInSubClassTest.java b/src/test/java/com/android/tools/r8/naming/ReservedFieldNameInSubClassTest.java
index 77190ce..943e4bf 100644
--- a/src/test/java/com/android/tools/r8/naming/ReservedFieldNameInSubClassTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ReservedFieldNameInSubClassTest.java
@@ -10,6 +10,7 @@
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.BooleanUtils;
@@ -34,7 +35,8 @@
@Parameterized.Parameters(name = "{0}, reserve name: {1}")
public static List<Object[]> data() {
- return buildParameters(getTestParameters().withAllRuntimes().build(), BooleanUtils.values());
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
}
public ReservedFieldNameInSubClassTest(TestParameters parameters, boolean reserveName) {
@@ -49,6 +51,7 @@
testForR8(parameters.getBackend())
.addProgramClasses(
TestClass.class, A.class, B.class, C.class, I.class, J.class, K.class)
+ .enableMemberValuePropagationAnnotations()
.enableMergeAnnotations()
.addKeepMainRule(TestClass.class)
.addKeepRules(
@@ -57,7 +60,7 @@
+ C.class.getTypeName()
+ "{ java.lang.String a; }"
: "")
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(expectedOutput)
.inspector();
@@ -140,13 +143,13 @@
@NeverMerge
static class A {
- String f1 = "He";
+ @NeverPropagateValue String f1 = "He";
}
@NeverMerge
static class B extends A {
- String f2 = "l";
+ @NeverPropagateValue String f2 = "l";
}
@NeverMerge
@@ -169,7 +172,7 @@
static class C extends B implements J, K {
- String a = "!";
+ @NeverPropagateValue String a = "!";
@Override
public String toString() {
diff --git a/src/test/java/com/android/tools/r8/naming/ReservedFieldNameInSubInterfaceTest.java b/src/test/java/com/android/tools/r8/naming/ReservedFieldNameInSubInterfaceTest.java
index 057ba0c..309c156 100644
--- a/src/test/java/com/android/tools/r8/naming/ReservedFieldNameInSubInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ReservedFieldNameInSubInterfaceTest.java
@@ -11,6 +11,7 @@
import static org.junit.Assume.assumeFalse;
import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.utils.BooleanUtils;
@@ -46,6 +47,7 @@
R8TestRunResult result =
testForR8(Backend.DEX)
.addProgramClasses(TestClass.class, A.class, B.class, I.class, J.class)
+ .enableMemberValuePropagationAnnotations()
.enableMergeAnnotations()
.addKeepMainRule(TestClass.class)
.addKeepRules(
@@ -102,6 +104,7 @@
.addProgramClasses(TestClass.class, A.class, B.class)
.addLibraryClasses(I.class, J.class)
.addLibraryFiles(runtimeJar(Backend.DEX))
+ .enableMemberValuePropagationAnnotations()
.enableMergeAnnotations()
.addKeepMainRule(TestClass.class)
.compile()
@@ -138,7 +141,7 @@
@NeverMerge
static class A {
- String f2 = " ";
+ @NeverPropagateValue String f2 = " ";
}
static class B extends A implements J {
diff --git a/src/test/java/com/android/tools/r8/naming/ReservedFieldNameInSuperInterfaceTest.java b/src/test/java/com/android/tools/r8/naming/ReservedFieldNameInSuperInterfaceTest.java
index f7551b7..4af8655 100644
--- a/src/test/java/com/android/tools/r8/naming/ReservedFieldNameInSuperInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ReservedFieldNameInSuperInterfaceTest.java
@@ -10,6 +10,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeFalse;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.utils.BooleanUtils;
@@ -50,6 +51,7 @@
reserveName
? "-keepclassmembernames class " + I.class.getTypeName() + "{ <fields>; }"
: "")
+ .enableMemberValuePropagationAnnotations()
.run(TestClass.class)
.assertSuccessWithOutput(expectedOutput);
@@ -79,6 +81,7 @@
.addLibraryClasses(I.class)
.addLibraryFiles(runtimeJar(Backend.DEX))
.addKeepMainRule(TestClass.class)
+ .enableMemberValuePropagationAnnotations()
.compile()
.addRunClasspathFiles(testForD8().addProgramClasses(I.class).compile().writeToZip())
.run(TestClass.class)
@@ -116,7 +119,7 @@
static class A implements I {
- String f2 = "world!";
+ @NeverPropagateValue String f2 = "world!";
@Override
public String toString() {
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingKeepPrecedenceTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingKeepPrecedenceTest.java
index da47d4c..3d4b859 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingKeepPrecedenceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingKeepPrecedenceTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -38,7 +39,7 @@
@NeverClassInline
public static class B { // Should be kept with all members without renaming.
- int foo = 4;
+ @NeverPropagateValue int foo = 4;
@NeverInline
int bar() {
@@ -63,7 +64,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public ApplyMappingKeepPrecedenceTest(TestParameters parameters) {
@@ -90,7 +91,8 @@
.addKeepClassRules(A.class)
.addKeepRules("-keepclassmembernames class " + B.class.getTypeName() + " { *; }")
.addKeepMainRule(Main.class)
- .setMinApi(parameters.getRuntime())
+ .enableMemberValuePropagationAnnotations()
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines(
"What is the answer to life the universe and everything?", "42")
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingMinificationTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingMinificationTest.java
index d6382aa..f527ead 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingMinificationTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingMinificationTest.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -32,8 +33,10 @@
@NeverClassInline
public static class A {
- public int fieldA = 1;
- public int fieldB = 2;
+
+ @NeverPropagateValue public int fieldA = 1;
+
+ @NeverPropagateValue public int fieldB = 2;
@NeverInline
public void methodA() {
@@ -75,7 +78,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public ApplyMappingMinificationTest(TestParameters parameters) {
@@ -96,9 +99,10 @@
.addKeepRules(
"-keepclassmembers class " + A.class.getTypeName() + " { void methodC(); }")
.enableInliningAnnotations()
+ .enableMemberValuePropagationAnnotations()
.enableNeverClassInliningAnnotations()
.addApplyMapping(StringUtils.lines(pgMap))
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), C.class)
.assertSuccessWithOutputLines("1", "2", "A.methodA", "A.methodB", "A.methodC", "B.foo")
.inspect(
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/desugar/DefaultInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/desugar/DefaultInterfaceMethodTest.java
index 58cdef6..ad3b7eb 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/desugar/DefaultInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/desugar/DefaultInterfaceMethodTest.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.TestRuntime;
import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.AndroidApiLevel;
@@ -44,7 +43,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection params() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
private final TestParameters parameters;
@@ -67,7 +66,7 @@
testForR8(parameters.getBackend())
.addProgramClasses(LibraryInterface.class, ProgramClass.class)
.addKeepMainRule(ProgramClass.class)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), ProgramClass.class)
.assertSuccessWithOutput(EXPECTED);
}
@@ -79,12 +78,12 @@
testForR8(parameters.getBackend())
.addProgramClasses(LibraryInterface.class)
.addKeepRules(ruleContent)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile();
CodeInspector inspector = libraryResult.inspector();
assertTrue(inspector.clazz(LibraryInterface.class).isPresent());
assertTrue(inspector.method(LibraryInterface.class.getMethod("foo")).isPresent());
- if (willDesugarDefaultInterfaceMethods(parameters.getRuntime())) {
+ if (willDesugarDefaultInterfaceMethods(parameters.getApiLevel())) {
ClassSubject companion = inspector.clazz(Reference.classFromDescriptor(
InterfaceMethodRewriter.getCompanionClassDescriptor(
classFromClass(LibraryInterface.class).getDescriptor())));
@@ -100,15 +99,14 @@
.addClasspathClasses(LibraryInterface.class)
.addApplyMapping(libraryResult.getProguardMap())
.addKeepMainRule(ProgramClass.class)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile()
.addRunClasspathFiles(libraryResult.writeToZip())
.run(parameters.getRuntime(), ProgramClass.class)
.assertSuccessWithOutput(EXPECTED);
}
- private static boolean willDesugarDefaultInterfaceMethods(TestRuntime runtime) {
- return runtime.isDex()
- && runtime.asDex().getMinApiLevel().getLevel() < AndroidApiLevel.N.getLevel();
+ private static boolean willDesugarDefaultInterfaceMethods(AndroidApiLevel apiLevel) {
+ return apiLevel != null && apiLevel.getLevel() < AndroidApiLevel.N.getLevel();
}
}
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/shared/NameClashTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/shared/NameClashTest.java
index 0b37f73..3466e21 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/shared/NameClashTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/shared/NameClashTest.java
@@ -401,21 +401,6 @@
}
@Test
- public void testR8_originalLibClassRenamedToExistingName() throws Exception {
- FileUtils.writeTextFile(mappingFile, mappingToExistingClassName());
- try {
- testR8_originalLibraryJar(mappingFile);
- fail("Expect compilation failure.");
- } catch (CompilationFailedException e) {
- assertThat(e.getCause().getMessage(), containsString("cannot be mapped to"));
- assertThat(
- e.getCause().getMessage(),
- containsString("because it is in conflict with an existing class with the same name."));
- assertThat(e.getCause().getMessage(), containsString(ProgramClass.class.getTypeName()));
- }
- }
-
- @Test
public void testProguard_minifiedLib() throws Exception {
FileUtils.writeTextFile(mappingFile, invertedMapping());
try {
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/MemberResolutionTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/MemberResolutionTest.java
index 30a8126..3b683fa 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/MemberResolutionTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/MemberResolutionTest.java
@@ -9,6 +9,7 @@
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -29,7 +30,7 @@
@NeverMerge
abstract class AbstractChecker {
// String tag -> p
- private String tag = "PrivateInitialTag_AbstractChecker";
+ @NeverPropagateValue private String tag = "PrivateInitialTag_AbstractChecker";
// check() -> x
private void check() {
@@ -80,7 +81,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public MemberResolutionTest(TestParameters parameters) {
@@ -121,9 +122,10 @@
AbstractChecker.class, ConcreteChecker.class, MemberResolutionTestMain.class)
.addKeepMainRule(MemberResolutionTestMain.class)
.addKeepRules("-applymapping " + mapPath)
+ .enableMemberValuePropagationAnnotations()
.enableMergeAnnotations()
.addOptionsModification(options -> options.enableInlining = false)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MemberResolutionTestMain.class)
.assertSuccessWithOutput(expectedOutput)
.inspector();
diff --git a/src/test/java/com/android/tools/r8/naming/testclasses/A.java b/src/test/java/com/android/tools/r8/naming/testclasses/A.java
new file mode 100644
index 0000000..28a93a6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/testclasses/A.java
@@ -0,0 +1,7 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming.testclasses;
+
+public class A {}
diff --git a/src/test/java/com/android/tools/r8/naming/testclasses/B.java b/src/test/java/com/android/tools/r8/naming/testclasses/B.java
new file mode 100644
index 0000000..a364510
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/testclasses/B.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming.testclasses;
+
+public class B extends A {
+
+ public void foo() {
+ System.out.println("B.foo");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/regress/b150688800/IdempotentCountErrorTest.java b/src/test/java/com/android/tools/r8/regress/b150688800/IdempotentCountErrorTest.java
new file mode 100644
index 0000000..52222e0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b150688800/IdempotentCountErrorTest.java
@@ -0,0 +1,60 @@
+// 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.regress.b150688800;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverPropagateValue;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class IdempotentCountErrorTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("0.0");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public IdempotentCountErrorTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .enableInliningAnnotations()
+ .enableMemberValuePropagationAnnotations()
+ .addInnerClasses(IdempotentCountErrorTest.class)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ static class TestClass {
+
+ @NeverInline
+ @NeverPropagateValue
+ public static double twoInputValues(double x, double y) {
+ return x + y;
+ }
+
+ public static void main(String[] args) {
+ if (args.length > 42) {
+ System.out.println(twoInputValues(0, 0));
+ } else {
+ System.out.println(twoInputValues(0, 0));
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
index 72ede27..0d89583 100644
--- a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.resolution;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.AsmTestBase;
@@ -31,10 +32,11 @@
import com.android.tools.r8.resolution.singletarget.two.OtherSubSubClassTwo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableList;
+import java.io.IOException;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import java.util.stream.Collectors;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -76,11 +78,11 @@
String methodName,
Class invokeReceiver,
Class singleTargetHolderOrNull,
- List<Class> allTargetHolders) {
+ List<Class> virtualTargetHolders) {
this.methodName = methodName;
this.invokeReceiver = invokeReceiver;
this.singleTargetHolderOrNull = singleTargetHolderOrNull;
- this.allTargetHolders = allTargetHolders;
+ this.virtualTargetHolders = virtualTargetHolders;
}
@BeforeClass
@@ -89,6 +91,10 @@
appInfo = appView.appInfo();
}
+ private static Object[] noVirtualSingleTarget(String name, Class<?> receiverAndTarget) {
+ return new Object[] {name, receiverAndTarget, null, Collections.emptyList()};
+ }
+
private static Object[] singleTarget(String name, Class<?> receiverAndTarget) {
return new Object[]{name, receiverAndTarget, receiverAndTarget,
Collections.singletonList(receiverAndTarget)};
@@ -119,54 +125,29 @@
new Object[][] {
singleTarget("singleTargetAtTop", AbstractTopClass.class),
singleTargetWithAbstracts(
- "singleShadowingOverride",
- AbstractTopClass.class,
- AbstractSubClass.class,
- AbstractTopClass.class),
+ "singleShadowingOverride", AbstractTopClass.class, AbstractSubClass.class),
manyTargets(
"abstractTargetAtTop",
AbstractTopClass.class,
- AbstractTopClass.class,
SubSubClassOne.class,
SubSubClassTwo.class),
singleTargetWithAbstracts(
- "overridenInAbstractClassOnly",
- AbstractTopClass.class,
- AbstractTopClass.class,
- SubSubClassThree.class),
- onlyUnreachableTargets(
- "overridenInAbstractClassOnly", SubSubClassThree.class, SubSubClassThree.class),
+ "overridenInAbstractClassOnly", AbstractTopClass.class, AbstractTopClass.class),
+ onlyUnreachableTargets("overridenInAbstractClassOnly", SubSubClassThree.class),
manyTargets(
"overriddenInTwoSubTypes",
AbstractTopClass.class,
- AbstractTopClass.class,
SubSubClassOne.class,
SubSubClassTwo.class),
manyTargets(
"definedInTwoSubTypes",
AbstractTopClass.class,
- AbstractTopClass.class,
SubSubClassOne.class,
SubSubClassTwo.class),
onlyUnreachableTargets("staticMethod", AbstractTopClass.class),
- manyTargets(
- "overriddenInTwoSubTypes",
- OtherAbstractTopClass.class,
- OtherAbstractTopClass.class,
- OtherSubSubClassOne.class,
- OtherSubSubClassTwo.class),
- manyTargets(
- "abstractOverriddenInTwoSubTypes",
- OtherAbstractTopClass.class,
- OtherAbstractTopClass.class,
- OtherSubSubClassOne.class,
- OtherSubSubClassTwo.class),
- manyTargets(
- "overridesOnDifferentLevels",
- OtherAbstractTopClass.class,
- OtherAbstractTopClass.class,
- OtherSubSubClassOne.class,
- OtherAbstractSubClassTwo.class),
+ manyTargets("overriddenInTwoSubTypes", OtherAbstractTopClass.class),
+ manyTargets("abstractOverriddenInTwoSubTypes", OtherAbstractTopClass.class),
+ manyTargets("overridesOnDifferentLevels", OtherAbstractTopClass.class),
singleTarget("defaultMethod", AbstractTopClass.class, InterfaceWithDefault.class),
manyTargets(
"overriddenDefault",
@@ -177,18 +158,11 @@
singleTarget("overriddenByIrrelevantInterface", AbstractTopClass.class),
singleTarget(
"overriddenByIrrelevantInterface", SubSubClassOne.class, AbstractTopClass.class),
- manyTargets(
- "overriddenInOtherInterface",
- AbstractTopClass.class,
- InterfaceWithDefault.class),
- manyTargets(
- "abstractMethod",
- ThirdAbstractTopClass.class,
- ThirdAbstractTopClass.class,
- ThirdSubClassOne.class),
- singleTarget("instanceMethod", ThirdAbstractTopClass.class, ThirdAbstractTopClass.class),
singleTarget(
- "otherInstanceMethod", ThirdAbstractTopClass.class, ThirdAbstractTopClass.class),
+ "overriddenInOtherInterface", AbstractTopClass.class, InterfaceWithDefault.class),
+ manyTargets("abstractMethod", ThirdAbstractTopClass.class),
+ noVirtualSingleTarget("instanceMethod", ThirdAbstractTopClass.class),
+ noVirtualSingleTarget("otherInstanceMethod", ThirdAbstractTopClass.class),
});
}
@@ -199,14 +173,15 @@
private final String methodName;
private final Class invokeReceiver;
private final Class singleTargetHolderOrNull;
- private final List<Class> allTargetHolders;
+ private final List<Class> virtualTargetHolders;
@Test
public void lookupSingleTarget() {
DexMethod method = buildNullaryVoidMethod(invokeReceiver, methodName, appInfo.dexItemFactory());
Assert.assertNotNull(
appInfo.resolveMethod(toType(invokeReceiver, appInfo), method).getSingleTarget());
- DexEncodedMethod singleVirtualTarget = appInfo.lookupSingleVirtualTarget(method, method.holder);
+ DexEncodedMethod singleVirtualTarget =
+ appInfo.lookupSingleVirtualTarget(method, method.holder, false);
if (singleTargetHolderOrNull == null) {
Assert.assertNull(singleVirtualTarget);
} else {
@@ -217,7 +192,7 @@
}
@Test
- public void lookupVirtualTargets() {
+ public void lookupVirtualTargets() throws IOException {
DexMethod method = buildNullaryVoidMethod(invokeReceiver, methodName, appInfo.dexItemFactory());
Assert.assertNotNull(
appInfo.resolveMethod(toType(invokeReceiver, appInfo), method).getSingleTarget());
@@ -226,16 +201,24 @@
LookupResult lookupResult =
resolutionResult.lookupVirtualDispatchTargets(
appView.definitionForProgramType(buildType(Main.class, appView.dexItemFactory())),
- appView);
+ appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<DexEncodedMethod> targets = lookupResult.asLookupResultSuccess().getMethodTargets();
- Set<DexType> targetHolders =
- targets.stream().map(m -> m.method.holder).collect(Collectors.toSet());
- Assert.assertEquals(allTargetHolders.size(), targetHolders.size());
+ assertFalse(lookupResult.asLookupResultSuccess().hasLambdaTargets());
+ Set<DexType> targetHolders = new HashSet<>();
+ lookupResult
+ .asLookupResultSuccess()
+ .forEach(
+ methodTarget -> targetHolders.add(methodTarget.getHolder().type),
+ lambdaTarget -> {
+ assert false;
+ });
+ Assert.assertEquals(virtualTargetHolders.size(), targetHolders.size());
assertTrue(
- allTargetHolders.stream().map(t -> toType(t, appInfo)).allMatch(targetHolders::contains));
+ virtualTargetHolders.stream()
+ .map(t -> toType(t, appInfo))
+ .allMatch(targetHolders::contains));
} else {
- assertTrue(allTargetHolders.isEmpty());
+ assertTrue(virtualTargetHolders.isEmpty());
}
}
}
diff --git a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java
index 674c090..eb40786 100644
--- a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java
@@ -124,7 +124,7 @@
assertEquals(methodOnB, resolved.method);
assertFalse(resolutionResult.isVirtualTarget());
DexEncodedMethod singleVirtualTarget =
- appInfo.lookupSingleInterfaceTarget(methodOnB, methodOnB.holder);
+ appInfo.lookupSingleVirtualTarget(methodOnB, methodOnB.holder, false);
Assert.assertNull(singleVirtualTarget);
}
diff --git a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java
index f9fb653..1f24983 100644
--- a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java
@@ -170,7 +170,7 @@
assertEquals(methodOnA, resolved.method);
assertFalse(resolutionResult.isVirtualTarget());
DexEncodedMethod singleVirtualTarget =
- appInfo.lookupSingleVirtualTarget(methodOnB, methodOnB.holder);
+ appInfo.lookupSingleVirtualTarget(methodOnB, methodOnB.holder, false);
Assert.assertNull(singleVirtualTarget);
}
diff --git a/src/test/java/com/android/tools/r8/resolution/access/indirectfield/IndirectFieldAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/indirectfield/IndirectFieldAccessTest.java
index e517531..94b3c45 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/indirectfield/IndirectFieldAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/indirectfield/IndirectFieldAccessTest.java
@@ -70,7 +70,6 @@
testForRuntime(parameters)
.addProgramClasses(getClasses())
.run(parameters.getRuntime(), Main.class)
- .disassemble()
.apply(this::checkExpectedResult);
}
diff --git a/src/test/java/com/android/tools/r8/resolution/access/indirectmethod/IndirectMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/indirectmethod/IndirectMethodAccessTest.java
index 1d75a8b..34af60e 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/indirectmethod/IndirectMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/indirectmethod/IndirectMethodAccessTest.java
@@ -70,7 +70,6 @@
.addProgramClasses(getClasses())
.addProgramClassFileData(getTransforms())
.run(parameters.getRuntime(), Main.class)
- .disassemble()
.apply(this::checkExpectedResult);
}
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java
index 27da1c4..1e9ca71 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
@@ -16,7 +17,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
@@ -24,9 +24,9 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -60,15 +60,13 @@
ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<String> targets =
- lookupResult.asLookupResultSuccess().getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
+ Set<String> targets = new HashSet<>();
+ lookupResult.forEach(
+ target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected =
ImmutableSet.of(
- I.class.getTypeName() + ".bar",
A.class.getTypeName() + ".bar",
J.class.getTypeName() + ".bar");
assertEquals(expected, targets);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodLambdaTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodLambdaTest.java
index c8df559..ccb80a5 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodLambdaTest.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
@@ -16,7 +17,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
@@ -24,9 +24,9 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -58,12 +58,11 @@
ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<String> targets =
- lookupResult.asLookupResultSuccess().getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
+ Set<String> targets = new HashSet<>();
+ lookupResult.forEach(
+ target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected =
ImmutableSet.of(A.class.getTypeName() + ".bar", I.class.getTypeName() + ".bar");
assertEquals(expected, targets);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java
index ce8ce66..b3aae9e 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
@@ -16,7 +17,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
@@ -24,9 +24,9 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -62,12 +62,11 @@
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<String> targets =
- lookupResult.asLookupResultSuccess().getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
+ Set<String> targets = new HashSet<>();
+ lookupResult.forEach(
+ target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected = ImmutableSet.of(J.class.getTypeName() + ".foo");
assertEquals(expected, targets);
}
@@ -106,12 +105,11 @@
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<String> targets =
- lookupResult.asLookupResultSuccess().getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
+ Set<String> targets = new HashSet<>();
+ lookupResult.forEach(
+ target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected = ImmutableSet.of(J.class.getTypeName() + ".foo");
assertEquals(expected, targets);
}
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DuplicateImportsTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DuplicateImportsTest.java
index da61d6a..7c50e54 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DuplicateImportsTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DuplicateImportsTest.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
@@ -16,7 +17,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
@@ -24,9 +24,9 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -60,12 +60,11 @@
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<String> targets =
- lookupResult.asLookupResultSuccess().getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
+ Set<String> targets = new HashSet<>();
+ lookupResult.forEach(
+ target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected = ImmutableSet.of(J.class.getTypeName() + ".foo");
assertEquals(expected, targets);
}
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceClInitTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceClInitTest.java
index 016347b..06f7e8d 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceClInitTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceClInitTest.java
@@ -66,7 +66,7 @@
() ->
appInfo
.resolveMethod(method.holder, method)
- .lookupVirtualDispatchTargets(context, appView));
+ .lookupVirtualDispatchTargets(context, appInfo));
}
private Matcher<String> getExpected() {
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java
index 0ba0226..c8fad67 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java
@@ -57,7 +57,7 @@
() ->
appInfo
.resolveMethod(method.holder, method)
- .lookupVirtualDispatchTargets(context, appView));
+ .lookupVirtualDispatchTargets(context, appInfo));
}
@Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/LambdaMultipleInterfacesTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/LambdaMultipleInterfacesTest.java
index eeca9fe..9cf0cf2 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/LambdaMultipleInterfacesTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/LambdaMultipleInterfacesTest.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
@@ -16,7 +17,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
@@ -24,9 +24,9 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -59,12 +59,11 @@
ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<String> targets =
- lookupResult.asLookupResultSuccess().getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
+ Set<String> targets = new HashSet<>();
+ lookupResult.forEach(
+ target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected =
ImmutableSet.of(A.class.getTypeName() + ".bar", J.class.getTypeName() + ".bar");
assertEquals(expected, targets);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/MultipleImplementsTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/MultipleImplementsTest.java
index 64c81fa..0013abb 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/MultipleImplementsTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/MultipleImplementsTest.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
@@ -16,7 +17,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
@@ -24,9 +24,9 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -59,12 +59,11 @@
ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<String> targets =
- lookupResult.asLookupResultSuccess().getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
+ Set<String> targets = new HashSet<>();
+ lookupResult.forEach(
+ target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected =
ImmutableSet.of(B.class.getTypeName() + ".foo", C.class.getTypeName() + ".foo");
assertEquals(expected, targets);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SimpleInterfaceInvokeTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SimpleInterfaceInvokeTest.java
index a21d3e1..fbe03e3 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SimpleInterfaceInvokeTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SimpleInterfaceInvokeTest.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.resolution.interfacetargets;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
@@ -16,7 +17,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
@@ -24,9 +24,9 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -60,12 +60,17 @@
ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<String> targets =
- lookupResult.asLookupResultSuccess().getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
+ assertFalse(lookupResult.asLookupResultSuccess().hasLambdaTargets());
+ Set<String> targets = new HashSet<>();
+ lookupResult
+ .asLookupResultSuccess()
+ .forEach(
+ target -> targets.add(target.getMethod().qualifiedName()),
+ lambda -> {
+ assert false;
+ });
ImmutableSet<String> expected =
ImmutableSet.of(A.class.getTypeName() + ".foo", B.class.getTypeName() + ".foo");
assertEquals(expected, targets);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubInterfaceOverridesTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubInterfaceOverridesTest.java
index 80caa62..4ccdb10 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubInterfaceOverridesTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubInterfaceOverridesTest.java
@@ -5,7 +5,9 @@
package com.android.tools.r8.resolution.interfacetargets;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
@@ -15,7 +17,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
@@ -23,9 +24,9 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -59,17 +60,19 @@
ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<String> targets =
- lookupResult.asLookupResultSuccess().getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
+ assertFalse(lookupResult.asLookupResultSuccess().hasLambdaTargets());
+ Set<String> targets = new HashSet<>();
+ lookupResult
+ .asLookupResultSuccess()
+ .forEach(
+ target -> targets.add(target.getMethod().qualifiedName()),
+ lambda -> {
+ fail();
+ });
ImmutableSet<String> expected =
- ImmutableSet.of(
- J.class.getTypeName() + ".foo",
- A.class.getTypeName() + ".foo",
- C.class.getTypeName() + ".foo");
+ ImmutableSet.of(J.class.getTypeName() + ".foo", A.class.getTypeName() + ".foo");
assertEquals(expected, targets);
}
@@ -115,7 +118,6 @@
@Override
public void foo() {
System.out.println("A.foo");
- ;
}
}
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeMissingOverridesTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeMissingOverridesTest.java
index 713d80e..d5e8bb3 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeMissingOverridesTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeMissingOverridesTest.java
@@ -5,7 +5,9 @@
package com.android.tools.r8.resolution.interfacetargets;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
@@ -15,7 +17,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
@@ -23,9 +24,9 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -58,12 +59,17 @@
ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<String> targets =
- lookupResult.asLookupResultSuccess().getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
+ assertFalse(lookupResult.asLookupResultSuccess().hasLambdaTargets());
+ Set<String> targets = new HashSet<>();
+ lookupResult
+ .asLookupResultSuccess()
+ .forEach(
+ target -> targets.add(target.getMethod().qualifiedName()),
+ lambda -> {
+ fail();
+ });
ImmutableSet<String> expected =
ImmutableSet.of(B.class.getTypeName() + ".foo", C.class.getTypeName() + ".foo");
assertEquals(expected, targets);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeOverridesTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeOverridesTest.java
index ba468e0..23f1c7b 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeOverridesTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeOverridesTest.java
@@ -5,7 +5,9 @@
package com.android.tools.r8.resolution.interfacetargets;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
@@ -15,7 +17,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
@@ -23,9 +24,9 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -58,12 +59,17 @@
ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<String> targets =
- lookupResult.asLookupResultSuccess().getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
+ assertFalse(lookupResult.asLookupResultSuccess().hasLambdaTargets());
+ Set<String> targets = new HashSet<>();
+ lookupResult
+ .asLookupResultSuccess()
+ .forEach(
+ target -> targets.add(target.getMethod().qualifiedName()),
+ lambda -> {
+ fail();
+ });
ImmutableSet<String> expected =
ImmutableSet.of(A.class.getTypeName() + ".foo", B.class.getTypeName() + ".foo");
assertEquals(expected, targets);
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateClasspathWidenTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateClasspathWidenTest.java
index a714a03..379b6e8 100644
--- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateClasspathWidenTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateClasspathWidenTest.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
@@ -13,7 +14,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
@@ -25,9 +25,9 @@
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
import java.nio.file.Path;
+import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -71,12 +71,11 @@
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Abstract.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<String> targets =
- lookupResult.asLookupResultSuccess().getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
+ Set<String> targets = new HashSet<>();
+ lookupResult.forEach(
+ target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected = ImmutableSet.of(C.class.getTypeName() + ".foo");
assertEquals(expected, targets);
}
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryTest.java
index eb08197..b5f36aa 100644
--- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryTest.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
@@ -14,7 +15,6 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRunResult;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
@@ -25,9 +25,9 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -59,17 +59,12 @@
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<String> targets =
- lookupResult.asLookupResultSuccess().getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
- ImmutableSet<String> expected =
- ImmutableSet.of(
- A.class.getTypeName() + ".bar",
- B.class.getTypeName() + ".bar",
- D.class.getTypeName() + ".bar");
+ Set<String> targets = new HashSet<>();
+ lookupResult.forEach(
+ target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
+ ImmutableSet<String> expected = ImmutableSet.of(D.class.getTypeName() + ".bar");
assertEquals(expected, targets);
}
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryWithNarrowingTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryWithNarrowingTest.java
index 619ee45..2305c93 100644
--- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryWithNarrowingTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryWithNarrowingTest.java
@@ -4,9 +4,9 @@
package com.android.tools.r8.resolution.packageprivate;
-import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
@@ -15,8 +15,8 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRunResult;
import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.graph.AccessFlags;
import com.android.tools.r8.graph.AppView;
-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.LookupResult;
@@ -28,9 +28,9 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -63,16 +63,13 @@
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<String> targets =
- lookupResult.asLookupResultSuccess().getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
+ Set<String> targets = new HashSet<>();
+ lookupResult.forEach(
+ target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected =
ImmutableSet.of(
- A.class.getTypeName() + ".bar",
- B.class.getTypeName() + ".bar",
D.class.getTypeName() + ".bar");
assertEquals(expected, targets);
}
@@ -96,19 +93,18 @@
@Test
public void testR8()
throws ExecutionException, CompilationFailedException, IOException, NoSuchMethodException {
- // TODO(b/149363086): Fix test.
testForR8(parameters.getBackend())
.addProgramClasses(A.class, B.class, C.class, Main.class)
.addProgramClassFileData(getDWithPackagePrivateFoo())
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
.run(parameters.getRuntime(), Main.class)
- .assertFailureWithErrorThatMatches(containsString("IllegalAccessError"));
+ .assertSuccessWithOutputLines(EXPECTED);
}
private byte[] getDWithPackagePrivateFoo() throws NoSuchMethodException, IOException {
return transformer(D.class)
- .setAccessFlags(D.class.getDeclaredMethod("bar"), m -> m.unsetPublic())
+ .setAccessFlags(D.class.getDeclaredMethod("bar"), AccessFlags::unsetPublic)
.transform();
}
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethod2Test.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethod2Test.java
index cc85b0d..e156624 100644
--- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethod2Test.java
+++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethod2Test.java
@@ -7,6 +7,7 @@
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
@@ -17,7 +18,6 @@
import com.android.tools.r8.TestRunResult;
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.graph.AppView;
-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.LookupResult;
@@ -31,9 +31,9 @@
import com.android.tools.r8.transformers.ClassTransformer;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -75,12 +75,11 @@
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<String> targets =
- lookupResult.asLookupResultSuccess().getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
+ Set<String> targets = new HashSet<>();
+ lookupResult.forEach(
+ target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
// TODO(b/148591377): The set should be empty.
ImmutableSet<String> expected = ImmutableSet.of(AbstractWidening.class.getTypeName() + ".foo");
assertEquals(expected, targets);
@@ -123,8 +122,7 @@
.addProgramClassFileData(getNonAbstractWithoutDeclaredMethods())
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
- .run(parameters.getRuntime(), Main.class)
- .disassemble();
+ .run(parameters.getRuntime(), Main.class);
if (parameters.isDexRuntime()
&& parameters.getRuntime().asDex().getVm().isOlderThanOrEqual(DexVm.ART_4_4_4_TARGET)) {
runResult.assertFailure();
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethodTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethodTest.java
index 42efe5c..d711ba7 100644
--- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethodTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethodTest.java
@@ -7,6 +7,7 @@
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
@@ -17,7 +18,6 @@
import com.android.tools.r8.TestRunResult;
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.graph.AppView;
-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.LookupResult;
@@ -31,9 +31,9 @@
import com.android.tools.r8.transformers.ClassTransformer;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -74,12 +74,11 @@
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<String> targets =
- lookupResult.asLookupResultSuccess().getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
+ Set<String> targets = new HashSet<>();
+ lookupResult.forEach(
+ target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
// TODO(b/148591377): The set should be empty.
ImmutableSet<String> expected = ImmutableSet.of(Abstract.class.getTypeName() + ".foo");
assertEquals(expected, targets);
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/WidenAccessOutsidePackageTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/WidenAccessOutsidePackageTest.java
index b761626..ec013d6 100644
--- a/src/test/java/com/android/tools/r8/resolution/packageprivate/WidenAccessOutsidePackageTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/WidenAccessOutsidePackageTest.java
@@ -5,6 +5,8 @@
package com.android.tools.r8.resolution.packageprivate;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
@@ -14,7 +16,6 @@
import com.android.tools.r8.TestRunResult;
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.graph.AppView;
-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.LookupResult;
@@ -24,9 +25,9 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -59,13 +60,17 @@
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
- Set<String> targets =
- lookupResult.asLookupResultSuccess().getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
- ImmutableSet<String> expected =
- ImmutableSet.of(A.class.getTypeName() + ".bar", B.class.getTypeName() + ".bar");
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+ assertFalse(lookupResult.asLookupResultSuccess().hasLambdaTargets());
+ Set<String> targets = new HashSet<>();
+ lookupResult
+ .asLookupResultSuccess()
+ .forEach(
+ target -> targets.add(target.getMethod().qualifiedName()),
+ lambda -> {
+ fail();
+ });
+ ImmutableSet<String> expected = ImmutableSet.of(B.class.getTypeName() + ".bar");
assertEquals(expected, targets);
}
@@ -85,13 +90,12 @@
@Test
public void testR8() throws ExecutionException, CompilationFailedException, IOException {
- // TODO(b/149363086): Fix expectation.
testForR8(parameters.getBackend())
.addProgramClasses(A.class, B.class, C.class, Main.class)
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(EXPECTED_DALVIK);
+ .assertSuccessWithOutputLines(EXPECTED);
}
public static class C extends B {
diff --git a/src/test/java/com/android/tools/r8/resolution/singletarget/one/SubSubClassThree.java b/src/test/java/com/android/tools/r8/resolution/singletarget/one/SubSubClassThree.java
index 9b4e20a..1cd4170 100644
--- a/src/test/java/com/android/tools/r8/resolution/singletarget/one/SubSubClassThree.java
+++ b/src/test/java/com/android/tools/r8/resolution/singletarget/one/SubSubClassThree.java
@@ -5,6 +5,7 @@
public abstract class SubSubClassThree extends AbstractSubClass {
+ @Override
public void overridenInAbstractClassOnly() {
System.out.println(SubSubClassThree.class.getCanonicalName());
}
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/AbstractInMiddleTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/AbstractInMiddleTest.java
index 88c7b9e..ebbb52f 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/AbstractInMiddleTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/AbstractInMiddleTest.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
@@ -16,7 +17,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
@@ -24,9 +24,9 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -59,17 +59,14 @@
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<String> targets =
- lookupResult.asLookupResultSuccess().getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
- // TODO(b/148591377): Should we report B.foo()?
+ Set<String> targets = new HashSet<>();
+ lookupResult.forEach(
+ target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected =
ImmutableSet.of(
A.class.getTypeName() + ".foo",
- B.class.getTypeName() + ".foo",
C.class.getTypeName() + ".foo");
assertEquals(expected, targets);
}
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceSubTypeTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceSubTypeTest.java
index 00ef1db..4f572ee 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceSubTypeTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceSubTypeTest.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
@@ -16,7 +17,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
@@ -24,9 +24,9 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -59,12 +59,11 @@
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<String> targets =
- lookupResult.asLookupResultSuccess().getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
+ Set<String> targets = new HashSet<>();
+ lookupResult.forEach(
+ target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected = ImmutableSet.of(J.class.getTypeName() + ".foo");
assertEquals(expected, targets);
}
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceTest.java
index 3c30267..55d4138 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceTest.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
@@ -16,7 +17,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
@@ -24,9 +24,9 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -59,12 +59,11 @@
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<String> targets =
- lookupResult.asLookupResultSuccess().getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
+ Set<String> targets = new HashSet<>();
+ lookupResult.forEach(
+ target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected = ImmutableSet.of(J.class.getTypeName() + ".foo");
assertEquals(expected, targets);
}
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java
index dccf199..2800d2c 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
@@ -16,7 +17,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
@@ -24,9 +24,9 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -62,12 +62,11 @@
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<String> targets =
- lookupResult.asLookupResultSuccess().getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
+ Set<String> targets = new HashSet<>();
+ lookupResult.forEach(
+ target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected = ImmutableSet.of(J.class.getTypeName() + ".foo");
assertEquals(expected, targets);
}
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvalidResolutionToThisTarget.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvalidResolutionToThisTarget.java
index 8eb3399..d909591 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvalidResolutionToThisTarget.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvalidResolutionToThisTarget.java
@@ -62,7 +62,7 @@
assertTrue(resolutionResult.isSingleResolution());
DexType mainType = buildType(Main.class, appInfo.dexItemFactory());
DexProgramClass main = appView.definitionForProgramType(mainType);
- assertNull(resolutionResult.lookupVirtualDispatchTarget(main, appView));
+ assertNull(resolutionResult.lookupVirtualDispatchTarget(main, appInfo));
});
assertThat(
foo.getMessage(),
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvokeVirtualToInterfaceDefinitionTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvokeVirtualToInterfaceDefinitionTest.java
index 7d32527..b2c70f9 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvokeVirtualToInterfaceDefinitionTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvokeVirtualToInterfaceDefinitionTest.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
@@ -16,7 +17,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
@@ -24,9 +24,9 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -58,12 +58,11 @@
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<String> targets =
- lookupResult.asLookupResultSuccess().getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
+ Set<String> targets = new HashSet<>();
+ lookupResult.forEach(
+ target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected = ImmutableSet.of(I.class.getTypeName() + ".foo");
assertEquals(expected, targets);
}
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java
index ffc9114..b2e57b2 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java
@@ -6,13 +6,13 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
@@ -26,6 +26,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@@ -61,7 +62,8 @@
methodToBeKept,
classToBeKept,
Arrays.asList(expectedMethodHolders),
- Arrays.asList(I.class, A.class, B.class, C.class));
+ Arrays.asList(I.class, A.class, B.class, C.class, Main.class),
+ Main.class);
}
private LookupResultSuccess testLookup(
@@ -69,7 +71,8 @@
Class<?> methodToBeKept,
Class<?> classToBeKept,
Collection<Class<?>> expectedMethodHolders,
- Collection<Class<?>> classes)
+ Collection<Class<?>> classes,
+ Class<?> main)
throws Exception {
AppView<AppInfoWithLiveness> appView =
computeAppViewWithLiveness(
@@ -78,6 +81,7 @@
List<ProguardConfigurationRule> rules = new ArrayList<>();
rules.addAll(buildKeepRuleForClassAndMethods(methodToBeKept, factory));
rules.addAll(buildKeepRuleForClass(classToBeKept, factory));
+ rules.addAll(buildKeepRuleForClassAndMethods(main, factory));
return rules;
});
AppInfoWithLiveness appInfo = appView.appInfo();
@@ -85,13 +89,12 @@
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Unrelated.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
- Set<String> targets =
- lookupResultSuccess.getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
+ Set<String> targets = new HashSet<>();
+ lookupResult.forEach(
+ target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
Set<String> expected =
expectedMethodHolders.stream()
.map(c -> c.getTypeName() + ".foo")
@@ -237,19 +240,18 @@
LookupResult lookupResult =
resolutionResult.lookupVirtualDispatchTargets(
classB,
- appView,
+ appInfo,
(type, subTypeConsumer, callSiteConsumer) -> {
- if (type == typeA) {
+ if (type == typeB) {
subTypeConsumer.accept(classB);
}
},
reference -> false);
assertTrue(lookupResult.isLookupResultSuccess());
LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
- Set<String> targets =
- lookupResultSuccess.getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
+ Set<String> targets = new HashSet<>();
+ lookupResult.forEach(
+ target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
Set<String> expected = ImmutableSet.of(A.class.getTypeName() + ".foo");
assertEquals(expected, targets);
assertTrue(lookupResultSuccess.isComplete());
@@ -267,13 +269,12 @@
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Unrelated.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
- Set<String> targets =
- lookupResultSuccess.getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
+ Set<String> targets = new HashSet<>();
+ lookupResult.forEach(
+ target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
Set<String> expected = ImmutableSet.of(Unrelated.class.getTypeName() + ".foo");
assertEquals(expected, targets);
assertTrue(lookupResultSuccess.isIncomplete());
@@ -334,7 +335,8 @@
I.class,
Z.class,
Collections.singleton(X.class),
- Arrays.asList(X.class, I.class, Y.class, Z.class))
+ Arrays.asList(X.class, I.class, Y.class, Z.class),
+ MainXYZ.class)
.isIncomplete());
}
@@ -368,6 +370,17 @@
}
}
+ public static class Main {
+
+ public static void main(String[] args) {
+ // This is necessary for considering the classes as instantiated.
+ new Unrelated();
+ new A();
+ new B();
+ new C();
+ }
+ }
+
public static class X {
public void foo() {
@@ -378,4 +391,14 @@
public static class Y extends X implements I {}
public static class Z extends Y {}
+
+ public static class MainXYZ {
+
+ public static void main(String[] args) {
+ // This is necessary for considering the classes as instantiated.
+ new X();
+ new Y();
+ new Z();
+ }
+ }
}
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateChainTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateChainTest.java
index 37c071a..e0f4416 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateChainTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateChainTest.java
@@ -7,6 +7,7 @@
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
@@ -16,7 +17,6 @@
import com.android.tools.r8.TestRunResult;
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.graph.AppView;
-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.LookupResult;
@@ -27,9 +27,9 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -64,14 +64,12 @@
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(TopRunner.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<String> targets =
- lookupResult.asLookupResultSuccess().getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
- ImmutableSet<String> expected =
- ImmutableSet.of(Top.class.getTypeName() + ".clear", Middle.class.getTypeName() + ".clear");
+ Set<String> targets = new HashSet<>();
+ lookupResult.forEach(
+ target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
+ ImmutableSet<String> expected = ImmutableSet.of(Middle.class.getTypeName() + ".clear");
assertEquals(expected, targets);
}
@@ -91,13 +89,12 @@
@Test
public void testR8() throws ExecutionException, CompilationFailedException, IOException {
- // TODO(b/148584615): Fix test.
testForR8(parameters.getBackend())
.addProgramClasses(Top.class, Middle.class, Bottom.class, TopRunner.class, Main.class)
.addKeepMainRule(Main.class)
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
- .assertFailureWithErrorThatMatches(containsString("AbstractMethodError"));
+ .assertSuccessWithOutputLines(EXPECTED);
}
public static class Bottom extends Middle {
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideTest.java
index dfc9d43..03425c6 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideTest.java
@@ -7,6 +7,7 @@
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
@@ -18,7 +19,6 @@
import com.android.tools.r8.TestRunResult;
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.graph.AppView;
-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.LookupResult;
@@ -30,9 +30,9 @@
import com.android.tools.r8.utils.DescriptorUtils;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -44,8 +44,8 @@
private static final String[] EXPECTED =
new String[] {"ViewModel.clear()", "MyViewModel.clear()", "ViewModel.clear()"};
- private static final String[] R8_OUTPUT =
- new String[] {"MyViewModel.clear()", "MyViewModel.clear()", "MyViewModel.clear()"};
+ private static final String[] AMBIGUOUS_EXPECTED_OUTPUT =
+ new String[] {"ViewModel.clear()", "MyViewModel.clear()", "MyViewModel.clear()"};
private final TestParameters parameters;
@@ -72,12 +72,11 @@
DexProgramClass context =
appView.definitionForProgramType(
buildType(ViewModelRunner.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<String> targets =
- lookupResult.asLookupResultSuccess().getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
+ Set<String> targets = new HashSet<>();
+ lookupResult.forEach(
+ target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected = ImmutableSet.of(ViewModel.class.getTypeName() + ".clear");
assertEquals(expected, targets);
}
@@ -99,13 +98,12 @@
@Test
public void testR8() throws ExecutionException, CompilationFailedException, IOException {
- // TODO(b/148429150): Fix R8 to output expected.
testForR8(parameters.getBackend())
.addProgramClasses(MyViewModel.class, Main.class, ViewModel.class, ViewModelRunner.class)
.addKeepMainRule(Main.class)
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(R8_OUTPUT);
+ .assertSuccessWithOutputLines(EXPECTED);
}
@Test
@@ -121,7 +119,7 @@
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultFailure());
}
@@ -137,21 +135,20 @@
&& parameters.getRuntime().asDex().getVm().isOlderThanOrEqual(DexVm.ART_4_4_4_TARGET)) {
runResult.assertFailureWithErrorThatMatches(containsString("clear overrides final"));
} else {
- runResult.assertFailureWithErrorThatMatches(containsString("java.lang.IllegalAccessError"));
+ runResult.assertFailureWithErrorThatThrows(IllegalAccessError.class);
}
}
@Test
public void testR8WithInvalidInvoke()
throws ExecutionException, CompilationFailedException, IOException {
- // TODO(b/148429150): Fix R8 to output expected.
testForR8(parameters.getBackend())
.addProgramClasses(MyViewModel.class, ViewModel.class, ViewModelRunner.class)
.addProgramClassFileData(getModifiedMainWithIllegalInvokeToViewModelClear())
.addKeepMainRule(Main.class)
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
- .assertFailureWithErrorThatMatches(containsString("java.lang.NullPointerException"));
+ .assertFailureWithErrorThatThrows(IllegalAccessError.class);
}
@Test
@@ -169,12 +166,11 @@
DexProgramClass context =
appView.definitionForProgramType(
buildType(ViewModelRunnerWithCast.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<String> targets =
- lookupResult.asLookupResultSuccess().getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
+ Set<String> targets = new HashSet<>();
+ lookupResult.forEach(
+ target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected = ImmutableSet.of(ViewModel.class.getTypeName() + ".clear");
assertEquals(expected, targets);
}
@@ -191,22 +187,20 @@
&& parameters.getRuntime().asDex().getVm().isOlderThanOrEqual(DexVm.ART_4_4_4_TARGET)) {
runResult.assertFailureWithErrorThatMatches(containsString("clear overrides final"));
} else {
- runResult.assertSuccessWithOutputLines(
- "ViewModel.clear()", "MyViewModel.clear()", "MyViewModel.clear()");
+ runResult.assertSuccessWithOutputLines(AMBIGUOUS_EXPECTED_OUTPUT);
}
}
@Test
public void testR8WithAmbiguousInvoke()
throws ExecutionException, CompilationFailedException, IOException {
- // TODO(b/148429150): Fix R8 to output expected.
testForR8(parameters.getBackend())
.addProgramClasses(MyViewModel.class, ViewModel.class, Main.class)
.addProgramClassFileData(getModifiedViewModelRunnerWithDirectMyViewModelTarget())
.addKeepMainRule(Main.class)
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(R8_OUTPUT);
+ .assertSuccessWithOutputLines(AMBIGUOUS_EXPECTED_OUTPUT);
}
private byte[] getModifiedMainWithIllegalInvokeToViewModelClear() throws IOException {
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PrivateOverrideOfVirtualTargetTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PrivateOverrideOfVirtualTargetTest.java
index 915cd21..645fb55 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PrivateOverrideOfVirtualTargetTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PrivateOverrideOfVirtualTargetTest.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
@@ -13,7 +14,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
@@ -24,9 +24,9 @@
import com.android.tools.r8.utils.DescriptorUtils;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -57,18 +57,17 @@
buildClasses(A.class, Main.class)
.addClassProgramData(getBWithModifiedInvokes())
.build(),
- DefaultWithoutTopTest.Main.class);
+ Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(A.class, "bar", appInfo.dexItemFactory());
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(B.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<String> targets =
- lookupResult.asLookupResultSuccess().getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
+ Set<String> targets = new HashSet<>();
+ lookupResult.forEach(
+ target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected = ImmutableSet.of(A.class.getTypeName() + ".bar");
assertEquals(expected, targets);
}
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java
index 3afeb68..628c927 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java
@@ -5,7 +5,9 @@
package com.android.tools.r8.resolution.virtualtargets;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
@@ -16,7 +18,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
@@ -24,9 +25,9 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -59,12 +60,17 @@
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
- LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appView);
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
- Set<String> targets =
- lookupResult.asLookupResultSuccess().getMethodTargets().stream()
- .map(DexEncodedMethod::qualifiedName)
- .collect(Collectors.toSet());
+ assertFalse(lookupResult.asLookupResultSuccess().hasLambdaTargets());
+ Set<String> targets = new HashSet<>();
+ lookupResult
+ .asLookupResultSuccess()
+ .forEach(
+ target -> targets.add(target.getMethod().qualifiedName()),
+ lambda -> {
+ fail();
+ });
ImmutableSet<String> expected =
ImmutableSet.of(B.class.getTypeName() + ".foo", I.class.getTypeName() + ".foo");
assertEquals(expected, targets);
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java b/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
index 2f9a1ec..78a36f2 100644
--- a/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
@@ -48,6 +48,7 @@
private void configure(InternalOptions options) {
options.enableEnumValueOptimization = enableOptimization;
+ options.enableEnumSwitchMapRemoval = enableOptimization;
}
@Test
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/EnumValuesLengthTest.java b/src/test/java/com/android/tools/r8/rewrite/enums/EnumValuesLengthTest.java
new file mode 100644
index 0000000..7a5300d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/enums/EnumValuesLengthTest.java
@@ -0,0 +1,157 @@
+// 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.rewrite.enums;
+
+import static junit.framework.TestCase.assertTrue;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+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 EnumValuesLengthTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public EnumValuesLengthTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testValuesLengthRemoved() throws Exception {
+ testForR8(parameters.getBackend())
+ .addKeepMainRule(Main.class)
+ .addInnerClasses(EnumValuesLengthTest.class)
+ .noMinification()
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(
+ opt -> {
+ opt.enableEnumValueOptimization = true;
+ // We need to keep the switch map to ensure kept switch maps have their
+ // values array length rewritten.
+ opt.enableEnumSwitchMapRemoval = false;
+ })
+ .compile()
+ .inspect(this::assertValuesLengthRemoved)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("0", "2", "5", "a", "D", "c", "D");
+ }
+
+ @Test
+ public void testValuesLengthSwitchMapRemoved() throws Exception {
+ // Make sure SwitchMap can still be removed with valuesLength optimization.
+ testForR8(parameters.getBackend())
+ .addKeepMainRule(Main.class)
+ .addInnerClasses(EnumValuesLengthTest.class)
+ .noMinification()
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(
+ opt -> {
+ opt.enableEnumValueOptimization = true;
+ })
+ .compile()
+ .inspect(this::assertSwitchMapRemoved)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("0", "2", "5", "a", "D", "c", "D");
+ }
+
+ private void assertSwitchMapRemoved(CodeInspector inspector) {
+ assertTrue(
+ inspector.allClasses().stream()
+ .noneMatch(c -> !c.getDexClass().isEnum() && !c.getFinalName().endsWith("Main")));
+ }
+
+ private void assertValuesLengthRemoved(CodeInspector inspector) {
+ for (FoundClassSubject clazz : inspector.allClasses()) {
+ clazz.forAllMethods(this::assertValuesLengthRemoved);
+ }
+ }
+
+ private void assertValuesLengthRemoved(FoundMethodSubject method) {
+ assertTrue(method.streamInstructions().noneMatch(InstructionSubject::isArrayLength));
+ assertTrue(
+ method
+ .streamInstructions()
+ .noneMatch(
+ instr ->
+ instr.isInvokeStatic() && instr.getMethod().name.toString().equals("values")));
+ }
+
+ public static class Main {
+
+ @NeverClassInline
+ enum E0 {}
+
+ @NeverClassInline
+ enum E2 {
+ A,
+ B
+ }
+
+ @NeverClassInline
+ enum E5 {
+ A,
+ B,
+ C,
+ D,
+ E
+ }
+
+ @NeverClassInline
+ enum EUnusedValues {
+ A,
+ B,
+ C
+ }
+
+ @NeverClassInline
+ enum ESwitch {
+ A,
+ B,
+ C,
+ D
+ }
+
+ @SuppressWarnings("ResultOfMethodCallIgnored")
+ public static void main(String[] args) {
+ EUnusedValues.values();
+ System.out.println(E0.values().length);
+ System.out.println(E2.values().length);
+ System.out.println(E5.values().length);
+ System.out.println(switchOn(ESwitch.A));
+ System.out.println(switchOn(ESwitch.B));
+ System.out.println(switchOn(ESwitch.C));
+ System.out.println(switchOn(ESwitch.D));
+ }
+
+ // SwitchMaps feature an array length on values, and some of them are not removed.
+ @NeverInline
+ static char switchOn(ESwitch e) {
+ switch (e) {
+ case A:
+ return 'a';
+ case C:
+ return 'c';
+ default:
+ return 'D';
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/NonTargetedMethodTest.java b/src/test/java/com/android/tools/r8/shaking/NonTargetedMethodTest.java
new file mode 100644
index 0000000..8f560a2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/NonTargetedMethodTest.java
@@ -0,0 +1,95 @@
+// 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 static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class NonTargetedMethodTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("A::foo", "C::bar");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public NonTargetedMethodTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .enableInliningAnnotations()
+ .enableMergeAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .addInnerClasses(NonTargetedMethodTest.class)
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(this::checkIsFooPresent);
+ }
+
+ private void checkIsFooPresent(CodeInspector inspector) {
+ ClassSubject classSubject = inspector.clazz(C.class);
+ assertThat(classSubject, isPresent());
+ assertThat(classSubject.uniqueMethodWithName("foo"), not(isPresent()));
+ }
+
+ @NeverMerge
+ private static class A {
+ @NeverInline
+ public void foo() {
+ System.out.println("A::foo");
+ }
+ }
+
+ @NeverMerge
+ @NeverClassInline
+ private static class B extends A {
+ // No override of foo, but B::foo will be the only target.
+ }
+
+ @NeverClassInline
+ private static class C extends A {
+
+ // Non-targeted override.
+ @Override
+ public void foo() {
+ System.out.println("C::foo");
+ }
+
+ @NeverInline
+ public void bar() {
+ System.out.println("C::bar");
+ }
+ }
+
+ private static class Main {
+
+ public static void main(String[] args) {
+ new B().foo();
+ new C().bar();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java b/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java
index 5220c01..8673412 100644
--- a/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java
@@ -169,7 +169,8 @@
@Test
public void test() throws Exception {
// Run the program on Art after is has been compiled with R8.
- String referenceResult = expectedResults.apply(isDexVmBetween5_1_1and7_0_0(parameters));
+ String referenceResult =
+ expectedResults.apply(!enableClassInlining && isDexVmBetween5_1_1and7_0_0(parameters));
R8TestCompileResult compiled =
compilationResults.apply(
new Dimensions(
diff --git a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
index 327e1b5..9c88b7f 100644
--- a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
@@ -158,7 +158,9 @@
Optional<ClassSubject> thing = inspector.clazz("shaking8.Thing");
assertTrue(thing.isPresent());
assertTrue(thing.get().field("int", "aField"));
- assertFalse(inspector.clazz("shaking8.OtherThing").isPresent());
+ Optional<ClassSubject> otherThing = inspector.clazz("shaking8.OtherThing");
+ assertTrue(otherThing.isPresent());
+ assertTrue(otherThing.get().field("int", "otherField"));
assertTrue(inspector.clazz("shaking8.YetAnotherThing").isPresent());
}
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 fbe40d6..d246ba7 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
@@ -140,7 +140,11 @@
.collect(Collectors.joining("\n"));
String refMapping =
new String(
- Files.readAllBytes(Paths.get(EXAMPLES_DIR, "shaking1", "print-mapping.ref")),
+ Files.readAllBytes(
+ Paths.get(
+ EXAMPLES_DIR,
+ "shaking1",
+ "print-mapping-" + backend.name().toLowerCase() + ".ref")),
StandardCharsets.UTF_8);
Assert.assertEquals(sorted(refMapping), sorted(actualMapping));
}
diff --git a/src/test/java/com/android/tools/r8/shaking/assumevalues/AssumeValuesForConstantValuedFieldTest.java b/src/test/java/com/android/tools/r8/shaking/assumevalues/AssumeValuesForConstantValuedFieldTest.java
index 94e96f1..0c6c8b8 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumevalues/AssumeValuesForConstantValuedFieldTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumevalues/AssumeValuesForConstantValuedFieldTest.java
@@ -19,7 +19,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public AssumeValuesForConstantValuedFieldTest(TestParameters parameters) {
@@ -32,7 +32,7 @@
.addInnerClasses(AssumeValuesForConstantValuedFieldTest.class)
.addKeepMainRule(TestClass.class)
.addKeepRules("-assumevalues class * { static boolean field return false; }")
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutputLines(
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking8Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking8Test.java
index 5b1009e..9b8d209 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking8Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking8Test.java
@@ -3,6 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking.examples;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
import com.android.tools.r8.shaking.TreeShakingTest;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -50,11 +54,10 @@
}
private static void shaking8ThingClassIsAbstractAndEmpty(CodeInspector inspector) {
- ClassSubject clazz = inspector.clazz("shaking8.Thing");
- Assert.assertTrue(clazz.isAbstract());
- clazz.forAllMethods((method) -> Assert.fail());
- clazz = inspector.clazz("shaking8.YetAnotherThing");
- Assert.assertTrue(clazz.isAbstract());
- clazz.forAllMethods((method) -> Assert.fail());
+ ClassSubject thingClass = inspector.clazz("shaking8.Thing");
+ Assert.assertTrue(thingClass.isAbstract());
+ thingClass.forAllMethods((method) -> Assert.fail());
+ ClassSubject yetAnotherThingClass = inspector.clazz("shaking8.YetAnotherThing");
+ assertThat(yetAnotherThingClass, not(isPresent()));
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptViaClassInitializerTestRunner.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptViaClassInitializerTestRunner.java
index 707a415..366a97e 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptViaClassInitializerTestRunner.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptViaClassInitializerTestRunner.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -51,7 +52,7 @@
public enum T {
A(A::new);
- private final Supplier<Object> factory;
+ @NeverPropagateValue private final Supplier<Object> factory;
T(Supplier<Object> factory) {
this.factory = factory;
@@ -111,6 +112,7 @@
.enableGraphInspector(consumer)
.addProgramClassesAndInnerClasses(Main.class, A.class, T.class)
.addKeepMethodRules(mainMethod)
+ .enableMemberValuePropagationAnnotations()
.setMinApi(AndroidApiLevel.N)
.apply(
b -> {
diff --git a/src/test/java/com/android/tools/r8/shaking/proxy/testclasses/SubInterface.java b/src/test/java/com/android/tools/r8/shaking/proxy/testclasses/SubInterface.java
index 755c063..4c0823d 100644
--- a/src/test/java/com/android/tools/r8/shaking/proxy/testclasses/SubInterface.java
+++ b/src/test/java/com/android/tools/r8/shaking/proxy/testclasses/SubInterface.java
@@ -5,5 +5,6 @@
package com.android.tools.r8.shaking.proxy.testclasses;
public interface SubInterface extends BaseInterface {
+ @Override
void method();
}
diff --git a/src/test/java/com/android/tools/r8/shaking/proxy/testclasses/TestClass.java b/src/test/java/com/android/tools/r8/shaking/proxy/testclasses/TestClass.java
index 2113eb7..97fdb2b 100644
--- a/src/test/java/com/android/tools/r8/shaking/proxy/testclasses/TestClass.java
+++ b/src/test/java/com/android/tools/r8/shaking/proxy/testclasses/TestClass.java
@@ -11,6 +11,7 @@
this.name = name;
}
+ @Override
public void method() {
System.out.println(name);
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmFunctionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmFunctionSubject.java
index 28eff88..cdf57aa 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmFunctionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmFunctionSubject.java
@@ -54,18 +54,18 @@
public KmTypeSubject receiverParameterType() {
KmType kmType = kmFunction.getReceiverParameterType();
assert !isExtension() || kmType != null;
- return kmType == null ? null : new KmTypeSubject(kmType);
+ return kmType == null ? null : new KmTypeSubject(codeInspector, kmType);
}
@Override
public List<KmValueParameterSubject> valueParameters() {
return kmFunction.getValueParameters().stream()
- .map(KmValueParameterSubject::new)
+ .map(kmValueParameter -> new KmValueParameterSubject(codeInspector, kmValueParameter))
.collect(Collectors.toList());
}
@Override
public KmTypeSubject returnType() {
- return new KmTypeSubject(kmFunction.getReturnType());
+ return new KmTypeSubject(codeInspector, kmFunction.getReturnType());
}
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeProjectionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeProjectionSubject.java
new file mode 100644
index 0000000..790ecd2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeProjectionSubject.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.utils.codeinspector;
+
+import com.android.tools.r8.errors.Unreachable;
+import kotlinx.metadata.KmTypeProjection;
+
+public class KmTypeProjectionSubject extends Subject {
+ private final CodeInspector codeInspector;
+ private final KmTypeProjection kmTypeProjection;
+
+ KmTypeProjectionSubject(CodeInspector codeInspector, KmTypeProjection kmTypeProjection) {
+ this.codeInspector = codeInspector;
+ this.kmTypeProjection = kmTypeProjection;
+ }
+
+ public KmTypeSubject type() {
+ return new KmTypeSubject(codeInspector, kmTypeProjection.getType());
+ }
+
+ @Override
+ public boolean isPresent() {
+ return true;
+ }
+
+ @Override
+ public boolean isRenamed() {
+ return type().isRenamed();
+ }
+
+ @Override
+ public boolean isSynthetic() {
+ throw new Unreachable("Cannot determine if a type argument is synthetic");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeSubject.java
index aa52c67..5d7ec81 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeSubject.java
@@ -6,15 +6,20 @@
import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromKotlinClassifier;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.Box;
+import java.util.List;
+import java.util.stream.Collectors;
import kotlinx.metadata.KmType;
import kotlinx.metadata.KmTypeVisitor;
public class KmTypeSubject extends Subject {
+ private final CodeInspector codeInspector;
private final KmType kmType;
- KmTypeSubject(KmType kmType) {
+ KmTypeSubject(CodeInspector codeInspector, KmType kmType) {
assert kmType != null;
+ this.codeInspector = codeInspector;
this.kmType = kmType;
}
@@ -46,6 +51,12 @@
return getDescriptorFromKmType(kmType);
}
+ public List<KmTypeProjectionSubject> typeArguments() {
+ return kmType.getArguments().stream()
+ .map(kmTypeProjection -> new KmTypeProjectionSubject(codeInspector, kmTypeProjection))
+ .collect(Collectors.toList());
+ }
+
@Override
public boolean isPresent() {
return true;
@@ -53,7 +64,8 @@
@Override
public boolean isRenamed() {
- throw new Unreachable("Cannot determine if a type is renamed");
+ ClassSubject classSubject = codeInspector.clazz(Reference.classFromDescriptor(descriptor()));
+ return classSubject.isRenamed();
}
@Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/KmValueParameterSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/KmValueParameterSubject.java
index 1f96b7d..61a2531 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/KmValueParameterSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/KmValueParameterSubject.java
@@ -9,21 +9,23 @@
import kotlinx.metadata.KmValueParameter;
public class KmValueParameterSubject extends Subject {
+ private final CodeInspector codeInspector;
private final KmValueParameter kmValueParameter;
- KmValueParameterSubject(KmValueParameter kmValueParameter) {
+ KmValueParameterSubject(CodeInspector codeInspector, KmValueParameter kmValueParameter) {
+ this.codeInspector = codeInspector;
this.kmValueParameter = kmValueParameter;
}
public KmTypeSubject type() {
- return new KmTypeSubject(kmValueParameter.getType());
+ return new KmTypeSubject(codeInspector, kmValueParameter.getType());
}
public KmTypeSubject varargElementType() {
if (!isVararg()) {
return null;
}
- return new KmTypeSubject(kmValueParameter.getVarargElementType());
+ return new KmTypeSubject(codeInspector, kmValueParameter.getVarargElementType());
}
public boolean isVararg() {
diff --git a/third_party/proguardsettings.tar.gz.sha1 b/third_party/proguardsettings.tar.gz.sha1
index d5c183a..13c1c86 100644
--- a/third_party/proguardsettings.tar.gz.sha1
+++ b/third_party/proguardsettings.tar.gz.sha1
@@ -1 +1 @@
-7bb1badb721635d2baa7b63aab1e3a67434f94dd
\ No newline at end of file
+8f67c53a54b0f05c657310c1f76896e4d7f402db
\ No newline at end of file
diff --git a/third_party/youtube/youtube.android_15.08.tar.gz.sha1 b/third_party/youtube/youtube.android_15.08.tar.gz.sha1
index 285c854..357adeb 100644
--- a/third_party/youtube/youtube.android_15.08.tar.gz.sha1
+++ b/third_party/youtube/youtube.android_15.08.tar.gz.sha1
@@ -1 +1 @@
-803d7565c1d56568cfe5d8da18e7fe82bfcac006
\ No newline at end of file
+dc8d9b8f89d9284336897fa4cb2a925ff9e07c67
\ No newline at end of file
diff --git a/tools/compiledump.py b/tools/compiledump.py
index 6f78eef..6bfb5cb 100755
--- a/tools/compiledump.py
+++ b/tools/compiledump.py
@@ -23,9 +23,14 @@
help='Dump file to compile',
default=None)
parser.add_argument(
+ '--temp',
+ help='Temp directory to extract the dump to, allows you to rerun the command'
+ ' more easily in the terminal with changes',
+ default=None)
+ parser.add_argument(
'-c',
'--compiler',
- help='Compiler to use (default read from version file)',
+ help='Compiler to use',
default=None)
parser.add_argument(
'-v',
@@ -37,6 +42,10 @@
' <hash> to run that hash from master.',
default=None)
parser.add_argument(
+ '--r8-jar',
+ help='Path to an R8 jar.',
+ default=None)
+ parser.add_argument(
'--nolib',
help='Use the non-lib distribution (default uses the lib distribution)',
default=False,
@@ -103,9 +112,10 @@
return args.version
def determine_compiler(args, dump):
- compilers = ('d8', 'r8', 'r8full')
+ compilers = ['d8', 'r8', 'r8full']
if args.compiler not in compilers:
- error("Unable to determine a compiler to use. Valid options: %s" % compilers.join(', '))
+ error("Unable to determine a compiler to use. Specified %s,"
+ " Valid options: %s" % (args.compiler, ', '.join(compilers)))
return args.compiler
def determine_output(args, temp):
@@ -137,11 +147,15 @@
def run(args, otherargs):
with utils.TempDir() as temp:
+ if args.temp:
+ temp = args.temp
+ if not os.path.exists(temp):
+ os.makedirs(temp)
dump = read_dump(args, temp)
version = determine_version(args, dump)
compiler = determine_compiler(args, dump)
out = determine_output(args, temp)
- jar = download_distribution(args, version, temp)
+ jar = args.r8_jar if args.r8_jar else download_distribution(args, version, temp)
wrapper_dir = prepare_wrapper(jar, temp)
cmd = [jdk.GetJavaExecutable()]
if args.debug_agent:
diff --git a/tools/run_on_app.py b/tools/run_on_app.py
index b96ccf4..16db28d 100755
--- a/tools/run_on_app.py
+++ b/tools/run_on_app.py
@@ -188,8 +188,6 @@
# do Bug: #BUG in the commit message of disabling to ensure re-enabling
DISABLED_PERMUTATIONS = [
# (app, version, type), e.g., ('gmail', '180826.15', 'deploy'),
- ('youtube', '13.37', 'deploy'), # b/120977564
- ('youtube', '15.08', 'deploy'), # b/150267318
('youtube', '15.09', 'deploy'), # b/150267318
]
@@ -517,14 +515,6 @@
args.extend(['--main-dex-rules', rules])
if 'allow-type-errors' in values:
extra_args.append('-Dcom.android.tools.r8.allowTypeErrors=1')
- if 'proto-shrinking' in values:
- extra_args.append('-Dcom.android.tools.r8.applyInliningToInlinee=1')
- extra_args.append('-Dcom.android.tools.r8.fieldBitAccessAnalysis=1')
- extra_args.append('-Dcom.android.tools.r8.generatedExtensionRegistryShrinking=1')
- extra_args.append('-Dcom.android.tools.r8.generatedMessageLiteShrinking=1')
- extra_args.append('-Dcom.android.tools.r8.generatedMessageLiteBuilderShrinking=1')
- extra_args.append('-Dcom.android.tools.r8.stringSwitchConversion=1')
- extra_args.append('-Dcom.android.tools.r8.traverseOneOfAndRepeatedProtoFields=0')
if not options.no_libraries:
for lib in libraries:
diff --git a/tools/youtube_data.py b/tools/youtube_data.py
index e8accdd..828e04b 100644
--- a/tools/youtube_data.py
+++ b/tools/youtube_data.py
@@ -141,7 +141,6 @@
'pgconf': [
'%s_proguard.config' % V14_19_PREFIX,
'%s/proguardsettings/YouTubeRelease_proguard.config' % utils.THIRD_PARTY],
- 'proto-shrinking': 1,
'maindexrules' : [
os.path.join(V14_19_BASE, 'mainDexClasses.rules'),
os.path.join(V14_19_BASE, 'main-dex-classes-release-optimized.pgcfg'),
@@ -171,7 +170,6 @@
'pgconf': [
'%s_proguard.config' % V14_44_PREFIX,
'%s/proguardsettings/YouTubeRelease_proguard.config' % utils.THIRD_PARTY],
- 'proto-shrinking': 1,
'maindexrules' : [
os.path.join(V14_44_BASE, 'mainDexClasses.rules'),
os.path.join(V14_44_BASE, 'main-dex-classes-release-optimized.pgcfg'),
@@ -200,8 +198,8 @@
'libraries' : [os.path.join(V15_08_BASE, 'legacy_YouTubeRelease_combined_library_jars.jar')],
'pgconf': [
'%s_proguard.config' % V15_08_PREFIX,
+ '%s_proto_safety.pgcfg' % V15_08_PREFIX,
'%s/proguardsettings/YouTubeRelease_proguard.config' % utils.THIRD_PARTY],
- 'proto-shrinking': 1,
'maindexrules' : [
os.path.join(V15_08_BASE, 'mainDexClasses.rules'),
os.path.join(V15_08_BASE, 'main-dex-classes-release-optimized.pgcfg'),
@@ -231,7 +229,6 @@
'pgconf': [
'%s_proguard.config' % V15_09_PREFIX,
'%s/proguardsettings/YouTubeRelease_proguard.config' % utils.THIRD_PARTY],
- 'proto-shrinking': 1,
'maindexrules' : [
os.path.join(V15_09_BASE, 'mainDexClasses.rules'),
os.path.join(V15_09_BASE, 'main-dex-classes-release-optimized.pgcfg'),