Merge commit '74b2bd6f9854f668928607bab99fdc59d264783a' into dev-release
diff --git a/build.gradle b/build.gradle
index 33dfc26..d7c83e7 100644
--- a/build.gradle
+++ b/build.gradle
@@ -294,6 +294,7 @@
def r8LibTestPath = "$buildDir/classes/r8libtest"
def java11ClassFiles = "$buildDir/classes/java/mainJava11"
def r8RetracePath = "$buildDir/libs/r8retrace.jar"
+def r8RetraceExludeDepsPath = "$buildDir/libs/r8retrace-exclude-deps.jar"
def osString = OperatingSystem.current().isLinux() ? "linux" :
OperatingSystem.current().isMacOsX() ? "mac" : "windows"
@@ -1082,6 +1083,19 @@
outputs.file r8RetracePath
}
+task R8RetraceNoDeps {
+ dependsOn R8LibNoDeps
+ dependsOn r8LibCreateTask(
+ "RetraceNoDeps",
+ ["src/main/keep_retrace.txt"],
+ R8LibNoDeps,
+ r8RetraceExludeDepsPath,
+ "--release",
+ repackageDepsNew.outputs.files
+ ).dependsOn(R8LibNoDeps)
+ outputs.file r8RetraceExludeDepsPath
+}
+
task sourceJar(type: Jar, dependsOn: classes) {
classifier = 'src'
from sourceSets.main.allSource
diff --git a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
index 559d2f2..187402a 100644
--- a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
@@ -6,8 +6,8 @@
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.utils.MapUtils;
-import com.android.tools.r8.utils.collections.BidirectionalManyToOneHashMap;
-import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneMap;
+import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeHashMap;
+import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneRepresentativeMap;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableList;
@@ -26,8 +26,8 @@
*/
public final class AppliedGraphLens extends NonIdentityGraphLens {
- private final MutableBidirectionalManyToOneMap<DexType, DexType> renamedTypeNames =
- new BidirectionalManyToOneHashMap<>();
+ private final MutableBidirectionalManyToOneRepresentativeMap<DexType, DexType> renamedTypeNames =
+ new BidirectionalManyToOneRepresentativeHashMap<>();
private final BiMap<DexField, DexField> originalFieldSignatures = HashBiMap.create();
private final BiMap<DexMethod, DexMethod> originalMethodSignatures = HashBiMap.create();
@@ -39,6 +39,12 @@
public AppliedGraphLens(AppView<? extends AppInfoWithClassHierarchy> appView) {
super(appView.dexItemFactory(), GraphLens.getIdentityLens());
for (DexProgramClass clazz : appView.appInfo().classes()) {
+ // TODO(b/169395592): If merged classes were removed from the application this would not be
+ // necessary.
+ if (appView.graphLens().lookupType(clazz.getType()) != clazz.getType()) {
+ continue;
+ }
+
// Record original type names.
recordOriginalTypeNames(clazz, appView);
@@ -83,7 +89,12 @@
List<DexType> originalTypes = Lists.newArrayList(appView.graphLens().getOriginalTypes(type));
boolean isIdentity = originalTypes.size() == 1 && originalTypes.get(0) == type;
if (!isIdentity) {
- originalTypes.forEach(originalType -> renamedTypeNames.put(originalType, type));
+ originalTypes.forEach(
+ originalType -> {
+ assert !renamedTypeNames.containsKey(originalType);
+ renamedTypeNames.put(originalType, type);
+ });
+ renamedTypeNames.setRepresentative(type, appView.graphLens().getOriginalType(type));
}
}
@@ -94,11 +105,7 @@
@Override
public DexType getOriginalType(DexType type) {
- Set<DexType> originalTypeNames = renamedTypeNames.getKeys(type);
- if (!originalTypeNames.isEmpty()) {
- return originalTypeNames.iterator().next();
- }
- return type;
+ return renamedTypeNames.getRepresentativeKeyOrDefault(type, type);
}
@Override
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 70bad80..22a830e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.graph;
import static com.google.common.base.Predicates.alwaysFalse;
+import static com.google.common.base.Predicates.alwaysTrue;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.CompilationError;
@@ -121,6 +122,16 @@
}
}
+ public void forEachClassMethod(Consumer<? super DexClassAndMethod> consumer) {
+ forEachClassMethodMatching(alwaysTrue(), consumer);
+ }
+
+ public void forEachClassMethodMatching(
+ Predicate<DexEncodedMethod> predicate, Consumer<? super DexClassAndMethod> consumer) {
+ methodCollection.forEachMethodMatching(
+ predicate, method -> consumer.accept(DexClassAndMethod.create(this, method)));
+ }
+
@Override
public ClassAccessFlags getAccessFlags() {
return accessFlags;
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 0a17134..0446e09 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
@@ -27,11 +27,23 @@
}
}
+ public boolean isDefaultMethod() {
+ return getHolder().isInterface() && getDefinition().isDefaultMethod();
+ }
+
+ public boolean isStructurallyEqualTo(DexClassAndMethod other) {
+ return getDefinition() == other.getDefinition() && getHolder() == other.getHolder();
+ }
+
@Override
public MethodAccessFlags getAccessFlags() {
return getDefinition().getAccessFlags();
}
+ public DexProto getProto() {
+ return getReference().getProto();
+ }
+
@Override
public boolean isMethodTarget() {
return true;
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 4fcd92b..91203c8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -1383,13 +1383,13 @@
}
public static DexEncodedMethod createDesugaringForwardingMethod(
- DexEncodedMethod target, DexClass clazz, DexMethod forwardMethod, DexItemFactory factory) {
- DexMethod method = target.method;
+ DexClassAndMethod target, DexClass clazz, DexMethod forwardMethod, DexItemFactory factory) {
+ DexMethod method = target.getReference();
assert forwardMethod != null;
// New method will have the same name, proto, and also all the flags of the
// default method, including bridge flag.
DexMethod newMethod = factory.createMethod(clazz.type, method.proto, method.name);
- MethodAccessFlags newFlags = target.accessFlags.copy();
+ MethodAccessFlags newFlags = target.getAccessFlags().copy();
// Some debuggers (like IntelliJ) automatically skip synthetic methods on single step.
newFlags.setSynthetic();
newFlags.unsetAbstract();
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 385fb58..e6d824a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -682,14 +682,18 @@
// Library methods listed here are based on their original implementations. That is, we assume
// these cannot be overridden.
public final Set<DexMethod> libraryMethodsReturningNonNull =
- ImmutableSet.of(
- classMethods.getName,
- classMethods.getSimpleName,
- classMethods.forName,
- objectsMethods.requireNonNull,
- objectsMethods.requireNonNullWithMessage,
- objectsMethods.requireNonNullWithMessageSupplier,
- stringMembers.valueOf);
+ ImmutableSet.<DexMethod>builder()
+ .add(
+ classMethods.getName,
+ classMethods.getSimpleName,
+ classMethods.forName,
+ objectsMethods.requireNonNull,
+ objectsMethods.requireNonNullWithMessage,
+ objectsMethods.requireNonNullWithMessageSupplier,
+ stringMembers.valueOf)
+ .addAll(stringBufferMethods.appendMethods)
+ .addAll(stringBuilderMethods.appendMethods)
+ .build();
// TODO(b/119596718): More idempotent methods? Any singleton accessors? E.g.,
// java.util.Calendar#getInstance(...) // 4 variants
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index e00c1fe..d35b1a0 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -261,6 +261,10 @@
return factory.isConstructor(this);
}
+ public boolean mustBeInlinedIntoInstanceInitializer(DexItemFactory dexItemFactory) {
+ return getName().startsWith(dexItemFactory.temporaryConstructorMethodPrefix);
+ }
+
public DexMethod withExtraArgumentPrepended(DexType type, DexItemFactory dexItemFactory) {
return dexItemFactory.createMethod(
holder, dexItemFactory.prependTypeToProto(type, proto), name);
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index 376abd6..27284ea 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -7,7 +7,6 @@
import static com.android.tools.r8.ir.desugar.DesugaredLibraryWrapperSynthesizer.TYPE_WRAPPER_SUFFIX;
import static com.android.tools.r8.ir.desugar.DesugaredLibraryWrapperSynthesizer.VIVIFIED_TYPE_WRAPPER_SUFFIX;
import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX;
-import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.DISPATCH_CLASS_NAME_SUFFIX;
import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.EMULATE_LIBRARY_CLASS_NAME_SUFFIX;
import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX;
import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_GROUP_CLASS_NAME_PREFIX;
@@ -42,7 +41,7 @@
// Bundletool is merging classes that may originate from a build with an old version of R8.
// Allow merging of classes that use names from older versions of R8.
private static List<String> OLD_SYNTHESIZED_NAMES =
- ImmutableList.of("$r8$backportedMethods$utility", "$r8$java8methods$utility");
+ ImmutableList.of("$r8$backportedMethods$utility", "$r8$java8methods$utility", "$-DC");
public final DexString descriptor;
private String toStringCache = null;
@@ -313,9 +312,11 @@
String name = toSourceString();
// The synthesized classes listed here must always be unique to a program context and thus
// never duplicated for distinct inputs.
- return
- // Hygienic suffix.
- name.contains(COMPANION_CLASS_NAME_SUFFIX)
+ return false
+ // Hygienic suffix.
+ || name.contains(COMPANION_CLASS_NAME_SUFFIX)
+ || name.contains(ENUM_UNBOXING_UTILITY_CLASS_SUFFIX)
+ || name.contains(SyntheticArgumentClass.SYNTHETIC_CLASS_SUFFIX)
// New and hygienic synthesis infrastructure.
|| name.contains(SyntheticItems.INTERNAL_SYNTHETIC_CLASS_SEPARATOR)
|| name.contains(SyntheticItems.EXTERNAL_SYNTHETIC_CLASS_SEPARATOR)
@@ -336,11 +337,8 @@
private static boolean isSynthesizedTypeThatCouldBeDuplicated(String name) {
// Any entry that is removed from here must be added to OLD_SYNTHESIZED_NAMES to ensure that
// newer releases can be used to merge previous builds.
- return name.contains(ENUM_UNBOXING_UTILITY_CLASS_SUFFIX) // Shared among enums.
- || name.contains(SyntheticArgumentClass.SYNTHETIC_CLASS_SUFFIX)
- || name.contains(LAMBDA_CLASS_NAME_PREFIX) // Could collide.
+ return name.contains(LAMBDA_CLASS_NAME_PREFIX) // Could collide.
|| name.contains(LAMBDA_GROUP_CLASS_NAME_PREFIX) // Could collide.
- || name.contains(DISPATCH_CLASS_NAME_SUFFIX) // Shared on reference.
|| name.contains(OutlineOptions.CLASS_NAME) // Global singleton.
|| name.contains(TwrCloseResourceRewriter.UTILITY_CLASS_NAME) // Global singleton.
|| name.contains(NestBasedAccessDesugaring.NEST_CONSTRUCTOR_NAME) // Global singleton.
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
index 33063ba..3dc807c 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
@@ -52,10 +52,6 @@
definition.parameterAnnotationsList.collectIndexedItems(indexedItems);
}
- public boolean isStructurallyEqualTo(ProgramMethod other) {
- return getDefinition() == other.getDefinition() && getHolder() == other.getHolder();
- }
-
public void registerCodeReferences(UseRegistry registry) {
Code code = getDefinition().getCode();
if (code != null) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index 495be54..dd0486a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -155,7 +155,7 @@
new AllInstantiatedOrUninstantiated(appView),
new SameParentClass(),
new SameNestHost(),
- new PreserveMethodCharacteristics(),
+ new PreserveMethodCharacteristics(appView),
new SameFeatureSplit(appView),
new RespectPackageBoundaries(appView),
new DontMergeSynchronizedClasses(appView),
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
index 431fdb9..3adb35c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
@@ -4,12 +4,14 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethodSignature;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.horizontalclassmerging.MergeGroup;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.OptionalBool;
import com.google.common.collect.Iterables;
import java.util.ArrayList;
@@ -28,9 +30,10 @@
static class MethodCharacteristics {
private final MethodAccessFlags accessFlags;
+ private final boolean isAssumeNoSideEffectsMethod;
private final OptionalBool isLibraryMethodOverride;
- private MethodCharacteristics(DexEncodedMethod method) {
+ private MethodCharacteristics(boolean isAssumeNoSideEffectsMethod, DexEncodedMethod method) {
this.accessFlags =
MethodAccessFlags.builder()
.setPrivate(method.getAccessFlags().isPrivate())
@@ -39,9 +42,16 @@
.setStrict(method.getAccessFlags().isStrict())
.setSynchronized(method.getAccessFlags().isSynchronized())
.build();
+ this.isAssumeNoSideEffectsMethod = isAssumeNoSideEffectsMethod;
this.isLibraryMethodOverride = method.isLibraryMethodOverride();
}
+ static MethodCharacteristics create(
+ AppView<AppInfoWithLiveness> appView, DexEncodedMethod method) {
+ return new MethodCharacteristics(
+ appView.appInfo().isAssumeNoSideEffectsMethod(method.getReference()), method);
+ }
+
@Override
public int hashCode() {
return (accessFlags.hashCode() << 2) | isLibraryMethodOverride.ordinal();
@@ -56,12 +66,17 @@
return false;
}
MethodCharacteristics characteristics = (MethodCharacteristics) obj;
- return isLibraryMethodOverride == characteristics.isLibraryMethodOverride
- && accessFlags.equals(characteristics.accessFlags);
+ return accessFlags.equals(characteristics.accessFlags)
+ && isAssumeNoSideEffectsMethod == characteristics.isAssumeNoSideEffectsMethod
+ && isLibraryMethodOverride == characteristics.isLibraryMethodOverride;
}
}
- public PreserveMethodCharacteristics() {}
+ private final AppView<AppInfoWithLiveness> appView;
+
+ public PreserveMethodCharacteristics(AppView<AppInfoWithLiveness> appView) {
+ this.appView = appView;
+ }
public static class TargetGroup {
@@ -72,12 +87,12 @@
return group;
}
- public boolean tryAdd(DexProgramClass clazz) {
+ public boolean tryAdd(AppView<AppInfoWithLiveness> appView, DexProgramClass clazz) {
Map<DexMethodSignature, MethodCharacteristics> newMethods = new HashMap<>();
for (DexEncodedMethod method : clazz.methods()) {
DexMethodSignature signature = method.getSignature();
MethodCharacteristics existingCharacteristics = methodMap.get(signature);
- MethodCharacteristics methodCharacteristics = new MethodCharacteristics(method);
+ MethodCharacteristics methodCharacteristics = MethodCharacteristics.create(appView, method);
if (existingCharacteristics == null) {
newMethods.put(signature, methodCharacteristics);
continue;
@@ -97,10 +112,10 @@
List<TargetGroup> groups = new ArrayList<>();
for (DexProgramClass clazz : group) {
- boolean added = Iterables.any(groups, targetGroup -> targetGroup.tryAdd(clazz));
+ boolean added = Iterables.any(groups, targetGroup -> targetGroup.tryAdd(appView, clazz));
if (!added) {
TargetGroup newGroup = new TargetGroup();
- added = newGroup.tryAdd(clazz);
+ added = newGroup.tryAdd(appView, clazz);
assert added;
groups.add(newGroup);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
index ee770e8..a1c4e7b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.code;
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
import static com.android.tools.r8.ir.code.DominatorTree.Assumption.MAY_HAVE_UNREACHABLE_BLOCKS;
@@ -12,6 +13,9 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DebugLocalInfo;
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.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
@@ -19,6 +23,7 @@
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.Phi.RegisterReadType;
import com.android.tools.r8.ir.optimize.NestUtils;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.IteratorUtils;
import com.google.common.collect.ImmutableList;
@@ -269,6 +274,76 @@
}
@Override
+ public boolean replaceCurrentInstructionByNullCheckIfPossible(
+ AppView<?> appView, ProgramMethod context) {
+ Instruction toBeReplaced = current;
+ assert toBeReplaced != null;
+ assert toBeReplaced.isInstanceFieldInstruction() || toBeReplaced.isInvokeMethodWithReceiver();
+ if (toBeReplaced.hasUsedOutValue()) {
+ return false;
+ }
+ if (toBeReplaced.isInvokeDirect()) {
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ DexMethod invokedMethod = toBeReplaced.asInvokeDirect().getInvokedMethod();
+ if (invokedMethod.isInstanceInitializer(dexItemFactory)
+ || invokedMethod.mustBeInlinedIntoInstanceInitializer(dexItemFactory)) {
+ return false;
+ }
+ }
+ if (toBeReplaced.instructionMayHaveSideEffects(
+ appView, context, Instruction.SideEffectAssumption.RECEIVER_NOT_NULL)) {
+ return false;
+ }
+ Value receiver =
+ toBeReplaced.isInstanceFieldInstruction()
+ ? toBeReplaced.asInstanceFieldInstruction().object()
+ : toBeReplaced.asInvokeMethodWithReceiver().getReceiver();
+ if (receiver.isNeverNull()) {
+ removeOrReplaceByDebugLocalRead();
+ return true;
+ }
+ 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().objectMembers.getClass;
+ replacement = new InvokeVirtual(getClassMethod, null, ImmutableList.of(receiver));
+ }
+ replaceCurrentInstruction(replacement);
+ return true;
+ }
+
+ @Override
+ public boolean replaceCurrentInstructionByInitClassIfPossible(
+ AppView<AppInfoWithLiveness> appView, IRCode code, DexType type) {
+ Instruction toBeReplaced = current;
+ assert toBeReplaced != null;
+ assert toBeReplaced.isStaticFieldInstruction() || toBeReplaced.isInvokeStatic();
+ if (toBeReplaced.hasUsedOutValue()) {
+ return false;
+ }
+ ProgramMethod context = code.context();
+ if (toBeReplaced.instructionMayHaveSideEffects(
+ appView, context, Instruction.SideEffectAssumption.CLASS_ALREADY_INITIALIZED)) {
+ return false;
+ }
+ if (!type.classInitializationMayHaveSideEffectsInContext(appView, context)) {
+ removeOrReplaceByDebugLocalRead();
+ return true;
+ }
+ if (!appView.canUseInitClass()) {
+ return false;
+ }
+ DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(type));
+ if (clazz != null) {
+ Value dest = code.createValue(TypeElement.getInt());
+ replaceCurrentInstruction(new InitClass(dest, clazz.type));
+ }
+ return true;
+ }
+
+ @Override
public void replaceCurrentInstructionWithConstClass(
AppView<?> appView, IRCode code, DexType type, DebugLocalInfo localInfo) {
if (current == null) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
index 763d520..b849f75 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
@@ -47,7 +47,10 @@
}
public static ConstNumber asConstNumberOrNull(Instruction instruction) {
- return (ConstNumber) instruction;
+ if (instruction == null) {
+ return null;
+ }
+ return instruction.asConstNumber();
}
@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 a96760f..757a581 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
@@ -11,7 +11,9 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
import java.util.ListIterator;
import java.util.NoSuchElementException;
@@ -42,6 +44,18 @@
}
@Override
+ public boolean replaceCurrentInstructionByNullCheckIfPossible(
+ AppView<?> appView, ProgramMethod context) {
+ return instructionIterator.replaceCurrentInstructionByNullCheckIfPossible(appView, context);
+ }
+
+ @Override
+ public boolean replaceCurrentInstructionByInitClassIfPossible(
+ AppView<AppInfoWithLiveness> appView, IRCode code, DexType type) {
+ return instructionIterator.replaceCurrentInstructionByInitClassIfPossible(appView, code, type);
+ }
+
+ @Override
public void replaceCurrentInstructionWithConstClass(
AppView<?> appView, IRCode code, DexType type, DebugLocalInfo localInfo) {
instructionIterator.replaceCurrentInstructionWithConstClass(appView, code, type, localInfo);
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 e8ba6bd..689514f 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
@@ -130,7 +130,11 @@
}
public boolean hasUnusedOutValue() {
- return !hasOutValue() || !outValue().hasAnyUsers();
+ return !hasUsedOutValue();
+ }
+
+ public boolean hasUsedOutValue() {
+ return hasOutValue() && outValue().hasAnyUsers();
}
public Value outValue() {
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
index c4cceea..6b17a04 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
@@ -11,7 +11,9 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.Sets;
import java.util.ListIterator;
@@ -97,6 +99,11 @@
Value insertConstStringInstruction(AppView<?> appView, IRCode code, DexString value);
+ boolean replaceCurrentInstructionByNullCheckIfPossible(AppView<?> appView, ProgramMethod context);
+
+ boolean replaceCurrentInstructionByInitClassIfPossible(
+ AppView<AppInfoWithLiveness> appView, IRCode code, DexType type);
+
void replaceCurrentInstructionWithConstClass(
AppView<?> appView, IRCode code, DexType type, DebugLocalInfo localInfo);
diff --git a/src/main/java/com/android/tools/r8/ir/code/Invoke.java b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
index 31fd8f4..5793ec1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Invoke.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
@@ -144,6 +144,10 @@
abstract public DexType getReturnType();
+ public boolean hasArguments() {
+ return !arguments().isEmpty();
+ }
+
public boolean hasReturnTypeVoid(DexItemFactory factory) {
return getReturnType() == factory.voidType;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
index 0fc2768..bbb6d27 100644
--- a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
@@ -10,7 +10,9 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.Sets;
import java.util.ListIterator;
@@ -62,6 +64,18 @@
}
@Override
+ public boolean replaceCurrentInstructionByNullCheckIfPossible(
+ AppView<?> appView, ProgramMethod context) {
+ return currentBlockIterator.replaceCurrentInstructionByNullCheckIfPossible(appView, context);
+ }
+
+ @Override
+ public boolean replaceCurrentInstructionByInitClassIfPossible(
+ AppView<AppInfoWithLiveness> appView, IRCode code, DexType type) {
+ return currentBlockIterator.replaceCurrentInstructionByInitClassIfPossible(appView, code, type);
+ }
+
+ @Override
public void replaceCurrentInstructionWithConstClass(
AppView<?> appView, IRCode code, DexType type, DebugLocalInfo localInfo) {
currentBlockIterator.replaceCurrentInstructionWithConstClass(appView, code, type, localInfo);
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 e2b0e47..d08eb3e 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
@@ -442,8 +442,7 @@
}
private void synthesizeTwrCloseResourceUtilityClass(
- Builder<?> builder, ExecutorService executorService)
- throws ExecutionException {
+ Builder<?> builder, ExecutorService executorService) throws ExecutionException {
if (twrCloseResourceRewriter != null) {
twrCloseResourceRewriter.synthesizeUtilityClass(builder, executorService, options);
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java b/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java
index f6ad5f3..f348214 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java
@@ -56,14 +56,14 @@
public void registerInvokeVirtual(DexMethod method) {
registerBackportedMethodRewriting(method);
registerLibraryRetargeting(method, false);
- registerInterfaceMethodRewriting(method);
+ registerInterfaceMethodRewriting(method, false);
registerDesugaredLibraryAPIConverter(method);
}
@Override
public void registerInvokeDirect(DexMethod method) {
registerLibraryRetargeting(method, false);
- registerInterfaceMethodRewriting(method);
+ registerInterfaceMethodRewriting(method, false);
registerDesugaredLibraryAPIConverter(method);
}
@@ -73,10 +73,11 @@
}
}
- private void registerInterfaceMethodRewriting(DexMethod method) {
+ private void registerInterfaceMethodRewriting(DexMethod method, boolean isInvokeSuper) {
if (!needsDesugarging) {
needsDesugarging =
- interfaceMethodRewriter != null && interfaceMethodRewriter.needsRewriting(method);
+ interfaceMethodRewriter != null
+ && interfaceMethodRewriter.needsRewriting(method, isInvokeSuper, appView);
}
}
@@ -103,14 +104,14 @@
}
registerBackportedMethodRewriting(method);
registerLibraryRetargeting(method, false);
- registerInterfaceMethodRewriting(method);
+ registerInterfaceMethodRewriting(method, false);
registerDesugaredLibraryAPIConverter(method);
}
@Override
public void registerInvokeInterface(DexMethod method) {
registerLibraryRetargeting(method, true);
- registerInterfaceMethodRewriting(method);
+ registerInterfaceMethodRewriting(method, false);
registerDesugaredLibraryAPIConverter(method);
}
@@ -131,7 +132,7 @@
@Override
public void registerInvokeSuper(DexMethod method) {
registerLibraryRetargeting(method, false);
- registerInterfaceMethodRewriting(method);
+ registerInterfaceMethodRewriting(method, true);
registerDesugaredLibraryAPIConverter(method);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index 1e3b81f..6200603 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMember;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
@@ -18,6 +19,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GenericSignature;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
+import com.android.tools.r8.graph.LibraryMethod;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ProgramMethod;
@@ -25,6 +27,7 @@
import com.android.tools.r8.ir.synthetic.ExceptionThrowingSourceCode;
import com.android.tools.r8.ir.synthetic.SynthesizedCode;
import com.android.tools.r8.position.MethodPosition;
+import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.android.tools.r8.utils.WorkList;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
@@ -39,7 +42,6 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.objectweb.asm.Opcodes;
@@ -105,14 +107,14 @@
// List of methods that are known to be forwarded to by a forwarding method at this point in the
// class hierarchy. This set consists of the default interface methods, i.e., the targets of the
// forwarding methods, *not* the forwarding methods themselves.
- final ImmutableList<DexEncodedMethod> forwardedMethodTargets;
+ final ImmutableList<DexClassAndMethod> forwardedMethodTargets;
// If the forwarding methods for the emulated interface methods have not been added yet,
// this contains the information to add it in the subclasses.
final EmulatedInterfaceInfo emulatedInterfaceInfo;
ClassInfo(
ClassInfo parent,
- ImmutableList<DexEncodedMethod> forwardedMethodTargets,
+ ImmutableList<DexClassAndMethod> forwardedMethodTargets,
EmulatedInterfaceInfo emulatedInterfaceInfo) {
this.parent = parent;
this.forwardedMethodTargets = forwardedMethodTargets;
@@ -121,7 +123,7 @@
static ClassInfo create(
ClassInfo parent,
- ImmutableList<DexEncodedMethod> forwardedMethodTargets,
+ ImmutableList<DexClassAndMethod> forwardedMethodTargets,
EmulatedInterfaceInfo emulatedInterfaceInfo) {
return forwardedMethodTargets.isEmpty()
? parent
@@ -132,8 +134,11 @@
return this == EMPTY;
}
- boolean isTargetedByForwards(DexEncodedMethod method) {
- return forwardedMethodTargets.contains(method)
+ boolean isTargetedByForwards(DexClassAndMethod method) {
+ return IterableUtils.any(
+ forwardedMethodTargets,
+ DexClassAndMember::getDefinition,
+ definition -> definition == method.getDefinition())
|| (parent != null && parent.isTargetedByForwards(method));
}
}
@@ -394,7 +399,7 @@
// The computation of a class information and the insertions of forwarding methods.
private ClassInfo computeClassInfo(
DexClass clazz, ClassInfo superInfo, SignaturesInfo signatureInfo) {
- Builder<DexEncodedMethod> additionalForwards = ImmutableList.builder();
+ ImmutableList.Builder<DexClassAndMethod> additionalForwards = ImmutableList.builder();
// First we deal with non-emulated interface desugaring.
resolveForwardingMethods(clazz, superInfo, signatureInfo.signatures, additionalForwards);
// Second we deal with emulated interface, if one method has override in the current class,
@@ -507,7 +512,7 @@
DexClass clazz,
ClassInfo superInfo,
MethodSignatures signatures,
- Builder<DexEncodedMethod> additionalForwards) {
+ Builder<DexClassAndMethod> additionalForwards) {
if (clazz.isProgramClass() && appView.isAlreadyLibraryDesugared(clazz.asProgramClass())) {
return;
}
@@ -515,10 +520,10 @@
resolveForwardForSignature(
clazz,
wrapper.get(),
- (targetHolder, target) -> {
+ target -> {
if (!superInfo.isTargetedByForwards(target)) {
additionalForwards.add(target);
- addForwardingMethod(targetHolder, target, clazz);
+ addForwardingMethod(target, clazz);
}
});
}
@@ -527,7 +532,7 @@
// Looks up a method signature from the point of 'clazz', if it can dispatch to a default method
// the 'addForward' call-back is called with the target of the forward.
private void resolveForwardForSignature(
- DexClass clazz, DexMethod method, BiConsumer<DexClass, DexEncodedMethod> addForward) {
+ DexClass clazz, DexMethod method, Consumer<DexClassAndMethod> addForward) {
// Resolve the default method with base type as the symbolic holder as call sites are not known.
// The dispatch target is then looked up from the possible "instance" class.
// Doing so can cause an invalid invoke to become valid (at runtime resolution at a subtype
@@ -550,54 +555,52 @@
return;
}
- DexEncodedMethod target = virtualDispatchTarget.getDefinition();
- DexClass targetHolder = virtualDispatchTarget.getHolder();
// Don't forward if the target is explicitly marked as 'dont-rewrite'
- if (dontRewrite(targetHolder, target)) {
+ if (dontRewrite(virtualDispatchTarget)) {
return;
}
// If resolution targets a default interface method, forward it.
- if (targetHolder.isInterface() && target.isDefaultMethod()) {
- addForward.accept(targetHolder, target);
+ if (virtualDispatchTarget.isDefaultMethod()) {
+ addForward.accept(virtualDispatchTarget);
return;
}
// Remaining edge cases only pertain to desugaring of library methods.
- DexLibraryClass libraryHolder = targetHolder.asLibraryClass();
- if (libraryHolder == null || ignoreLibraryInfo()) {
+ if (!virtualDispatchTarget.isLibraryMethod() || ignoreLibraryInfo()) {
return;
}
- if (isRetargetMethod(libraryHolder, target)) {
- addForward.accept(targetHolder, target);
+ LibraryMethod libraryMethod = virtualDispatchTarget.asLibraryMethod();
+ if (isRetargetMethod(libraryMethod)) {
+ addForward.accept(virtualDispatchTarget);
return;
}
// If target is a non-interface library class it may be an emulated interface,
// except on a rewritten type, where L8 has already dealt with the desugaring.
- if (!libraryHolder.isInterface()
- && !appView.rewritePrefix.hasRewrittenType(libraryHolder.type, appView)) {
+ if (!libraryMethod.getHolder().isInterface()
+ && !appView.rewritePrefix.hasRewrittenType(libraryMethod.getHolderType(), appView)) {
// Here we use step-3 of resolution to find a maximally specific default interface method.
- DexClassAndMethod result = appInfo.lookupMaximallySpecificMethod(libraryHolder, method);
- if (result != null && rewriter.isEmulatedInterface(result.getHolder().type)) {
- addForward.accept(result.getHolder(), result.getDefinition());
+ DexClassAndMethod result =
+ appInfo.lookupMaximallySpecificMethod(libraryMethod.getHolder(), method);
+ if (result != null && rewriter.isEmulatedInterface(result.getHolderType())) {
+ addForward.accept(result);
}
}
}
- private boolean isRetargetMethod(DexLibraryClass holder, DexEncodedMethod method) {
+ private boolean isRetargetMethod(LibraryMethod method) {
assert needsLibraryInfo();
- assert holder.type == method.getHolderType();
- assert method.isNonPrivateVirtualMethod();
- if (method.isFinal()) {
- return false;
- }
- return appView.options().desugaredLibraryConfiguration.retargetMethod(method, appView) != null;
+ assert method.getDefinition().isNonPrivateVirtualMethod();
+ return !method.getAccessFlags().isFinal()
+ && appView.options().desugaredLibraryConfiguration.retargetMethod(method, appView) != null;
}
- private boolean dontRewrite(DexClass clazz, DexEncodedMethod method) {
- return needsLibraryInfo() && clazz.isLibraryClass() && rewriter.dontRewrite(method.method);
+ private boolean dontRewrite(DexClassAndMethod method) {
+ return needsLibraryInfo()
+ && method.getHolder().isLibraryClass()
+ && rewriter.dontRewrite(method);
}
// Construction of actual forwarding methods.
@@ -630,13 +633,12 @@
// Note: The parameter 'target' may be a public method on a class in case of desugared
// library retargeting (See below target.isInterface check).
- private void addForwardingMethod(DexClass targetHolder, DexEncodedMethod target, DexClass clazz) {
- assert targetHolder != null;
+ private void addForwardingMethod(DexClassAndMethod target, DexClass clazz) {
if (!clazz.isProgramClass()) {
return;
}
- DexEncodedMethod methodOnSelf = clazz.lookupMethod(target.method);
+ DexEncodedMethod methodOnSelf = clazz.lookupMethod(target.getReference());
if (methodOnSelf != null) {
throw new CompilationError(
"Attempt to add forwarding method that conflicts with existing method.",
@@ -645,13 +647,12 @@
new MethodPosition(methodOnSelf.method.asMethodReference()));
}
- DexMethod method = target.method;
// NOTE: Never add a forwarding method to methods of classes unknown or coming from android.jar
// even if this results in invalid code, these classes are never desugared.
// In desugared library, emulated interface methods can be overridden by retarget lib members.
DexMethod forwardMethod =
- targetHolder.isInterface()
- ? rewriter.defaultAsMethodOfCompanionClass(method)
+ target.getHolder().isInterface()
+ ? rewriter.defaultAsMethodOfCompanionClass(target)
: appView.options().desugaredLibraryConfiguration.retargetMethod(target, appView);
DexEncodedMethod desugaringForwardingMethod =
DexEncodedMethod.createDesugaringForwardingMethod(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
index c2b151c..3dc74ac 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
@@ -44,9 +44,10 @@
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.WorkList;
+import com.android.tools.r8.utils.collections.DexClassAndMethodSet;
import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -73,7 +74,7 @@
// the invoke needs to be rewritten.
private final Map<DexString, List<DexMethod>> nonFinalHolderRewrites = new IdentityHashMap<>();
// Non final virtual library methods requiring generation of emulated dispatch.
- private final Set<DexEncodedMethod> emulatedDispatchMethods = Sets.newIdentityHashSet();
+ private final DexClassAndMethodSet emulatedDispatchMethods = DexClassAndMethodSet.create();
public DesugaredLibraryRetargeter(AppView<?> appView) {
this.appView = appView;
@@ -316,9 +317,6 @@
DexEncodedMethod singleTarget = resolutionResult.getSingleTarget();
assert singleTarget != null;
retarget = getRetargetLibraryMember(singleTarget.method);
- if (retarget == null) {
- return null;
- }
}
return retarget;
}
@@ -356,52 +354,80 @@
DexClass typeClass = appView.definitionFor(inType);
if (typeClass != null) {
DexType newHolder = retargetCoreLibMember.get(methodName).get(inType);
- List<DexEncodedMethod> found = findDexEncodedMethodsWithName(methodName, typeClass);
- for (DexEncodedMethod encodedMethod : found) {
+ List<DexClassAndMethod> found = findMethodsWithName(methodName, typeClass);
+ for (DexClassAndMethod method : found) {
+ DexMethod methodReference = method.getReference();
if (!typeClass.isFinal()) {
- nonFinalHolderRewrites.putIfAbsent(encodedMethod.method.name, new ArrayList<>());
- nonFinalHolderRewrites.get(encodedMethod.method.name).add(encodedMethod.method);
- if (!encodedMethod.isStatic()) {
- if (InterfaceMethodRewriter.isEmulatedInterfaceDispatch(appView, encodedMethod)) {
+ nonFinalHolderRewrites.putIfAbsent(method.getName(), new ArrayList<>());
+ nonFinalHolderRewrites.get(method.getName()).add(methodReference);
+ if (!method.getAccessFlags().isStatic()) {
+ if (isEmulatedInterfaceDispatch(method)) {
// In this case interface method rewriter takes care of it.
continue;
- } else if (!encodedMethod.isFinal()) {
+ } else if (!method.getAccessFlags().isFinal()) {
// Virtual rewrites require emulated dispatch for inheritance.
// The call is rewritten to the dispatch holder class instead.
- handleEmulateDispatch(appView, encodedMethod);
- newHolder = dispatchHolderTypeFor(encodedMethod);
+ handleEmulateDispatch(appView, method);
+ newHolder = dispatchHolderTypeFor(method);
}
}
}
- DexProto proto = encodedMethod.method.proto;
- DexMethod method = appView.dexItemFactory().createMethod(inType, proto, methodName);
- retargetLibraryMember.put(
- method, computeRetargetMethod(method, encodedMethod.isStatic(), newHolder));
+ retargetLibraryMember.put(methodReference, computeRetargetMethod(method, newHolder));
}
}
}
}
}
- private DexMethod computeRetargetMethod(DexMethod method, boolean isStatic, DexType newHolder) {
- DexItemFactory factory = appView.dexItemFactory();
- DexProto newProto = isStatic ? method.proto : factory.prependHolderToProto(method);
- return factory.createMethod(newHolder, newProto, method.name);
+ private boolean isEmulatedInterfaceDispatch(DexClassAndMethod method) {
+ // Answers true if this method is already managed through emulated interface dispatch.
+ Map<DexType, DexType> emulateLibraryInterface =
+ appView.options().desugaredLibraryConfiguration.getEmulateLibraryInterface();
+ if (emulateLibraryInterface.isEmpty()) {
+ return false;
+ }
+ DexMethod methodToFind = method.getReference();
+
+ // Look-up all superclass and interfaces, if an emulated interface is found, and it implements
+ // the method, answers true.
+ WorkList<DexClass> worklist = WorkList.newIdentityWorkList(method.getHolder());
+ while (worklist.hasNext()) {
+ DexClass clazz = worklist.next();
+ if (clazz.isInterface()
+ && emulateLibraryInterface.containsKey(clazz.getType())
+ && clazz.lookupMethod(methodToFind) != null) {
+ return true;
+ }
+ // All super types are library class, or we are doing L8 compilation.
+ clazz.forEachImmediateSupertype(
+ superType -> {
+ DexClass superClass = appView.definitionFor(superType);
+ if (superClass != null) {
+ worklist.addIfNotSeen(superClass);
+ }
+ });
+ }
+ return false;
}
- private List<DexEncodedMethod> findDexEncodedMethodsWithName(
- DexString methodName, DexClass clazz) {
- List<DexEncodedMethod> found = new ArrayList<>();
- for (DexEncodedMethod encodedMethod : clazz.methods()) {
- if (encodedMethod.method.name == methodName) {
- found.add(encodedMethod);
- }
- }
- assert found.size() > 0 : "Should have found a method (library specifications).";
+ private DexMethod computeRetargetMethod(DexClassAndMethod method, DexType newHolder) {
+ DexItemFactory factory = appView.dexItemFactory();
+ DexProto newProto =
+ method.getAccessFlags().isStatic()
+ ? method.getProto()
+ : factory.prependHolderToProto(method.getReference());
+ return factory.createMethod(newHolder, newProto, method.getName());
+ }
+
+ private List<DexClassAndMethod> findMethodsWithName(DexString methodName, DexClass clazz) {
+ List<DexClassAndMethod> found = new ArrayList<>();
+ clazz.forEachClassMethodMatching(
+ definition -> definition.getName() == methodName, found::add);
+ assert !found.isEmpty() : "Should have found a method (library specifications).";
return found;
}
- private void handleEmulateDispatch(AppView<?> appView, DexEncodedMethod method) {
+ private void handleEmulateDispatch(AppView<?> appView, DexClassAndMethod method) {
emulatedDispatchMethods.add(method);
if (!appView.options().isDesugaredLibraryCompilation()) {
// Add rewrite rules so keeps rules are correctly generated in the program.
@@ -437,8 +463,8 @@
private void addInterfacesAndForwardingMethods(
ExecutorService executorService, IRConverter converter) throws ExecutionException {
assert !appView.options().isDesugaredLibraryCompilation();
- Map<DexType, List<DexEncodedMethod>> map = Maps.newIdentityHashMap();
- for (DexEncodedMethod emulatedDispatchMethod : emulatedDispatchMethods) {
+ Map<DexType, List<DexClassAndMethod>> map = Maps.newIdentityHashMap();
+ for (DexClassAndMethod emulatedDispatchMethod : emulatedDispatchMethods) {
map.putIfAbsent(emulatedDispatchMethod.getHolderType(), new ArrayList<>(1));
map.get(emulatedDispatchMethod.getHolderType()).add(emulatedDispatchMethod);
}
@@ -470,7 +496,7 @@
}
private boolean inherit(
- DexLibraryClass clazz, DexType typeToInherit, Set<DexEncodedMethod> retarget) {
+ DexLibraryClass clazz, DexType typeToInherit, DexClassAndMethodSet retarget) {
DexLibraryClass current = clazz;
while (current.type != appView.dexItemFactory().objectType) {
if (current.type == typeToInherit) {
@@ -491,13 +517,13 @@
private void addInterfacesAndForwardingMethods(
DexProgramClass clazz,
- List<DexEncodedMethod> methods,
+ List<DexClassAndMethod> methods,
Consumer<DexEncodedMethod> newForwardingMethodsConsumer) {
// DesugaredLibraryRetargeter emulate dispatch: insertion of a marker interface & forwarding
// methods.
// We cannot use the ClassProcessor since this applies up to 26, while the ClassProcessor
// applies up to 24.
- for (DexEncodedMethod method : methods) {
+ for (DexClassAndMethod method : methods) {
clazz.addExtraInterfaces(
Collections.singletonList(new ClassTypeSignature(dispatchInterfaceTypeFor(method))));
if (clazz.lookupVirtualMethod(method.getReference()) == null) {
@@ -508,7 +534,7 @@
}
}
- private DexEncodedMethod createForwardingMethod(DexEncodedMethod target, DexClass clazz) {
+ private DexEncodedMethod createForwardingMethod(DexClassAndMethod target, DexClass clazz) {
// NOTE: Never add a forwarding method to methods of classes unknown or coming from
// android.jar
// even if this results in invalid code, these classes are never desugared.
@@ -533,7 +559,7 @@
| Constants.ACC_INTERFACE);
ClassAccessFlags holderAccessFlags =
ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC);
- for (DexEncodedMethod emulatedDispatchMethod : emulatedDispatchMethods) {
+ for (DexClassAndMethod emulatedDispatchMethod : emulatedDispatchMethods) {
// Dispatch interface.
DexType interfaceType = dispatchInterfaceTypeFor(emulatedDispatchMethod);
DexEncodedMethod itfMethod =
@@ -560,7 +586,7 @@
}
private DexEncodedMethod generateInterfaceDispatchMethod(
- DexEncodedMethod emulatedDispatchMethod, DexType interfaceType) {
+ DexClassAndMethod emulatedDispatchMethod, DexType interfaceType) {
MethodAccessFlags flags =
MethodAccessFlags.fromSharedAccessFlags(
Constants.ACC_PUBLIC | Constants.ACC_ABSTRACT | Constants.ACC_SYNTHETIC, false);
@@ -582,7 +608,7 @@
}
private DexEncodedMethod generateHolderDispatchMethod(
- DexEncodedMethod emulatedDispatchMethod, DexType dispatchHolder, DexMethod itfMethod) {
+ DexClassAndMethod emulatedDispatchMethod, DexType dispatchHolder, DexMethod itfMethod) {
// The method should look like:
// static foo(rcvr, arg0, arg1) {
// if (rcvr instanceof interfaceType) {
@@ -613,7 +639,7 @@
}
private void reportInvalidLibrarySupertype(
- DexLibraryClass libraryClass, Set<DexEncodedMethod> retarget) {
+ DexLibraryClass libraryClass, DexClassAndMethodSet retarget) {
DexClass dexClass = appView.definitionFor(libraryClass.superType);
String message;
if (dexClass == null) {
@@ -634,15 +660,15 @@
retarget);
}
- private DexType dispatchInterfaceTypeFor(DexEncodedMethod method) {
+ private DexType dispatchInterfaceTypeFor(DexClassAndMethod method) {
return dispatchTypeFor(method, "dispatchInterface");
}
- private DexType dispatchHolderTypeFor(DexEncodedMethod method) {
+ private DexType dispatchHolderTypeFor(DexClassAndMethod method) {
return dispatchTypeFor(method, "dispatchHolder");
}
- private DexType dispatchTypeFor(DexEncodedMethod method, String suffix) {
+ private DexType dispatchTypeFor(DexClassAndMethod method, String suffix) {
String descriptor =
"L"
+ appView
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 47793a8..1529d2f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -4,6 +4,13 @@
package com.android.tools.r8.ir.desugar;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_CUSTOM;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_DIRECT;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_INTERFACE;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_STATIC;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_SUPER;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_VIRTUAL;
+
import com.android.tools.r8.DesugarGraphConsumer;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.dex.Constants;
@@ -36,8 +43,10 @@
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.InvokeCustom;
import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.InvokeSuper;
import com.android.tools.r8.ir.conversion.IRConverter;
@@ -102,7 +111,6 @@
// Public for testing.
public static final String EMULATE_LIBRARY_CLASS_NAME_SUFFIX = "$-EL";
- public static final String DISPATCH_CLASS_NAME_SUFFIX = "$-DC";
public static final String COMPANION_CLASS_NAME_SUFFIX = "$-CC";
public static final String DEFAULT_METHOD_PREFIX = "$default$";
public static final String PRIVATE_METHOD_PREFIX = "$private$";
@@ -208,7 +216,16 @@
return emulatedInterfaces.containsKey(itf);
}
- public boolean needsRewriting(DexMethod method) {
+ public boolean needsRewriting(DexMethod method, boolean isInvokeSuper, AppView<?> appView) {
+ if (isInvokeSuper) {
+ DexClass clazz = appView.appInfo().definitionFor(method.getHolderType());
+ if (clazz != null
+ && clazz.isLibraryClass()
+ && clazz.isInterface()
+ && appView.rewritePrefix.hasRewrittenType(clazz.type, appView)) {
+ return true;
+ }
+ }
return emulatedMethods.contains(method.getName());
}
@@ -216,18 +233,19 @@
return emulatedInterfaces.get(itf);
}
- private void leavingStaticInvokeToInterface(
- DexProgramClass holder, DexEncodedMethod encodedMethod) {
+ private void leavingStaticInvokeToInterface(ProgramMethod method) {
// When leaving static interface method invokes possibly upgrade the class file
// version, but don't go above the initial class file version. If the input was
// 1.7 or below, this will make a VerificationError on the input a VerificationError
// on the output. If the input was 1.8 or above the runtime behaviour (potential ICCE)
// will remain the same.
- if (holder.hasClassFileVersion()) {
- encodedMethod.upgradeClassFileVersion(
- Ordered.min(CfVersion.V1_8, holder.getInitialClassFileVersion()));
+ if (method.getHolder().hasClassFileVersion()) {
+ method
+ .getDefinition()
+ .upgradeClassFileVersion(
+ Ordered.min(CfVersion.V1_8, method.getHolder().getInitialClassFileVersion()));
} else {
- encodedMethod.upgradeClassFileVersion(CfVersion.V1_8);
+ method.getDefinition().upgradeClassFileVersion(CfVersion.V1_8);
}
}
@@ -235,279 +253,300 @@
// NOTE: can be called for different methods concurrently.
public void rewriteMethodReferences(IRCode code) {
ProgramMethod context = code.context();
- DexEncodedMethod encodedMethod = context.getDefinition();
- if (synthesizedMethods.contains(encodedMethod)) {
+ if (synthesizedMethods.contains(context)) {
return;
}
ListIterator<BasicBlock> blocks = code.listIterator();
- AppInfo appInfo = appView.appInfo();
while (blocks.hasNext()) {
BasicBlock block = blocks.next();
InstructionListIterator instructions = block.listIterator(code);
while (instructions.hasNext()) {
Instruction instruction = instructions.next();
-
- if (instruction.isInvokeCustom()) {
- // Check that static interface methods are not referenced
- // from invoke-custom instructions via method handles.
- DexCallSite callSite = instruction.asInvokeCustom().getCallSite();
- reportStaticInterfaceMethodHandle(encodedMethod.method, callSite.bootstrapMethod);
- for (DexValue arg : callSite.bootstrapArgs) {
- if (arg.isDexValueMethodHandle()) {
- reportStaticInterfaceMethodHandle(
- encodedMethod.method, arg.asDexValueMethodHandle().value);
- }
- }
- continue;
+ switch (instruction.opcode()) {
+ case INVOKE_CUSTOM:
+ rewriteInvokeCustom(instruction.asInvokeCustom(), context);
+ break;
+ case INVOKE_DIRECT:
+ rewriteInvokeDirect(instruction.asInvokeDirect(), instructions, context);
+ break;
+ case INVOKE_STATIC:
+ rewriteInvokeStatic(instruction.asInvokeStatic(), instructions, context);
+ break;
+ case INVOKE_SUPER:
+ rewriteInvokeSuper(instruction.asInvokeSuper(), instructions, context);
+ break;
+ case INVOKE_INTERFACE:
+ case INVOKE_VIRTUAL:
+ rewriteInvokeInterfaceOrInvokeVirtual(
+ instruction.asInvokeMethodWithReceiver(), instructions);
+ break;
+ default:
+ // Intentionally empty.
+ break;
}
+ }
+ }
+ }
- if (instruction.isInvokeStatic()) {
- InvokeStatic invokeStatic = instruction.asInvokeStatic();
- DexMethod method = invokeStatic.getInvokedMethod();
- if (appView.getSyntheticItems().isPendingSynthetic(method.holder)) {
- // We did not create this code yet, but it will not require rewriting.
- continue;
- }
- DexClass clazz = appInfo.definitionFor(method.holder);
- if (clazz == null) {
- // NOTE: leave unchanged those calls to undefined targets. This may lead to runtime
- // exception but we can not report it as error since it can also be the intended
- // behavior.
- if (invokeStatic.getInterfaceBit()) {
- leavingStaticInvokeToInterface(context.getHolder(), encodedMethod);
- }
- warnMissingType(encodedMethod.method, method.holder);
- } else if (clazz.isInterface()) {
- if (isNonDesugaredLibraryClass(clazz)) {
- // NOTE: we intentionally don't desugar static calls into static interface
- // methods coming from android.jar since it is only possible in case v24+
- // version of android.jar is provided.
- //
- // We assume such calls are properly guarded by if-checks like
- // 'if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.XYZ) { ... }'
- //
- // WARNING: This may result in incorrect code on older platforms!
- // Retarget call to an appropriate method of companion class.
+ private void rewriteInvokeCustom(InvokeCustom invoke, ProgramMethod context) {
+ // Check that static interface methods are not referenced from invoke-custom instructions via
+ // method handles.
+ DexCallSite callSite = invoke.getCallSite();
+ reportStaticInterfaceMethodHandle(context, callSite.bootstrapMethod);
+ for (DexValue arg : callSite.bootstrapArgs) {
+ if (arg.isDexValueMethodHandle()) {
+ reportStaticInterfaceMethodHandle(context, arg.asDexValueMethodHandle().value);
+ }
+ }
+ }
- if (!options.canLeaveStaticInterfaceMethodInvokes()) {
- // On pre-L devices static calls to interface methods result in verifier
- // rejecting the whole class. We have to create special dispatch classes,
- // so the user class is not rejected because it make this call directly.
- // TODO(b/166247515): If this an incorrect invoke-static without the interface bit
- // we end up "fixing" the code and remove and ICCE error.
- ProgramMethod newProgramMethod =
- appView
- .getSyntheticItems()
- .createMethod(
- context.getHolder(),
- factory,
- syntheticMethodBuilder -> {
- syntheticMethodBuilder
- .setProto(method.proto)
- .setAccessFlags(
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC
- | Constants.ACC_STATIC
- | Constants.ACC_SYNTHETIC,
- false))
- .setCode(
- m ->
- ForwardMethodBuilder.builder(factory)
- .setStaticTarget(method, true)
- .setStaticSource(m)
- .build());
- });
- instructions.replaceCurrentInstruction(
- new InvokeStatic(
- newProgramMethod.getReference(),
- invokeStatic.outValue(),
- invokeStatic.arguments()));
- synchronized (synthesizedMethods) {
- // The synthetic dispatch class has static interface method invokes, so set
- // the class file version accordingly.
- newProgramMethod.getDefinition().upgradeClassFileVersion(CfVersion.V1_8);
- synthesizedMethods.add(newProgramMethod);
- }
- } else {
- // When leaving static interface method invokes upgrade the class file version.
- encodedMethod.upgradeClassFileVersion(CfVersion.V1_8);
- }
- } else {
+ private void rewriteInvokeDirect(
+ InvokeDirect invoke, InstructionListIterator instructions, ProgramMethod context) {
+ DexMethod method = invoke.getInvokedMethod();
+ if (factory.isConstructor(method)) {
+ return;
+ }
+
+ DexClass clazz = appView.definitionForHolder(method, context);
+ if (clazz == null) {
+ // Report missing class since we don't know if it is an interface.
+ warnMissingType(context, method.holder);
+ return;
+ }
+
+ if (!clazz.isInterface()) {
+ return;
+ }
+
+ if (clazz.isLibraryClass()) {
+ throw new CompilationError(
+ "Unexpected call to a private method "
+ + "defined in library class "
+ + clazz.toSourceString(),
+ getMethodOrigin(context.getReference()));
+ }
+
+ DexEncodedMethod directTarget = clazz.lookupMethod(method);
+ if (directTarget != null) {
+ // This can be a private instance method call. Note that the referenced
+ // method is expected to be in the current class since it is private, but desugaring
+ // may move some methods or their code into other classes.
+ instructions.replaceCurrentInstruction(
+ new InvokeStatic(
+ directTarget.isPrivateMethod()
+ ? privateAsMethodOfCompanionClass(method)
+ : defaultAsMethodOfCompanionClass(method),
+ invoke.outValue(),
+ invoke.arguments()));
+ } else {
+ // The method can be a default method in the interface hierarchy.
+ DexClassAndMethod virtualTarget =
+ appView.appInfoForDesugaring().lookupMaximallySpecificMethod(clazz, method);
+ if (virtualTarget != null) {
+ // This is a invoke-direct call to a virtual method.
+ instructions.replaceCurrentInstruction(
+ new InvokeStatic(
+ defaultAsMethodOfCompanionClass(virtualTarget.getDefinition().method),
+ invoke.outValue(),
+ invoke.arguments()));
+ } else {
+ // The below assert is here because a well-type program should have a target, but we
+ // cannot throw a compilation error, since we have no knowledge about the input.
+ assert false;
+ }
+ }
+ }
+
+ private void rewriteInvokeStatic(
+ InvokeStatic invoke, InstructionListIterator instructions, ProgramMethod context) {
+ DexMethod invokedMethod = invoke.getInvokedMethod();
+ if (appView.getSyntheticItems().isPendingSynthetic(invokedMethod.holder)) {
+ // We did not create this code yet, but it will not require rewriting.
+ return;
+ }
+
+ DexClass clazz = appView.definitionFor(invokedMethod.holder, context);
+ if (clazz == null) {
+ // NOTE: leave unchanged those calls to undefined targets. This may lead to runtime
+ // exception but we can not report it as error since it can also be the intended
+ // behavior.
+ if (invoke.getInterfaceBit()) {
+ leavingStaticInvokeToInterface(context);
+ }
+ warnMissingType(context, invokedMethod.holder);
+ return;
+ }
+
+ if (!clazz.isInterface()) {
+ if (invoke.getInterfaceBit()) {
+ leavingStaticInvokeToInterface(context);
+ }
+ return;
+ }
+
+ if (isNonDesugaredLibraryClass(clazz)) {
+ // NOTE: we intentionally don't desugar static calls into static interface
+ // methods coming from android.jar since it is only possible in case v24+
+ // version of android.jar is provided.
+ //
+ // We assume such calls are properly guarded by if-checks like
+ // 'if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.XYZ) { ... }'
+ //
+ // WARNING: This may result in incorrect code on older platforms!
+ // Retarget call to an appropriate method of companion class.
+
+ if (!options.canLeaveStaticInterfaceMethodInvokes()) {
+ // On pre-L devices static calls to interface methods result in verifier
+ // rejecting the whole class. We have to create special dispatch classes,
+ // so the user class is not rejected because it make this call directly.
+ // TODO(b/166247515): If this an incorrect invoke-static without the interface bit
+ // we end up "fixing" the code and remove and ICCE error.
+ ProgramMethod newProgramMethod =
+ appView
+ .getSyntheticItems()
+ .createMethod(
+ context.getHolder(),
+ factory,
+ syntheticMethodBuilder ->
+ syntheticMethodBuilder
+ .setProto(invokedMethod.proto)
+ .setAccessFlags(
+ MethodAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_PUBLIC
+ | Constants.ACC_STATIC
+ | Constants.ACC_SYNTHETIC,
+ false))
+ .setCode(
+ m ->
+ ForwardMethodBuilder.builder(factory)
+ .setStaticTarget(invokedMethod, true)
+ .setStaticSource(m)
+ .build()));
+ instructions.replaceCurrentInstruction(
+ new InvokeStatic(
+ newProgramMethod.getReference(), invoke.outValue(), invoke.arguments()));
+ synchronized (synthesizedMethods) {
+ // The synthetic dispatch class has static interface method invokes, so set
+ // the class file version accordingly.
+ newProgramMethod.getDefinition().upgradeClassFileVersion(CfVersion.V1_8);
+ synthesizedMethods.add(newProgramMethod);
+ }
+ } else {
+ // When leaving static interface method invokes upgrade the class file version.
+ context.getDefinition().upgradeClassFileVersion(CfVersion.V1_8);
+ }
+ } else {
+ instructions.replaceCurrentInstruction(
+ new InvokeStatic(
+ staticAsMethodOfCompanionClass(invokedMethod),
+ invoke.outValue(),
+ invoke.arguments()));
+ }
+ }
+
+ private void rewriteInvokeSuper(
+ InvokeSuper invoke, InstructionListIterator instructions, ProgramMethod context) {
+ DexMethod invokedMethod = invoke.getInvokedMethod();
+ DexClass clazz = appView.definitionFor(invokedMethod.holder, context);
+ if (clazz == null) {
+ // NOTE: leave unchanged those calls to undefined targets. This may lead to runtime
+ // exception but we can not report it as error since it can also be the intended
+ // behavior.
+ warnMissingType(context, invokedMethod.holder);
+ return;
+ }
+
+ if (clazz.isInterface() && !clazz.isLibraryClass()) {
+ // NOTE: we intentionally don't desugar super calls into interface methods
+ // coming from android.jar since it is only possible in case v24+ version
+ // of android.jar is provided.
+ //
+ // We assume such calls are properly guarded by if-checks like
+ // 'if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.XYZ) { ... }'
+ //
+ // WARNING: This may result in incorrect code on older platforms!
+ // Retarget call to an appropriate method of companion class.
+ DexMethod amendedMethod = amendDefaultMethod(context.getHolder(), invokedMethod);
+ instructions.replaceCurrentInstruction(
+ new InvokeStatic(
+ defaultAsMethodOfCompanionClass(amendedMethod),
+ invoke.outValue(),
+ invoke.arguments()));
+ } else {
+ DexType emulatedItf = maximallySpecificEmulatedInterfaceOrNull(invokedMethod);
+ if (emulatedItf == null) {
+ if (clazz.isInterface() && appView.rewritePrefix.hasRewrittenType(clazz.type, appView)) {
+ DexClassAndMethod target =
+ appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context);
+ if (target != null && target.getDefinition().isDefaultMethod()) {
+ DexClass holder = target.getHolder();
+ if (holder.isLibraryClass() && holder.isInterface()) {
instructions.replaceCurrentInstruction(
- new InvokeStatic(staticAsMethodOfCompanionClass(method),
- invokeStatic.outValue(), invokeStatic.arguments()));
- }
- } else {
- assert !clazz.isInterface();
- if (invokeStatic.getInterfaceBit()) {
- leavingStaticInvokeToInterface(context.getHolder(), encodedMethod);
+ new InvokeStatic(
+ defaultAsMethodOfCompanionClass(target.getReference(), factory),
+ invoke.outValue(),
+ invoke.arguments()));
}
}
- continue;
}
-
- if (instruction.isInvokeSuper()) {
- InvokeSuper invokeSuper = instruction.asInvokeSuper();
- DexMethod invokedMethod = invokeSuper.getInvokedMethod();
- DexClass clazz = appInfo.definitionFor(invokedMethod.holder);
- if (clazz == null) {
- // NOTE: leave unchanged those calls to undefined targets. This may lead to runtime
- // exception but we can not report it as error since it can also be the intended
- // behavior.
- warnMissingType(encodedMethod.method, invokedMethod.holder);
- } else if (clazz.isInterface() && !clazz.isLibraryClass()) {
- // NOTE: we intentionally don't desugar super calls into interface methods
- // coming from android.jar since it is only possible in case v24+ version
- // of android.jar is provided.
- //
- // We assume such calls are properly guarded by if-checks like
- // 'if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.XYZ) { ... }'
- //
- // WARNING: This may result in incorrect code on older platforms!
- // Retarget call to an appropriate method of companion class.
- DexMethod amendedMethod =
- amendDefaultMethod(
- appInfo.definitionFor(encodedMethod.getHolderType()), invokedMethod);
+ } else {
+ // That invoke super may not resolve since the super method may not be present
+ // since it's in the emulated interface. We need to force resolution. If it resolves
+ // to a library method, then it needs to be rewritten.
+ // If it resolves to a program overrides, the invoke-super can remain.
+ DexClassAndMethod superTarget =
+ appView.appInfoForDesugaring().lookupSuperTarget(invoke.getInvokedMethod(), context);
+ if (superTarget != null && superTarget.isLibraryMethod()) {
+ // Rewriting is required because the super invoke resolves into a missing
+ // method (method is on desugared library). Find out if it needs to be
+ // retarget or if it just calls a companion class method and rewrite.
+ DexMethod retargetMethod =
+ options.desugaredLibraryConfiguration.retargetMethod(superTarget, appView);
+ if (retargetMethod == null) {
+ DexMethod originalCompanionMethod =
+ instanceAsMethodOfCompanionClass(
+ superTarget.getReference(), DEFAULT_METHOD_PREFIX, factory);
+ DexMethod companionMethod =
+ factory.createMethod(
+ getCompanionClassType(emulatedItf),
+ factory.protoWithDifferentFirstParameter(
+ originalCompanionMethod.proto, emulatedItf),
+ originalCompanionMethod.name);
instructions.replaceCurrentInstruction(
- new InvokeStatic(defaultAsMethodOfCompanionClass(amendedMethod),
- invokeSuper.outValue(), invokeSuper.arguments()));
+ new InvokeStatic(companionMethod, invoke.outValue(), invoke.arguments()));
} else {
- DexType dexType = maximallySpecificEmulatedInterfaceOrNull(invokedMethod);
- if (dexType == null) {
- if (clazz.isInterface()
- && appView.rewritePrefix.hasRewrittenType(clazz.type, appView)) {
- DexClassAndMethod target =
- appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, code.context());
- if (target != null && target.getDefinition().isDefaultMethod()) {
- DexClass holder = target.getHolder();
- if (holder.isLibraryClass() && holder.isInterface()) {
- instructions.replaceCurrentInstruction(
- new InvokeStatic(
- defaultAsMethodOfCompanionClass(target.getReference(), factory),
- invokeSuper.outValue(),
- invokeSuper.arguments()));
- }
- }
- }
- } else {
- // That invoke super may not resolve since the super method may not be present
- // since it's in the emulated interface. We need to force resolution. If it resolves
- // to a library method, then it needs to be rewritten.
- // If it resolves to a program overrides, the invoke-super can remain.
- DexClassAndMethod superTarget =
- appView
- .appInfoForDesugaring()
- .lookupSuperTarget(invokeSuper.getInvokedMethod(), code.context());
- if (superTarget != null && superTarget.isLibraryMethod()) {
- // Rewriting is required because the super invoke resolves into a missing
- // method (method is on desugared library). Find out if it needs to be
- // retarget or if it just calls a companion class method and rewrite.
- DexMethod retargetMethod =
- options.desugaredLibraryConfiguration.retargetMethod(superTarget, appView);
- if (retargetMethod == null) {
- DexMethod originalCompanionMethod =
- instanceAsMethodOfCompanionClass(
- superTarget.getReference(), DEFAULT_METHOD_PREFIX, factory);
- DexMethod companionMethod =
- factory.createMethod(
- getCompanionClassType(dexType),
- factory.protoWithDifferentFirstParameter(
- originalCompanionMethod.proto, dexType),
- originalCompanionMethod.name);
- instructions.replaceCurrentInstruction(
- new InvokeStatic(
- companionMethod, invokeSuper.outValue(), invokeSuper.arguments()));
- } else {
- instructions.replaceCurrentInstruction(
- new InvokeStatic(
- retargetMethod, invokeSuper.outValue(), invokeSuper.arguments()));
- }
- }
- }
- }
- continue;
- }
-
- if (instruction.isInvokeDirect()) {
- InvokeDirect invokeDirect = instruction.asInvokeDirect();
- DexMethod method = invokeDirect.getInvokedMethod();
- if (factory.isConstructor(method)) {
- continue;
- }
-
- DexClass clazz = appInfo.definitionForHolder(method);
- if (clazz == null) {
- // Report missing class since we don't know if it is an interface.
- warnMissingType(encodedMethod.method, method.holder);
- } else if (clazz.isInterface()) {
- if (clazz.isLibraryClass()) {
- throw new CompilationError("Unexpected call to a private method " +
- "defined in library class " + clazz.toSourceString(),
- getMethodOrigin(encodedMethod.method));
- }
- DexEncodedMethod directTarget = clazz.lookupMethod(method);
- if (directTarget != null) {
- // This can be a private instance method call. Note that the referenced
- // method is expected to be in the current class since it is private, but desugaring
- // may move some methods or their code into other classes.
- if (directTarget.isPrivateMethod()) {
- instructions.replaceCurrentInstruction(
- new InvokeStatic(
- privateAsMethodOfCompanionClass(method),
- invokeDirect.outValue(),
- invokeDirect.arguments()));
- } else {
- instructions.replaceCurrentInstruction(
- new InvokeStatic(
- defaultAsMethodOfCompanionClass(method),
- invokeDirect.outValue(),
- invokeDirect.arguments()));
- }
- } else {
- // The method can be a default method in the interface hierarchy.
- DexClassAndMethod virtualTarget =
- appView.appInfoForDesugaring().lookupMaximallySpecificMethod(clazz, method);
- if (virtualTarget != null) {
- // This is a invoke-direct call to a virtual method.
- instructions.replaceCurrentInstruction(
- new InvokeStatic(
- defaultAsMethodOfCompanionClass(virtualTarget.getDefinition().method),
- invokeDirect.outValue(),
- invokeDirect.arguments()));
- } else {
- // The below assert is here because a well-type program should have a target, but we
- // cannot throw a compilation error, since we have no knowledge about the input.
- assert false;
- }
- }
- }
- }
-
- if (instruction.isInvokeVirtual() || instruction.isInvokeInterface()) {
- InvokeMethod invokeMethod = instruction.asInvokeMethod();
- DexMethod invokedMethod = invokeMethod.getInvokedMethod();
- DexType dexType = maximallySpecificEmulatedInterfaceOrNull(invokedMethod);
- if (dexType != null) {
- // The call potentially ends up in a library class, in which case we need to rewrite,
- // since the code may be in the desugared library.
- SingleResolutionResult resolution =
- appView
- .appInfoForDesugaring()
- .resolveMethod(invokedMethod, invokeMethod.getInterfaceBit())
- .asSingleResolution();
- if (resolution != null
- && (resolution.getResolvedHolder().isLibraryClass()
- || appView.options().isDesugaredLibraryCompilation())) {
- rewriteCurrentInstructionToEmulatedInterfaceCall(
- dexType, invokedMethod, invokeMethod, instructions);
- }
+ instructions.replaceCurrentInstruction(
+ new InvokeStatic(retargetMethod, invoke.outValue(), invoke.arguments()));
}
}
}
}
}
+ private void rewriteInvokeInterfaceOrInvokeVirtual(
+ InvokeMethodWithReceiver invoke, InstructionListIterator instructions) {
+ DexMethod invokedMethod = invoke.getInvokedMethod();
+ DexType emulatedItf = maximallySpecificEmulatedInterfaceOrNull(invokedMethod);
+ if (emulatedItf == null) {
+ return;
+ }
+
+ // The call potentially ends up in a library class, in which case we need to rewrite, since the
+ // code may be in the desugared library.
+ SingleResolutionResult resolution =
+ appView
+ .appInfoForDesugaring()
+ .resolveMethod(invokedMethod, invoke.getInterfaceBit())
+ .asSingleResolution();
+ if (resolution != null
+ && (resolution.getResolvedHolder().isLibraryClass()
+ || appView.options().isDesugaredLibraryCompilation())) {
+ rewriteCurrentInstructionToEmulatedInterfaceCall(
+ emulatedItf, invokedMethod, invoke, instructions);
+ }
+ }
+
private DexType maximallySpecificEmulatedInterfaceOrNull(DexMethod invokedMethod) {
// Here we try to avoid doing the expensive look-up on all invokes.
if (!emulatedMethods.contains(invokedMethod.name)) {
@@ -547,12 +586,13 @@
DexMethod invokedMethod,
InvokeMethod invokeMethod,
InstructionListIterator instructions) {
- DexEncodedMethod defaultMethod = appView.definitionFor(emulatedItf).lookupMethod(invokedMethod);
- if (defaultMethod != null && !dontRewrite(defaultMethod.method)) {
- assert !defaultMethod.isAbstract();
+ DexClassAndMethod defaultMethod =
+ appView.definitionFor(emulatedItf).lookupClassMethod(invokedMethod);
+ if (defaultMethod != null && !dontRewrite(defaultMethod)) {
+ assert !defaultMethod.getAccessFlags().isAbstract();
instructions.replaceCurrentInstruction(
new InvokeStatic(
- emulateInterfaceLibraryMethod(invokedMethod, emulatedItf, factory),
+ emulateInterfaceLibraryMethod(defaultMethod),
invokeMethod.outValue(),
invokeMethod.arguments()));
}
@@ -570,10 +610,11 @@
return appView.rewritePrefix.hasRewrittenType(clazz.type, appView);
}
- boolean dontRewrite(DexMethod method) {
+ boolean dontRewrite(DexClassAndMethod method) {
for (Pair<DexType, DexString> dontRewrite :
options.desugaredLibraryConfiguration.getDontRewriteInvocation()) {
- if (method.holder == dontRewrite.getFirst() && method.name == dontRewrite.getSecond()) {
+ if (method.getHolderType() == dontRewrite.getFirst()
+ && method.getName() == dontRewrite.getSecond()) {
return true;
}
}
@@ -666,87 +707,82 @@
return false;
}
- private static DexMethod emulateInterfaceLibraryMethod(
- DexMethod method, DexType holder, DexItemFactory factory) {
+ private DexMethod emulateInterfaceLibraryMethod(DexClassAndMethod method) {
return factory.createMethod(
- getEmulateLibraryInterfaceClassType(holder, factory),
- factory.prependTypeToProto(holder, method.proto),
- factory.createString(method.name.toString()));
+ getEmulateLibraryInterfaceClassType(method.getHolderType(), factory),
+ factory.prependTypeToProto(method.getHolderType(), method.getProto()),
+ method.getName());
}
private DexProgramClass synthesizeEmulateInterfaceLibraryClass(
DexProgramClass theInterface, Map<DexType, List<DexType>> emulatedInterfacesHierarchy) {
List<DexEncodedMethod> emulationMethods = new ArrayList<>();
- for (DexEncodedMethod method : theInterface.methods()) {
- if (method.isDefaultMethod()) {
- DexMethod libraryMethod =
- factory.createMethod(
- emulatedInterfaces.get(theInterface.type), method.method.proto, method.method.name);
- DexMethod originalCompanionMethod =
- method.isStatic()
- ? staticAsMethodOfCompanionClass(method.method)
- : instanceAsMethodOfCompanionClass(method.method, DEFAULT_METHOD_PREFIX, factory);
- DexMethod companionMethod =
- factory.createMethod(
- getCompanionClassType(theInterface.type),
- originalCompanionMethod.proto,
- originalCompanionMethod.name);
+ theInterface.forEachProgramMethodMatching(
+ DexEncodedMethod::isDefaultMethod,
+ method -> {
+ DexMethod libraryMethod =
+ method.getReference().withHolder(emulatedInterfaces.get(theInterface.type), factory);
+ DexMethod companionMethod =
+ method.getAccessFlags().isStatic()
+ ? staticAsMethodOfCompanionClass(method)
+ : defaultAsMethodOfCompanionClass(method);
- // To properly emulate the library interface call, we need to compute the interfaces
- // inheriting from the interface and manually implement the dispatch with instance of.
- // The list guarantees that an interface is always after interfaces it extends,
- // hence reverse iteration.
- List<DexType> subInterfaces = emulatedInterfacesHierarchy.get(theInterface.type);
- List<Pair<DexType, DexMethod>> extraDispatchCases = new ArrayList<>();
- // In practice, there is usually a single case (except for tests),
- // so we do not bother to make the following loop more clever.
- Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
- options.desugaredLibraryConfiguration.getRetargetCoreLibMember();
- for (DexString methodName : retargetCoreLibMember.keySet()) {
- if (method.method.name == methodName) {
- for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
- DexClass inClass = appView.definitionFor(inType);
- if (inClass != null && implementsInterface(inClass, theInterface.type)) {
- extraDispatchCases.add(
- new Pair<>(
- inType,
- factory.createMethod(
- retargetCoreLibMember.get(methodName).get(inType),
- factory.protoWithDifferentFirstParameter(
- originalCompanionMethod.proto, inType),
- method.method.name)));
+ // To properly emulate the library interface call, we need to compute the interfaces
+ // inheriting from the interface and manually implement the dispatch with instance of.
+ // The list guarantees that an interface is always after interfaces it extends,
+ // hence reverse iteration.
+ List<DexType> subInterfaces = emulatedInterfacesHierarchy.get(theInterface.type);
+ List<Pair<DexType, DexMethod>> extraDispatchCases = new ArrayList<>();
+ // In practice, there is usually a single case (except for tests),
+ // so we do not bother to make the following loop more clever.
+ Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
+ options.desugaredLibraryConfiguration.getRetargetCoreLibMember();
+ for (DexString methodName : retargetCoreLibMember.keySet()) {
+ if (method.getName() == methodName) {
+ for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
+ DexClass inClass = appView.definitionFor(inType);
+ if (inClass != null && implementsInterface(inClass, theInterface.type)) {
+ extraDispatchCases.add(
+ new Pair<>(
+ inType,
+ factory.createMethod(
+ retargetCoreLibMember.get(methodName).get(inType),
+ factory.protoWithDifferentFirstParameter(
+ companionMethod.proto, inType),
+ method.getName())));
+ }
}
}
}
- }
- if (subInterfaces != null) {
- for (int i = subInterfaces.size() - 1; i >= 0; i--) {
- DexClass subInterfaceClass = appView.definitionFor(subInterfaces.get(i));
- assert subInterfaceClass != null; // Else computation of subInterface would have failed.
- // if the method is implemented, extra dispatch is required.
- DexEncodedMethod result = subInterfaceClass.lookupVirtualMethod(method.method);
- if (result != null && !result.isAbstract()) {
- extraDispatchCases.add(
- new Pair<>(
- subInterfaceClass.type,
- factory.createMethod(
- getCompanionClassType(subInterfaceClass.type),
- factory.protoWithDifferentFirstParameter(
- originalCompanionMethod.proto, subInterfaceClass.type),
- originalCompanionMethod.name)));
+ if (subInterfaces != null) {
+ for (int i = subInterfaces.size() - 1; i >= 0; i--) {
+ DexClass subInterfaceClass = appView.definitionFor(subInterfaces.get(i));
+ assert subInterfaceClass
+ != null; // Else computation of subInterface would have failed.
+ // if the method is implemented, extra dispatch is required.
+ DexEncodedMethod result =
+ subInterfaceClass.lookupVirtualMethod(method.getReference());
+ if (result != null && !result.isAbstract()) {
+ extraDispatchCases.add(
+ new Pair<>(
+ subInterfaceClass.type,
+ factory.createMethod(
+ getCompanionClassType(subInterfaceClass.type),
+ factory.protoWithDifferentFirstParameter(
+ companionMethod.proto, subInterfaceClass.type),
+ companionMethod.name)));
+ }
}
}
- }
- emulationMethods.add(
- DexEncodedMethod.toEmulateDispatchLibraryMethod(
- method.getHolderType(),
- emulateInterfaceLibraryMethod(method.method, method.getHolderType(), factory),
- companionMethod,
- libraryMethod,
- extraDispatchCases,
- appView));
- }
- }
+ emulationMethods.add(
+ DexEncodedMethod.toEmulateDispatchLibraryMethod(
+ method.getHolderType(),
+ emulateInterfaceLibraryMethod(method),
+ companionMethod,
+ libraryMethod,
+ extraDispatchCases,
+ appView));
+ });
if (emulationMethods.isEmpty()) {
return null;
}
@@ -799,17 +835,17 @@
return factory.createSynthesizedType(elTypeDescriptor);
}
- private void reportStaticInterfaceMethodHandle(DexMethod referencedFrom, DexMethodHandle handle) {
+ private void reportStaticInterfaceMethodHandle(ProgramMethod context, DexMethodHandle handle) {
if (handle.type.isInvokeStatic()) {
DexClass holderClass = appView.definitionFor(handle.asMethod().holder);
// NOTE: If the class definition is missing we can't check. Let it be handled as any other
// missing call target.
if (holderClass == null) {
- warnMissingType(referencedFrom, handle.asMethod().holder);
+ warnMissingType(context, handle.asMethod().holder);
} else if (holderClass.isInterface()) {
throw new Unimplemented(
"Desugaring of static interface method handle in `"
- + referencedFrom.toSourceString()
+ + context.toSourceString()
+ "` is not yet supported.");
}
}
@@ -831,15 +867,6 @@
return getCompanionClassType(type, factory);
}
- // Gets the forwarding class for the interface `type`.
- final DexType getDispatchClassType(DexType type) {
- assert type.isClassType();
- String descriptor = type.descriptor.toString();
- String dcTypeDescriptor = descriptor.substring(0, descriptor.length() - 1)
- + DISPATCH_CLASS_NAME_SUFFIX + ";";
- return factory.createType(dcTypeDescriptor);
- }
-
// Checks if `type` is a companion class.
public static boolean isCompanionClassType(DexType type) {
return type.descriptor.toString().endsWith(COMPANION_CLASS_NAME_SUFFIX + ";");
@@ -864,21 +891,15 @@
}
// Represent a static interface method as a method of companion class.
+ final DexMethod staticAsMethodOfCompanionClass(DexClassAndMethod method) {
+ return staticAsMethodOfCompanionClass(method.getReference());
+ }
+
final DexMethod staticAsMethodOfCompanionClass(DexMethod method) {
// No changes for static methods.
return factory.createMethod(getCompanionClassType(method.holder), method.proto, method.name);
}
- // Represent a static interface method as a method of dispatch class.
- final DexMethod staticAsMethodOfDispatchClass(DexMethod method) {
- return factory.createMethod(getDispatchClassType(method.holder), method.proto, method.name);
- }
-
- // Checks if the type ends with dispatch class suffix.
- public static boolean hasDispatchClassSuffix(DexType clazz) {
- return clazz.getName().endsWith(DISPATCH_CLASS_NAME_SUFFIX);
- }
-
private static DexMethod instanceAsMethodOfCompanionClass(
DexMethod method, String prefix, DexItemFactory factory) {
// Add an implicit argument to represent the receiver.
@@ -913,6 +934,10 @@
return defaultAsMethodOfCompanionClass(method, factory);
}
+ DexMethod defaultAsMethodOfCompanionClass(DexClassAndMethod method) {
+ return defaultAsMethodOfCompanionClass(method.getReference(), factory);
+ }
+
// Represent a private instance interface method as a method of companion class.
static DexMethod privateAsMethodOfCompanionClass(DexMethod method, DexItemFactory factory) {
// Add an implicit argument to represent the receiver.
@@ -1135,37 +1160,6 @@
return true;
}
- public static boolean isEmulatedInterfaceDispatch(AppView<?> appView, DexEncodedMethod method) {
- // Answers true if this method is already managed through emulated interface dispatch.
- Map<DexType, DexType> emulateLibraryInterface =
- appView.options().desugaredLibraryConfiguration.getEmulateLibraryInterface();
- if (emulateLibraryInterface.isEmpty()) {
- return false;
- }
- DexMethod methodToFind = method.method;
-
- // Look-up all superclass and interfaces, if an emulated interface is found, and it implements
- // the method, answers true.
- LinkedList<DexType> workList = new LinkedList<>();
- workList.add(methodToFind.holder);
- while (!workList.isEmpty()) {
- DexType dexType = workList.removeFirst();
- DexClass dexClass = appView.definitionFor(dexType);
- assert dexClass != null; // It is a library class, or we are doing L8 compilation.
- if (dexClass.isInterface() && emulateLibraryInterface.containsKey(dexType)) {
- DexEncodedMethod dexEncodedMethod = dexClass.lookupMethod(methodToFind);
- if (dexEncodedMethod != null) {
- return true;
- }
- }
- Collections.addAll(workList, dexClass.interfaces.values);
- if (dexClass.superType != appView.dexItemFactory().objectType) {
- workList.add(dexClass.superType);
- }
- }
- return false;
- }
-
private boolean shouldIgnoreFromReports(DexType missing) {
return appView.rewritePrefix.hasRewrittenType(missing, appView)
|| missing.isD8R8SynthesizedClassType()
@@ -1187,13 +1181,13 @@
options.warningMissingInterfaceForDesugar(classToDesugar, implementing, missing);
}
- private void warnMissingType(DexMethod referencedFrom, DexType missing) {
+ private void warnMissingType(ProgramMethod context, DexType missing) {
// Companion/Emulated interface/Conversion classes for desugared library won't be missing,
// they are in the desugared library.
if (shouldIgnoreFromReports(missing)) {
return;
}
- DexMethod method = appView.graphLens().getOriginalMethodSignature(referencedFrom);
+ DexMethod method = appView.graphLens().getOriginalMethodSignature(context.getReference());
Origin origin = getMethodOrigin(method);
MethodPosition position = new MethodPosition(method.asMethodReference());
options.warningMissingTypeForDesugar(origin, position, missing, method);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index 42787b7..c2db31a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -14,7 +14,6 @@
import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
import com.android.tools.r8.code.Instruction;
import com.android.tools.r8.code.InvokeSuper;
-import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.graph.AppView;
@@ -27,7 +26,6 @@
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.DexLibraryClass;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
@@ -45,7 +43,6 @@
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.collections.BidirectionalManyToManyRepresentativeMap;
@@ -362,85 +359,6 @@
return c -> 7 * checksum;
}
- DexProgramClass process(DexLibraryClass iface, Set<DexProgramClass> callers) {
- assert iface.isInterface();
-
- // The list of methods to be created in dispatch class.
- // NOTE: we do NOT check static methods being actually called on the interface against
- // static methods actually existing in that interface. It is essential for supporting
- // D8 desugaring when each class may be dexed separately since it allows us to assume
- // that all synthesized dispatch classes have the same set of methods so we don't
- // need to merge them.
- List<DexEncodedMethod> dispatchMethods = new ArrayList<>();
-
- // Process public static methods, for each of them create a method dispatching the call to it.
- for (DexEncodedMethod direct : iface.directMethods()) {
- MethodAccessFlags originalAccessFlags = direct.accessFlags;
- if (!originalAccessFlags.isStatic() || !originalAccessFlags.isPublic()) {
- // We assume only public static methods of library interfaces can be called
- // from program classes, since there should not be protected or package private
- // static methods on interfaces.
- assert !originalAccessFlags.isStatic() || originalAccessFlags.isPrivate();
- continue;
- }
-
- assert !rewriter.factory.isClassConstructor(direct.method);
-
- DexMethod origMethod = direct.method;
- DexMethod newMethod = rewriter.staticAsMethodOfDispatchClass(origMethod);
- // Create a forwarding method to the library static interface method. The method is added
- // to the dispatch class, however, the targeted method is still on the interface, so the
- // interface bit should be set to true.
- ForwardMethodBuilder forwardMethodBuilder =
- ForwardMethodBuilder.builder(appView.dexItemFactory())
- .setStaticSource(newMethod)
- .setStaticTarget(origMethod, true);
- DexEncodedMethod newEncodedMethod =
- new DexEncodedMethod(
- newMethod,
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC, false),
- MethodTypeSignature.noSignature(),
- DexAnnotationSet.empty(),
- ParameterAnnotationsList.empty(),
- forwardMethodBuilder.build(),
- true);
- newEncodedMethod.getMutableOptimizationInfo().markNeverInline();
- dispatchMethods.add(newEncodedMethod);
- }
-
- ClassAccessFlags dispatchClassFlags =
- ClassAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC | Constants.ACC_FINAL | Constants.ACC_SYNTHETIC);
-
- // Create dispatch class.
- DexType dispatchClassType = rewriter.getDispatchClassType(iface.type);
- DexProgramClass dispatchClass =
- new DexProgramClass(
- dispatchClassType,
- null,
- new SynthesizedOrigin("interface dispatch", getClass()),
- dispatchClassFlags,
- rewriter.factory.objectType,
- DexTypeList.empty(),
- iface.sourceFile,
- null,
- Collections.emptyList(),
- null,
- Collections.emptyList(),
- ClassSignature.noSignature(),
- DexAnnotationSet.empty(),
- DexEncodedField.EMPTY_ARRAY,
- DexEncodedField.EMPTY_ARRAY,
- dispatchMethods.toArray(DexEncodedMethod.EMPTY_ARRAY),
- DexEncodedMethod.EMPTY_ARRAY,
- rewriter.factory.getSkipNameValidationForTesting(),
- DexProgramClass::checksumFromType,
- callers);
- syntheticClasses.put(iface, dispatchClass);
- return dispatchClass;
- }
-
private boolean canMoveToCompanionClass(DexEncodedMethod method) {
Code code = method.getCode();
assert code != null;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
index d41fd32..f7be74d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
@@ -132,10 +132,10 @@
// The only encoded method.
CfCode code =
- BackportedMethods.CloseResourceMethod_closeResourceImpl(
- options, twrCloseResourceMethod);
- MethodAccessFlags flags = MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC, false);
+ BackportedMethods.CloseResourceMethod_closeResourceImpl(options, twrCloseResourceMethod);
+ MethodAccessFlags flags =
+ MethodAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC, false);
DexEncodedMethod method =
new DexEncodedMethod(
twrCloseResourceMethod,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 8f510fb..1cce76e 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
@@ -985,6 +985,16 @@
continue;
}
+ if (invoke.isInvokeMethodWithReceiver()) {
+ if (iterator.replaceCurrentInstructionByNullCheckIfPossible(appView, context)) {
+ continue;
+ }
+ } else if (invoke.isInvokeStatic()
+ && iterator.replaceCurrentInstructionByInitClassIfPossible(
+ appView, code, resolutionResult.getResolvedHolder().getType())) {
+ continue;
+ }
+
// TODO(b/156853206): Should not duplicate resolution.
ProgramMethod singleTarget = oracle.lookupSingleTarget(invoke, context);
if (singleTarget == null) {
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 39ce729..2950d4c 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,7 +3,6 @@
// 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 static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
import static com.google.common.base.Predicates.alwaysTrue;
@@ -14,7 +13,6 @@
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.graph.ProgramMethod;
@@ -26,13 +24,10 @@
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.InitClass;
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.StaticGet;
import com.android.tools.r8.ir.code.StaticPut;
@@ -44,7 +39,6 @@
import com.android.tools.r8.shaking.ProguardMemberRuleReturnValue;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import java.util.ListIterator;
import java.util.Set;
@@ -216,8 +210,8 @@
}
if (current.isStaticGet()) {
StaticGet staticGet = current.asStaticGet();
- replaceInstructionByInitClassIfPossible(
- staticGet, staticGet.getField().holder, code, iterator, code.context());
+ iterator.replaceCurrentInstructionByInitClassIfPossible(
+ appView, code, staticGet.getField().holder);
}
replacement.setPosition(position);
if (block.hasCatchHandlers()) {
@@ -236,16 +230,12 @@
ListIterator<BasicBlock> blocks,
InstructionListIterator iterator,
InvokeMethod invoke) {
+ if (invoke.hasUnusedOutValue()) {
+ return;
+ }
+
DexMethod invokedMethod = invoke.getInvokedMethod();
- if (invokedMethod.proto.returnType.isVoidType()) {
- return;
- }
-
- if (!invoke.hasOutValue() || !invoke.outValue().hasNonDebugUsers()) {
- return;
- }
-
- DexType invokedHolder = invokedMethod.holder;
+ DexType invokedHolder = invokedMethod.getHolderType();
if (!invokedHolder.isClassType()) {
return;
}
@@ -302,10 +292,10 @@
invoke.setOutValue(null);
if (invoke.isInvokeMethodWithReceiver()) {
- replaceInstructionByNullCheckIfPossible(invoke, iterator, context);
+ iterator.replaceCurrentInstructionByNullCheckIfPossible(appView, context);
} else if (invoke.isInvokeStatic()) {
- replaceInstructionByInitClassIfPossible(
- invoke, singleTarget.getHolderType(), code, iterator, context);
+ iterator.replaceCurrentInstructionByInitClassIfPossible(
+ appView, code, singleTarget.getHolderType());
}
// Insert the definition of the replacement.
@@ -404,11 +394,11 @@
// 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.
if (current.isInstanceGet()) {
- replaceInstructionByNullCheckIfPossible(current, iterator, context);
+ iterator.replaceCurrentInstructionByNullCheckIfPossible(appView, context);
} else {
assert current.isStaticGet();
- replaceInstructionByInitClassIfPossible(
- current, target.getHolderType(), code, iterator, context);
+ iterator.replaceCurrentInstructionByInitClassIfPossible(
+ appView, code, target.getHolderType());
}
// Insert the definition of the replacement.
@@ -426,58 +416,6 @@
}
}
- private void replaceInstructionByNullCheckIfPossible(
- Instruction instruction, InstructionListIterator iterator, ProgramMethod context) {
- assert instruction.isInstanceFieldInstruction() || instruction.isInvokeMethodWithReceiver();
- assert !instruction.hasOutValue() || !instruction.outValue().hasAnyUsers();
- if (instruction.instructionMayHaveSideEffects(
- appView, context, Instruction.SideEffectAssumption.RECEIVER_NOT_NULL)) {
- return;
- }
- Value receiver =
- instruction.isInstanceFieldInstruction()
- ? instruction.asInstanceFieldInstruction().object()
- : instruction.asInvokeMethodWithReceiver().getReceiver();
- 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().objectMembers.getClass;
- replacement = new InvokeVirtual(getClassMethod, null, ImmutableList.of(receiver));
- }
- iterator.replaceCurrentInstruction(replacement);
- }
-
- private void replaceInstructionByInitClassIfPossible(
- Instruction instruction,
- DexType holder,
- IRCode code,
- InstructionListIterator iterator,
- ProgramMethod context) {
- assert instruction.isStaticFieldInstruction() || instruction.isInvokeStatic();
- if (instruction.instructionMayHaveSideEffects(
- appView, context, Instruction.SideEffectAssumption.CLASS_ALREADY_INITIALIZED)) {
- return;
- }
- if (!holder.classInitializationMayHaveSideEffectsInContext(appView, context)) {
- iterator.removeOrReplaceByDebugLocalRead();
- return;
- }
- if (!appView.canUseInitClass()) {
- return;
- }
- DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(holder));
- if (clazz != null) {
- Value dest = code.createValue(TypeElement.getInt());
- iterator.replaceCurrentInstruction(new InitClass(dest, clazz.type));
- }
- }
-
private void replaceInstancePutByNullCheckIfNeverRead(
IRCode code, InstructionListIterator iterator, InstancePut current) {
DexEncodedField field = appView.appInfo().resolveField(current.getField()).getResolvedField();
@@ -492,7 +430,7 @@
return;
}
- replaceInstructionByNullCheckIfPossible(current, iterator, code.context());
+ iterator.replaceCurrentInstructionByNullCheckIfPossible(appView, code.context());
}
private void replaceStaticPutByInitClassIfNeverRead(
@@ -509,8 +447,7 @@
return;
}
- replaceInstructionByInitClassIfPossible(
- current, field.getHolderType(), code, iterator, code.context());
+ iterator.replaceCurrentInstructionByInitClassIfPossible(appView, code, field.getHolderType());
}
/**
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 508e0f4..f673395 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -37,6 +37,7 @@
import com.android.tools.r8.ir.analysis.type.PrimitiveTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.Add;
+import com.android.tools.r8.ir.code.Assume;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
import com.android.tools.r8.ir.code.Binop;
@@ -782,7 +783,7 @@
// Process instruction. Returns true if an outline candidate was found.
private void processInstruction(Instruction instruction) {
// Figure out whether to include the instruction.
- boolean include = false;
+ boolean include;
int instructionIncrement = 1;
if (instruction.isConstInstruction()) {
if (index == start) {
@@ -811,13 +812,13 @@
includeInstruction(instruction);
// Check if this instruction ends the outline.
if (actualInstructions >= appView.options().outline.maxSize) {
- candidate(start, index + 1, actualInstructions);
+ candidate(start, index + 1);
} else {
index++;
}
} else if (index > start) {
// Do not add this instruction, candidate ends with previous instruction.
- candidate(start, index, actualInstructions);
+ candidate(start, index);
} else {
// Restart search from next instruction.
reset(index + 1);
@@ -830,7 +831,7 @@
int returnValueUsersLeftIfIncluded = returnValueUsersLeft;
if (returnValue != null) {
for (Value value : instruction.inValues()) {
- if (value == returnValue) {
+ if (value.getAliasedValue() == returnValue) {
returnValueUsersLeftIfIncluded--;
}
}
@@ -870,10 +871,9 @@
}
// Find the number of in-going arguments, if adding this instruction.
int newArgumentRegisters = argumentRegisters;
- if (instruction.inValues().size() > 0) {
- List<Value> inValues = orderedInValues(instruction, returnValue);
- for (int i = 0; i < inValues.size(); i++) {
- Value value = inValues.get(i);
+ if (invoke.hasArguments()) {
+ for (int i = 0; i < invoke.arguments().size(); i++) {
+ Value value = invoke.getArgument(i).getAliasedValue();
if (value == returnValue) {
continue;
}
@@ -884,7 +884,8 @@
newArgumentRegisters += value.requiredRegisters();
} else {
// For virtual calls only re-use the receiver argument.
- if (i > 0 || !arguments.contains(value)) {
+ Value receiver = invoke.asInvokeMethodWithReceiver().getReceiver().getAliasedValue();
+ if (value != receiver || !arguments.contains(value)) {
newArgumentRegisters += value.requiredRegisters();
}
}
@@ -907,7 +908,11 @@
offset++;
previous = instructions.get(index - offset);
} while (previous.isConstInstruction());
- if (!previous.isNewInstance() || previous.outValue() != returnValue) {
+ if (!previous.isNewInstance()) {
+ return false;
+ }
+ if (returnValue == null || returnValue != previous.outValue()) {
+ assert false;
return false;
}
// Clear pending new instance flag as the last thing, now the matching constructor is known
@@ -996,6 +1001,10 @@
// Add the current instruction to the outline.
private void includeInstruction(Instruction instruction) {
if (instruction.isAssume()) {
+ Assume assume = instruction.asAssume();
+ if (returnValue != null && assume.src().getAliasedValue() == returnValue) {
+ adjustReturnValueUsersLeft(assume.outValue().numberOfAllUsers() - 1);
+ }
return;
}
@@ -1005,12 +1014,7 @@
if (returnValue != null) {
for (Value value : inValues) {
if (value.getAliasedValue() == returnValue) {
- assert returnValueUsersLeft > 0;
- returnValueUsersLeft--;
- }
- if (returnValueUsersLeft == 0) {
- returnValue = null;
- returnType = appView.dexItemFactory().voidType;
+ adjustReturnValueUsersLeft(-1);
}
}
}
@@ -1080,9 +1084,18 @@
}
}
+ private void adjustReturnValueUsersLeft(int change) {
+ returnValueUsersLeft += change;
+ assert returnValueUsersLeft >= 0;
+ if (returnValueUsersLeft == 0) {
+ returnValue = null;
+ returnType = appView.dexItemFactory().voidType;
+ }
+ }
+
protected abstract void handle(int start, int end, Outline outline);
- private void candidate(int start, int index, int actualInstructions) {
+ private void candidate(int start, int index) {
List<Instruction> instructions = getInstructionArray();
assert !instructions.get(start).isConstInstruction();
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 143d776..0ce08a5 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
@@ -584,10 +584,13 @@
currentUsers = indirectOutValueUsers;
}
- assert !methodCallsOnInstance.isEmpty();
-
- inliner.performForcedInlining(
- method, code, methodCallsOnInstance, inliningIRProvider, Timing.empty());
+ if (!methodCallsOnInstance.isEmpty()) {
+ inliner.performForcedInlining(
+ method, code, methodCallsOnInstance, inliningIRProvider, Timing.empty());
+ } else {
+ assert indirectMethodCallsOnInstance.stream()
+ .noneMatch(method -> method.getDefinition().getOptimizationInfo().mayHaveSideEffects());
+ }
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodSideEffectModelCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodSideEffectModelCollection.java
index b1890af..ae91ac8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodSideEffectModelCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodSideEffectModelCollection.java
@@ -38,7 +38,10 @@
ImmutableMap.<DexMethod, BiPredicate<DexMethod, List<Value>>>builder()
.put(
dexItemFactory.stringMembers.constructor,
- (method, arguments) -> arguments.get(1).isNeverNull());
+ (method, arguments) -> arguments.get(1).isNeverNull())
+ .put(
+ dexItemFactory.stringMembers.valueOf,
+ (method, arguments) -> arguments.get(0).isNeverNull());
putAll(
builder,
dexItemFactory.stringBufferMethods.constructorMethods,
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 25688ce..ef724ec 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.errors.CodeSizeOverflowDiagnostic;
import com.android.tools.r8.errors.ConstantPoolOverflowDiagnostic;
-import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
@@ -126,35 +125,29 @@
LensCodeRewriterUtils rewriter = new LensCodeRewriterUtils(appView);
for (DexProgramClass clazz : application.classes()) {
assert SyntheticItems.verifyNotInternalSynthetic(clazz.getType());
- if (clazz.getSynthesizedFrom().isEmpty()
- || options.isDesugaredLibraryCompilation()
- || options.cfToCfDesugar) {
- try {
- writeClass(clazz, consumer, rewriter, markerString);
- } catch (ClassTooLargeException e) {
- throw appView
- .options()
- .reporter
- .fatalError(
- new ConstantPoolOverflowDiagnostic(
- clazz.getOrigin(),
- Reference.classFromBinaryName(e.getClassName()),
- e.getConstantPoolCount()));
- } catch (MethodTooLargeException e) {
- throw appView
- .options()
- .reporter
- .fatalError(
- new CodeSizeOverflowDiagnostic(
- clazz.getOrigin(),
- Reference.methodFromDescriptor(
- Reference.classFromBinaryName(e.getClassName()).getDescriptor(),
- e.getMethodName(),
- e.getDescriptor()),
- e.getCodeSize()));
- }
- } else {
- throw new Unimplemented("No support for synthetics in the Java bytecode backend.");
+ try {
+ writeClass(clazz, consumer, rewriter, markerString);
+ } catch (ClassTooLargeException e) {
+ throw appView
+ .options()
+ .reporter
+ .fatalError(
+ new ConstantPoolOverflowDiagnostic(
+ clazz.getOrigin(),
+ Reference.classFromBinaryName(e.getClassName()),
+ e.getConstantPoolCount()));
+ } catch (MethodTooLargeException e) {
+ throw appView
+ .options()
+ .reporter
+ .fatalError(
+ new CodeSizeOverflowDiagnostic(
+ clazz.getOrigin(),
+ Reference.methodFromDescriptor(
+ Reference.classFromBinaryName(e.getClassName()).getDescriptor(),
+ e.getMethodName(),
+ e.getDescriptor()),
+ e.getCodeSize()));
}
}
ApplicationWriter.supplyAdditionalConsumers(
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 b374e58..6b316f7 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -469,7 +469,6 @@
|| getMissingClasses().contains(type)
// TODO(b/150693139): Remove these exceptions once fixed.
|| InterfaceMethodRewriter.isCompanionClassType(type)
- || InterfaceMethodRewriter.hasDispatchClassSuffix(type)
|| InterfaceMethodRewriter.isEmulatedLibraryClassType(type)
|| type.toDescriptorString().startsWith("Lj$/$r8$retargetLibraryMember$")
|| TwrCloseResourceRewriter.isUtilityClassDescriptor(type)
@@ -577,6 +576,10 @@
return neverInlineDueToSingleCaller.contains(method.getReference());
}
+ public boolean isAssumeNoSideEffectsMethod(DexMethod method) {
+ return noSideEffects.containsKey(method);
+ }
+
public boolean isWhyAreYouNotInliningMethod(DexMethod method) {
return whyAreYouNotInlining.contains(method);
}
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 985090a..db914a4 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -63,12 +63,15 @@
import com.android.tools.r8.shaking.ProguardConfigurationRule;
import com.android.tools.r8.utils.IROrdering.IdentityIROrdering;
import com.android.tools.r8.utils.IROrdering.NondeterministicIROrdering;
+import com.android.tools.r8.utils.collections.DexClassAndMethodSet;
import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
import com.android.tools.r8.utils.structural.Ordered;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.io.IOException;
import java.io.PrintStream;
@@ -990,7 +993,7 @@
DexType libraryType,
DexType invalidSuperType,
String message,
- Set<DexEncodedMethod> retarget) {
+ DexClassAndMethodSet retarget) {
if (invalidLibraryClasses.add(invalidSuperType)) {
reporter.warning(
new InvalidLibrarySuperclassDiagnostic(
@@ -998,7 +1001,9 @@
Reference.classFromDescriptor(libraryType.toDescriptorString()),
Reference.classFromDescriptor(invalidSuperType.toDescriptorString()),
message,
- ListUtils.map(retarget, method -> method.getReference().asMethodReference())));
+ Lists.newArrayList(
+ Iterables.transform(
+ retarget, method -> method.getReference().asMethodReference()))));
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/IterableUtils.java b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
index ebbef95..cd1c0a0 100644
--- a/src/main/java/com/android/tools/r8/utils/IterableUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
@@ -15,6 +15,16 @@
public class IterableUtils {
+ public static <S, T> boolean any(
+ Iterable<S> iterable, Function<S, T> transform, Predicate<T> predicate) {
+ for (S element : iterable) {
+ if (predicate.test(transform.apply(element))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public static <T> Iterable<T> append(Iterable<T> iterable, T element) {
return Iterables.concat(iterable, singleton(element));
}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSet.java b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSet.java
new file mode 100644
index 0000000..ad7c3fe
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSet.java
@@ -0,0 +1,66 @@
+// 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.collections;
+
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.google.common.collect.ImmutableMap;
+import java.util.IdentityHashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Supplier;
+
+public class DexClassAndMethodSet extends DexClassAndMethodSetBase<DexClassAndMethod> {
+
+ private static final DexClassAndMethodSet EMPTY = new DexClassAndMethodSet(ImmutableMap::of);
+
+ protected DexClassAndMethodSet(
+ Supplier<? extends Map<DexMethod, DexClassAndMethod>> backingFactory) {
+ super(backingFactory);
+ }
+
+ protected DexClassAndMethodSet(
+ Supplier<? extends Map<DexMethod, DexClassAndMethod>> backingFactory,
+ Map<DexMethod, DexClassAndMethod> backing) {
+ super(backingFactory, backing);
+ }
+
+ public static DexClassAndMethodSet create() {
+ return new DexClassAndMethodSet(IdentityHashMap::new);
+ }
+
+ public static DexClassAndMethodSet create(int capacity) {
+ return new DexClassAndMethodSet(IdentityHashMap::new, new IdentityHashMap<>(capacity));
+ }
+
+ public static DexClassAndMethodSet create(DexClassAndMethod element) {
+ DexClassAndMethodSet result = create();
+ result.add(element);
+ return result;
+ }
+
+ public static DexClassAndMethodSet create(DexClassAndMethodSet methodSet) {
+ DexClassAndMethodSet newMethodSet = create();
+ newMethodSet.addAll(methodSet);
+ return newMethodSet;
+ }
+
+ public static DexClassAndMethodSet createConcurrent() {
+ return new DexClassAndMethodSet(ConcurrentHashMap::new);
+ }
+
+ public static DexClassAndMethodSet createLinked() {
+ return new DexClassAndMethodSet(LinkedHashMap::new);
+ }
+
+ public static DexClassAndMethodSet empty() {
+ return EMPTY;
+ }
+
+ public void addAll(DexClassAndMethodSet methods) {
+ backing.putAll(methods.backing);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java
new file mode 100644
index 0000000..797d8b6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java
@@ -0,0 +1,87 @@
+// 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.collections;
+
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.google.common.collect.Sets;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+public abstract class DexClassAndMethodSetBase<T extends DexClassAndMethod> implements Iterable<T> {
+
+ protected final Map<DexMethod, T> backing;
+ protected final Supplier<? extends Map<DexMethod, T>> backingFactory;
+
+ protected DexClassAndMethodSetBase(Supplier<? extends Map<DexMethod, T>> backingFactory) {
+ this(backingFactory, backingFactory.get());
+ }
+
+ protected DexClassAndMethodSetBase(
+ Supplier<? extends Map<DexMethod, T>> backingFactory, Map<DexMethod, T> backing) {
+ this.backing = backing;
+ this.backingFactory = backingFactory;
+ }
+
+ public boolean add(T method) {
+ T existing = backing.put(method.getReference(), method);
+ assert existing == null || existing.isStructurallyEqualTo(method);
+ return existing == null;
+ }
+
+ public void addAll(Iterable<T> methods) {
+ methods.forEach(this::add);
+ }
+
+ public boolean contains(DexEncodedMethod method) {
+ return backing.containsKey(method.getReference());
+ }
+
+ public boolean contains(T method) {
+ return backing.containsKey(method.getReference());
+ }
+
+ public void clear() {
+ backing.clear();
+ }
+
+ public boolean isEmpty() {
+ return backing.isEmpty();
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ return backing.values().iterator();
+ }
+
+ public boolean remove(DexMethod method) {
+ T existing = backing.remove(method);
+ return existing != null;
+ }
+
+ public boolean remove(DexEncodedMethod method) {
+ return remove(method.getReference());
+ }
+
+ public int size() {
+ return backing.size();
+ }
+
+ public Stream<T> stream() {
+ return backing.values().stream();
+ }
+
+ public Set<DexEncodedMethod> toDefinitionSet() {
+ assert backing instanceof IdentityHashMap;
+ Set<DexEncodedMethod> definitions = Sets.newIdentityHashSet();
+ forEach(method -> definitions.add(method.getDefinition()));
+ return definitions;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java b/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java
index f058bf1..d36d207 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java
@@ -11,32 +11,24 @@
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.ProgramMethod;
import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Sets;
import java.util.IdentityHashMap;
-import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
-import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
-import java.util.stream.Stream;
-public class ProgramMethodSet implements Iterable<ProgramMethod> {
+public class ProgramMethodSet extends DexClassAndMethodSetBase<ProgramMethod> {
private static final ProgramMethodSet EMPTY = new ProgramMethodSet(ImmutableMap::of);
- private final Map<DexMethod, ProgramMethod> backing;
- private final Supplier<? extends Map<DexMethod, ProgramMethod>> backingFactory;
-
protected ProgramMethodSet(Supplier<? extends Map<DexMethod, ProgramMethod>> backingFactory) {
- this(backingFactory, backingFactory.get());
+ super(backingFactory);
}
protected ProgramMethodSet(
Supplier<? extends Map<DexMethod, ProgramMethod>> backingFactory,
Map<DexMethod, ProgramMethod> backing) {
- this.backing = backing;
- this.backingFactory = backingFactory;
+ super(backingFactory, backing);
}
public static ProgramMethodSet create() {
@@ -71,16 +63,6 @@
return EMPTY;
}
- public boolean add(ProgramMethod method) {
- ProgramMethod existing = backing.put(method.getReference(), method);
- assert existing == null || existing.isStructurallyEqualTo(method);
- return existing == null;
- }
-
- public void addAll(Iterable<ProgramMethod> methods) {
- methods.forEach(this::add);
- }
-
public void addAll(ProgramMethodSet methods) {
backing.putAll(methods.backing);
}
@@ -89,36 +71,6 @@
return add(new ProgramMethod(clazz, definition));
}
- public boolean contains(DexEncodedMethod method) {
- return backing.containsKey(method.getReference());
- }
-
- public boolean contains(ProgramMethod method) {
- return backing.containsKey(method.getReference());
- }
-
- public void clear() {
- backing.clear();
- }
-
- public boolean isEmpty() {
- return backing.isEmpty();
- }
-
- @Override
- public Iterator<ProgramMethod> iterator() {
- return backing.values().iterator();
- }
-
- public boolean remove(DexMethod method) {
- ProgramMethod existing = backing.remove(method);
- return existing != null;
- }
-
- public boolean remove(DexEncodedMethod method) {
- return remove(method.getReference());
- }
-
public ProgramMethodSet rewrittenWithLens(DexDefinitionSupplier definitions, GraphLens lens) {
ProgramMethodSet rewritten = new ProgramMethodSet(backingFactory);
forEach(
@@ -130,19 +82,4 @@
});
return rewritten;
}
-
- public int size() {
- return backing.size();
- }
-
- public Stream<ProgramMethod> stream() {
- return backing.values().stream();
- }
-
- public Set<DexEncodedMethod> toDefinitionSet() {
- assert backing instanceof IdentityHashMap;
- Set<DexEncodedMethod> definitions = Sets.newIdentityHashSet();
- forEach(method -> definitions.add(method.getDefinition()));
- return definitions;
- }
}
diff --git a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
index 726136f..d25c465 100644
--- a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
@@ -96,7 +96,6 @@
Assert.assertTrue(
descriptor.contains(LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX)
|| descriptor.endsWith(InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX + ";")
- || descriptor.endsWith(InterfaceMethodRewriter.DISPATCH_CLASS_NAME_SUFFIX + ";")
|| descriptor.equals(TwrCloseResourceRewriter.UTILITY_CLASS_DESCRIPTOR)
|| descriptor.contains(SyntheticItems.EXTERNAL_SYNTHETIC_CLASS_SEPARATOR)
|| descriptor.equals(mainClassDescriptor));
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/IterableTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/IterableTest.java
index f6e0d0a..f998b89 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/IterableTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/IterableTest.java
@@ -4,16 +4,23 @@
package com.android.tools.r8.desugar.desugaredlibrary;
+import static org.hamcrest.MatcherAssert.assertThat;
+
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.CodeMatchers;
+import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
+import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.jetbrains.annotations.NotNull;
+import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -25,12 +32,13 @@
private final TestParameters parameters;
private final boolean shrinkDesugaredLibrary;
private static final String EXPECTED_OUTPUT =
- StringUtils.lines("1", "2", "3", "4", "5", "Count: 4");
+ StringUtils.lines("1", "2", "3", "4", "5", "Count: 4", "1", "2", "3", "4", "5");
@Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
public static List<Object[]> data() {
return buildParameters(
- BooleanUtils.values(), getTestParameters().withAllRuntimes().withAllApiLevels().build());
+ BooleanUtils.values(),
+ getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build());
}
public IterableTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
@@ -38,6 +46,54 @@
this.parameters = parameters;
}
+ private void inspect(CodeInspector inspector) {
+ if (parameters
+ .getApiLevel()
+ .isGreaterThanOrEqualTo(apiLevelWithDefaultInterfaceMethodsSupport())) {
+ assertThat(
+ inspector.clazz(MyIterableSub.class).uniqueMethodWithFinalName("myForEach"),
+ CodeMatchers.invokesMethod(null, MyIterable.class.getTypeName(), "forEach", null));
+ } else {
+ assertThat(
+ inspector.clazz(MyIterableSub.class).uniqueMethodWithFinalName("myForEach"),
+ CodeMatchers.invokesMethod(null, "j$.lang.Iterable$-CC", "$default$forEach", null));
+ }
+ }
+
+ @Test
+ public void testIterableD8Cf() throws Exception {
+ // Only test without shrinking desugared library.
+ Assume.assumeFalse(shrinkDesugaredLibrary);
+ // Use D8 to desugar with Java classfile output.
+ Path jar =
+ testForD8(Backend.CF)
+ .addInnerClasses(IterableTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .writeToZip();
+
+ if (parameters.getRuntime().isDex()) {
+ // Convert to DEX without desugaring and run.
+ testForD8()
+ .addProgramFiles(jar)
+ .setMinApi(parameters.getApiLevel())
+ .disableDesugaring()
+ .compile()
+ .addRunClasspathFiles(buildDesugaredLibrary(parameters.getApiLevel()))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ } else {
+ // Run on the JVM with desugared library on classpath.
+ testForJvm()
+ .addProgramFiles(jar)
+ .addRunClasspathFiles(buildDesugaredLibraryClassFile(parameters.getApiLevel()))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+ }
+
@Test
public void testIterable() throws Exception {
if (parameters.isCfRuntime()) {
@@ -69,6 +125,8 @@
iterable.forEach(System.out::println);
Stream<Integer> stream = StreamSupport.stream(iterable.spliterator(), false);
System.out.println("Count: " + stream.filter(x -> x != 3).count());
+ MyIterableSub<Integer> iterableSub = new MyIterableSub<>(Arrays.asList(1, 2, 3, 4, 5));
+ iterableSub.myForEach(System.out::println);
}
}
@@ -86,4 +144,15 @@
return collection.iterator();
}
}
+
+ static class MyIterableSub<E> extends MyIterable<E> {
+
+ public MyIterableSub(Collection<E> collection) {
+ super(collection);
+ }
+
+ public void myForEach(Consumer<E> consumer) {
+ super.forEach(consumer);
+ }
+ }
}
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 3e40117..31bbcbd 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
@@ -5,10 +5,13 @@
package com.android.tools.r8.desugar.desugaredlibrary;
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.assertEquals;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestCompileResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.graph.DexType;
@@ -19,6 +22,7 @@
import com.android.tools.r8.utils.codeinspector.CheckCastInstructionSubject;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.CodeMatchers;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.InvokeInstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -26,6 +30,12 @@
import com.android.tools.r8.utils.codeinspector.TypeSubject;
import com.google.common.collect.ImmutableSet;
import java.nio.file.Path;
+import java.time.DateTimeException;
+import java.time.ZoneId;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalQueries;
+import java.time.temporal.TemporalQuery;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@@ -48,6 +58,8 @@
"1970-01-02T10:17:36.789Z",
"GMT",
"GMT",
+ "true",
+ "true",
"Hello, world");
@Parameters(name = "{2}, shrinkDesugaredLibrary: {0}, traceReferencesKeepRules {1}")
@@ -69,7 +81,15 @@
this.parameters = parameters;
}
- private void checkRewrittenInvokes(CodeInspector inspector) {
+ private void checkRewrittenInvokesForD8(CodeInspector inspector) {
+ checkRewrittenInvokes(inspector, false);
+ }
+
+ private void checkRewrittenInvokesForR8(CodeInspector inspector) {
+ checkRewrittenInvokes(inspector, true);
+ }
+
+ private void checkRewrittenInvokes(CodeInspector inspector, boolean isR8) {
Set<String> expectedInvokeHolders;
Set<String> expectedCatchGuards;
Set<String> expectedCheckCastType;
@@ -118,6 +138,31 @@
.map(TypeSubject::toString)
.collect(Collectors.toSet());
assertEquals(expectedCatchGuards, foundCatchGuards);
+
+ if (isR8) {
+ assertThat(
+ inspector.clazz(TemporalAccessorImpl.class).uniqueMethodWithFinalName("query"),
+ not(isPresent()));
+ } else {
+ assertThat(
+ inspector.clazz(TemporalAccessorImplSub.class).uniqueMethodWithFinalName("query"),
+ CodeMatchers.invokesMethod(
+ null, TemporalAccessorImpl.class.getTypeName(), "query", null));
+ }
+ if (parameters
+ .getApiLevel()
+ .isGreaterThanOrEqualTo(TestBase.apiLevelWithDefaultInterfaceMethodsSupport())) {
+ assertThat(
+ inspector.clazz(TemporalAccessorImpl.class).uniqueMethodWithName("query"),
+ not(isPresent()));
+ } else {
+ assertThat(
+ inspector
+ .clazz(isR8 ? TemporalAccessorImplSub.class : TemporalAccessorImpl.class)
+ .uniqueMethodWithFinalName("query"),
+ CodeMatchers.invokesMethod(
+ null, "j$.time.temporal.TemporalAccessor$-CC", "$default$query", null));
+ }
}
private String desugaredLibraryKeepRules(
@@ -149,7 +194,7 @@
.setMinApi(parameters.getApiLevel())
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
- .inspect(this::checkRewrittenInvokes)
+ .inspect(this::checkRewrittenInvokesForD8)
.writeToZip();
String desugaredLibraryKeepRules;
@@ -198,7 +243,7 @@
.setMinApi(parameters.getApiLevel())
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
- .inspect(this::checkRewrittenInvokes);
+ .inspect(this::checkRewrittenInvokesForD8);
result
.addDesugaredCoreLibraryRunClassPath(
this::buildDesugaredLibrary,
@@ -219,11 +264,12 @@
testForR8(parameters.getBackend())
.addInnerClasses(JavaTimeTest.class)
.addKeepMainRule(TestClass.class)
+ .enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.enableInliningAnnotations()
.compile()
- .inspect(this::checkRewrittenInvokes);
+ .inspect(this::checkRewrittenInvokesForR8);
result
.addDesugaredCoreLibraryRunClassPath(
this::buildDesugaredLibrary,
@@ -234,6 +280,31 @@
.assertSuccessWithOutput(expectedOutput);
}
+ @NoVerticalClassMerging
+ static class TemporalAccessorImpl implements TemporalAccessor {
+ @Override
+ public boolean isSupported(TemporalField field) {
+ return false;
+ }
+
+ @Override
+ public long getLong(TemporalField field) {
+ throw new DateTimeException("Mock");
+ }
+ }
+
+ @NoVerticalClassMerging
+ static class TemporalAccessorImplSub extends TemporalAccessorImpl {
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R> R query(TemporalQuery<R> query) {
+ if (query == TemporalQueries.zoneId()) {
+ return (R) ZoneId.of("GMT");
+ }
+ return super.query(query);
+ }
+ }
+
static class TestClass {
@NeverInline
@@ -246,6 +317,36 @@
return System.currentTimeMillis() > 0 ? null : new Object();
}
+ public static void superInvokeOnLibraryDesugaredDefaultMethod() {
+ TemporalAccessor mock =
+ new TemporalAccessor() {
+ @Override
+ public boolean isSupported(TemporalField field) {
+ return false;
+ }
+
+ @Override
+ public long getLong(TemporalField field) {
+ throw new DateTimeException("Mock");
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R> R query(TemporalQuery<R> query) {
+ if (query == TemporalQueries.zoneId()) {
+ return (R) ZoneId.of("GMT");
+ }
+ return TemporalAccessor.super.query(query);
+ }
+ };
+ System.out.println(ZoneId.from(mock).equals(ZoneId.of("GMT")));
+ }
+
+ public static void superInvokeOnLibraryDesugaredDefaultMethodFromSubclass() {
+ TemporalAccessor mock = new TemporalAccessorImplSub();
+ System.out.println(ZoneId.from(mock).equals(ZoneId.of("GMT")));
+ }
+
public static void main(String[] args) {
java.time.Clock.systemDefaultZone();
try {
@@ -266,6 +367,9 @@
System.out.println(timeZone.getID());
System.out.println(timeZone.toZoneId().getId());
+ superInvokeOnLibraryDesugaredDefaultMethod();
+ superInvokeOnLibraryDesugaredDefaultMethodFromSubclass();
+
System.out.println("Hello, world");
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/conversion/StringSwitchConversionFromIfTest.java b/src/test/java/com/android/tools/r8/ir/conversion/StringSwitchConversionFromIfTest.java
index 294efee..bcdd93f 100644
--- a/src/test/java/com/android/tools/r8/ir/conversion/StringSwitchConversionFromIfTest.java
+++ b/src/test/java/com/android/tools/r8/ir/conversion/StringSwitchConversionFromIfTest.java
@@ -7,7 +7,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.assertNotEquals;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
@@ -87,7 +87,16 @@
// instruction may throw. This holds even if the string-switch instruction is
// compiled to a sequence of `if (x.equals("..."))` instructions that do not even
// use the hash code.
- assertNotEquals(0, hashCodeValues.size());
+ assertTrue(
+ code.collectArguments().get(0).uniqueUsers().stream()
+ .filter(Instruction::isInvokeMethod)
+ .map(Instruction::asInvokeMethod)
+ .map(invoke -> invoke.getInvokedMethod().getName().toSourceString())
+ .anyMatch(
+ name ->
+ name.equals("getClass")
+ || name.equals("hashCode")
+ || name.equals("requireNonNull")));
}
})
.run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
index af047b9..7049089 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.optimize;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -333,7 +335,7 @@
assertCounters(INLINABLE, NEVER_INLINABLE, countInvokes(inspector, m));
m = clazz.method("int", "notInlinableDueToMissingNpe", ImmutableList.of("inlining.A"));
- assertCounters(INLINABLE, NEVER_INLINABLE, countInvokes(inspector, m));
+ assertCounters(INLINABLE, ALWAYS_INLINABLE, countInvokes(inspector, m));
m = clazz.method("int", "notInlinableDueToSideEffect", ImmutableList.of("inlining.A"));
assertCounters(INLINABLE, NEVER_INLINABLE, countInvokes(inspector, m));
@@ -354,7 +356,12 @@
CodeInspector inspector =
new CodeInspector(getGeneratedFiles(), getGeneratedProguardMap(), null);
ClassSubject clazz = inspector.clazz(nullabilityClass);
- MethodSubject m = clazz.method("int", "conditionalOperator", ImmutableList.of("inlining.A"));
+ assertThat(clazz.uniqueMethodWithName("conditionalOperator"), isAbsent());
+
+ // The enum parameter may get unboxed.
+ MethodSubject m =
+ clazz.uniqueMethodWithName(
+ parameters.isCfRuntime() ? "moreControlFlows" : "moreControlFlows$enumunboxing$");
assertTrue(m.isPresent());
// Verify that a.b() is resolved to an inline instance-get.
@@ -365,27 +372,6 @@
InstructionSubject instruction = iterator.next();
if (instruction.isInstanceGet()) {
++instanceGetCount;
- } else if (instruction.isInvoke()) {
- ++invokeCount;
- }
- }
- assertEquals(1, instanceGetCount);
- assertEquals(0, invokeCount);
-
- // The enum parameter may get unboxed.
- m =
- clazz.uniqueMethodWithName(
- parameters.isCfRuntime() ? "moreControlFlows" : "moreControlFlows$enumunboxing$");
- assertTrue(m.isPresent());
-
- // Verify that a.b() is resolved to an inline instance-get.
- iterator = m.iterateInstructions();
- instanceGetCount = 0;
- invokeCount = 0;
- while (iterator.hasNext()) {
- InstructionSubject instruction = iterator.next();
- if (instruction.isInstanceGet()) {
- ++instanceGetCount;
} else if (instruction.isInvoke() && !isEnumInvoke(instruction)) {
++invokeCount;
}
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
index c253c45..8d01a11 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.BasicBlock;
@@ -23,6 +24,7 @@
import com.android.tools.r8.ir.code.Move;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
import java.util.LinkedList;
import java.util.List;
@@ -62,6 +64,18 @@
}
@Override
+ public boolean replaceCurrentInstructionByNullCheckIfPossible(
+ AppView<?> appView, ProgramMethod context) {
+ throw new Unimplemented();
+ }
+
+ @Override
+ public boolean replaceCurrentInstructionByInitClassIfPossible(
+ AppView<AppInfoWithLiveness> appView, IRCode code, DexType type) {
+ throw new Unimplemented();
+ }
+
+ @Override
public void replaceCurrentInstructionWithConstClass(
AppView<?> appView, IRCode code, DexType type, DebugLocalInfo localInfo) {
throw new Unimplemented();
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java
index 447ed7e..7565cf7 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java
@@ -87,7 +87,7 @@
.assertErrorsMatch(
diagnosticMessage(
containsString(
- "--main-dex-list-output require --main-dex-rules and/or"
+ "--main-dex-list-output requires --main-dex-rules and/or"
+ " --main-dex-list"))));
}
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumeNoSideEffectsForJavaLangClassTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumeNoSideEffectsForJavaLangClassTest.java
index eb29f88..fed4e6d 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumeNoSideEffectsForJavaLangClassTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumeNoSideEffectsForJavaLangClassTest.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethodWithName;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf;
+import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.AssumeMayHaveSideEffects;
@@ -71,7 +72,11 @@
assertThat(
methodSubject,
onlyIf(maybeNullReceiver || maybeSubtype, invokesMethodWithName("hashCode")));
- assertThat(methodSubject, onlyIf(maybeNullReceiver, invokesMethodWithName("getClass")));
+ assertThat(
+ methodSubject,
+ onlyIf(
+ maybeNullReceiver,
+ anyOf(invokesMethodWithName("getClass"), invokesMethodWithName("requireNonNull"))));
assertThat(
methodSubject,
onlyIf(maybeNullReceiver || maybeSubtype, invokesMethodWithName("toString")));
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java
index 1c7f6ac..8130a6f 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java
@@ -111,7 +111,7 @@
private final TestConfig config;
private final boolean enableHorizontalClassMerging;
- @Parameterized.Parameters(name = "{0} {1}")
+ @Parameterized.Parameters(name = "{0}, config: {1}, horizontal: {2}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withAllRuntimesAndApiLevels().build(),
diff --git a/src/test/java/com/android/tools/r8/smali/OutlineTest.java b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
index e85f2d8..04ac085 100644
--- a/src/test/java/com/android/tools/r8/smali/OutlineTest.java
+++ b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
@@ -252,7 +252,7 @@
" move-result-object v0",
" invoke-virtual { v0, v1 }, " + stringBuilderAppendSignature,
" move-result-object v0",
- " invoke-virtual { v0 }, " + stringBuilderToStringSignature,
+ " invoke-virtual { v0, v1 }, " + stringBuilderAppendSignature,
" move-result-object v0",
" const v0, 0",
" const v1, 1",
@@ -818,7 +818,7 @@
" move-result-object v0",
" invoke-virtual { v0, v1 }, " + stringBuilderAppendSignature,
" move-result-object v0",
- " invoke-virtual { v0 }, " + stringBuilderToStringSignature,
+ " invoke-virtual { v0, v1 }, " + stringBuilderAppendSignature,
" return-void");
String returnType2 = "java.lang.String";
@@ -866,14 +866,14 @@
ClassSubject clazz = inspector.clazz(OutlineOptions.CLASS_NAME);
assertTrue(clazz.isPresent());
assertEquals(3, clazz.getDexProgramClass().getMethodCollection().numberOfDirectMethods());
- // Collect the return types of the putlines for the body of method1 and method2.
+ // Collect the return types of the outlines for the body of method1 and method2.
List<DexType> r = new ArrayList<>();
for (DexEncodedMethod directMethod : clazz.getDexProgramClass().directMethods()) {
if (directMethod.getCode().asDexCode().instructions[0] instanceof InvokeVirtual) {
r.add(directMethod.method.proto.returnType);
}
}
- assert r.size() == 2;
+ assertEquals(2, r.size());
DexType r1 = r.get(0);
DexType r2 = r.get(1);
DexItemFactory factory = inspector.getFactory();
@@ -882,7 +882,7 @@
// Run the code.
String result = runArt(processedApplication);
- assertEquals("TestTestTestTest", result);
+ assertEquals("TestTestTestTestTest", result);
}
@Test
diff --git a/tools/archive.py b/tools/archive.py
index b7ca7c0..e92e87d 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -145,6 +145,8 @@
utils.D8,
utils.R8LIB,
utils.R8LIB_NO_DEPS,
+ utils.R8RETRACE,
+ utils.R8RETRACE_NO_DEPS,
utils.LIBRARY_DESUGAR_CONVERSIONS,
'-Pno_internal'
])
@@ -191,6 +193,8 @@
utils.R8_FULL_EXCLUDE_DEPS_JAR,
utils.R8LIB_EXCLUDE_DEPS_JAR,
utils.R8LIB_EXCLUDE_DEPS_JAR + '.map',
+ utils.R8RETRACE_JAR,
+ utils.R8RETRACE_EXCLUDE_DEPS_JAR,
utils.MAVEN_ZIP,
utils.MAVEN_ZIP_LIB,
utils.DESUGAR_CONFIGURATION,
diff --git a/tools/as_utils.py b/tools/as_utils.py
index fa81ac4..43153c6 100644
--- a/tools/as_utils.py
+++ b/tools/as_utils.py
@@ -3,11 +3,18 @@
# 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.
+import utils
+
from distutils.version import LooseVersion
-from HTMLParser import HTMLParser
import os
import shutil
+if utils.is_python3():
+ from html.parser import HTMLParser
+else:
+ from HTMLParser import HTMLParser
+
+
def add_r8_dependency(checkout_dir, temp_dir, minified):
build_file = os.path.join(checkout_dir, 'build.gradle')
assert os.path.isfile(build_file), (
diff --git a/tools/r8_release.py b/tools/r8_release.py
index c3aff38..31bb4c6 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -362,6 +362,7 @@
g4_open('src.jar')
g4_open('lib.jar')
g4_open('lib.jar.map')
+ g4_open('retrace.jar')
g4_open('desugar_jdk_libs_configuration.jar')
download_file(options.version, 'r8-full-exclude-deps.jar', 'full.jar')
download_file(options.version, 'r8-src.jar', 'src.jar')
@@ -370,6 +371,7 @@
options.version, 'r8lib-exclude-deps.jar.map', 'lib.jar.map')
download_file(options.version, 'desugar_jdk_libs_configuration.jar',
'desugar_jdk_libs_configuration.jar')
+ download_file(options.version, 'r8retrace-exclude-deps.jar', 'retrace.jar')
g4_open('METADATA')
sed(r'[1-9]\.[0-9]{1,2}\.[0-9]{1,3}-dev',
options.version,
diff --git a/tools/run_on_app_dump.py b/tools/run_on_app_dump.py
index 4509656..c4de6e3 100755
--- a/tools/run_on_app_dump.py
+++ b/tools/run_on_app_dump.py
@@ -5,6 +5,7 @@
import adb
import apk_masseur
+import as_utils
import compiledump
import gradle
import optparse
@@ -516,7 +517,7 @@
result['status'] = 'success'
result_per_shrinker = build_app_with_shrinkers(
app, options, temp_dir, app_dir)
- for shrinker, shrinker_result in result_per_shrinker.iteritems():
+ for shrinker, shrinker_result in result_per_shrinker.items():
result[shrinker] = shrinker_result
return result
@@ -940,7 +941,8 @@
assert any(app.name == app_name for app in options.apps)
if options.shrinker:
for shrinker in options.shrinker:
- assert shrinker in SHRINKERS
+ assert shrinker in SHRINKERS, (
+ 'Shrinker must be one of %s' % ', '.join(SHRINKERS))
else:
options.shrinker = [shrinker for shrinker in SHRINKERS]
diff --git a/tools/utils.py b/tools/utils.py
index 351a038..ee49000 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -41,6 +41,8 @@
R8 = 'r8'
R8LIB = 'r8lib'
R8LIB_NO_DEPS = 'r8LibNoDeps'
+R8RETRACE = 'R8Retrace'
+R8RETRACE_NO_DEPS = 'R8RetraceNoDeps'
R8_SRC = 'sourceJar'
LIBRARY_DESUGAR_CONVERSIONS = 'buildLibraryDesugarConversions'
@@ -52,6 +54,8 @@
R8_SRC_JAR = os.path.join(LIBS, 'r8-src.jar')
R8LIB_EXCLUDE_DEPS_JAR = os.path.join(LIBS, 'r8lib-exclude-deps.jar')
R8_FULL_EXCLUDE_DEPS_JAR = os.path.join(LIBS, 'r8-full-exclude-deps.jar')
+R8RETRACE_JAR = os.path.join(LIBS, 'r8retrace.jar')
+R8RETRACE_EXCLUDE_DEPS_JAR = os.path.join(LIBS, 'r8retrace-exclude-deps.jar')
MAVEN_ZIP = os.path.join(LIBS, 'r8.zip')
MAVEN_ZIP_LIB = os.path.join(LIBS, 'r8lib.zip')
LIBRARY_DESUGAR_CONVERSIONS_ZIP = os.path.join(LIBS, 'library_desugar_conversions.zip')
@@ -104,6 +108,9 @@
version = os.environ.get(ANDROID_TOOLS_VERSION_ENVIRONMENT_NAME, '28.0.3')
return os.path.join(getAndroidHome(), 'build-tools', version)
+def is_python3():
+ return sys.version_info.major == 3
+
def Print(s, quiet=False):
if quiet:
return