Merge commit '401798770b3c9f8529a1d582c969dc2afb3ee756' into dev-release
diff --git a/infra/config/global/luci-milo.cfg b/infra/config/global/luci-milo.cfg
index 1c2c87f..7400340 100644
--- a/infra/config/global/luci-milo.cfg
+++ b/infra/config/global/luci-milo.cfg
@@ -26,11 +26,6 @@
short_name: "jdk9"
}
builders {
- name: "buildbucket/luci.r8.ci/linux-jdk8_9"
- category: "R8"
- short_name: "jdk8_9"
- }
- builders {
name: "buildbucket/luci.r8.ci/linux-android-4.0.4"
category: "R8"
short_name: "4.0.4"
diff --git a/infra/config/global/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
index a18a124..af1ff68 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -84,12 +84,23 @@
triggers: "linux-android-10.0.0_release"
triggers: "linux-internal_release"
triggers: "linux-jctf_release"
- triggers: "linux-run-on-app-dump_release"
triggers: "linux_release"
triggers: "r8cf-linux-jctf_release"
triggers: "windows_release"
}
+trigger {
+ id: "app-dump-gitiles-trigger"
+ acl_sets: "default"
+ gitiles: {
+ repo: "https://r8.googlesource.com/r8"
+ # Only trigger apps from 3.0 (this works until we reach version 10)
+ refs: "regexp:refs/heads/[3-9]+\\.[0-9]+(\\.[0-9]+)?"
+ path_regexps: "src/main/java/com/android/tools/r8/Version.java"
+ }
+ triggers: "linux-run-on-app-dump_release"
+}
+
job {
id: "archive"
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 8e56a8e..8a88d3d 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -290,9 +290,11 @@
// desugared library only on cf inputs. We cannot easily rewrite part of the program
// without iterating again the IR. We fall-back to writing one app with rewriting and
// merging it with the other app in rewriteNonDexInputs.
+ timing.begin("Rewrite non-dex inputs");
DexApplication app =
rewriteNonDexInputs(
appView, inputApp, options, executor, timing, appView.appInfo().app());
+ timing.end();
appView.setAppInfo(
new AppInfo(
appView.appInfo().getSyntheticItems().commit(app),
diff --git a/src/main/java/com/android/tools/r8/DexSplitterHelper.java b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
index 8b5d011..0230729 100644
--- a/src/main/java/com/android/tools/r8/DexSplitterHelper.java
+++ b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
@@ -87,6 +87,7 @@
getDistribution(app, featureClassMapping, mapper);
for (Entry<String, LazyLoadedDexApplication.Builder> entry : applications.entrySet()) {
String feature = entry.getKey();
+ timing.begin("Feature " + feature);
DexApplication featureApp = entry.getValue().build();
assert !options.hasMethodsFilter();
@@ -121,6 +122,7 @@
} finally {
consumer.finished(options.reporter);
}
+ timing.end();
}
} catch (ExecutionException e) {
throw unwrapExecutionException(e);
diff --git a/src/main/java/com/android/tools/r8/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
index a4ce917..da40e79 100644
--- a/src/main/java/com/android/tools/r8/GenerateLintFiles.java
+++ b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
@@ -146,7 +146,7 @@
List<DexEncodedMethod> directMethods = new ArrayList<>();
List<DexEncodedMethod> virtualMethods = new ArrayList<>();
for (DexEncodedMethod method : methods) {
- assert method.holder() == clazz.type;
+ assert method.getHolderType() == clazz.type;
CfCode code = null;
if (!method.accessFlags.isAbstract() /*&& !method.accessFlags.isNative()*/) {
code = buildEmptyThrowingCfCode(method.method);
diff --git a/src/main/java/com/android/tools/r8/PrintUses.java b/src/main/java/com/android/tools/r8/PrintUses.java
index 2f53841..3d039c9 100644
--- a/src/main/java/com/android/tools/r8/PrintUses.java
+++ b/src/main/java/com/android/tools/r8/PrintUses.java
@@ -201,7 +201,7 @@
private void addField(DexField field) {
addType(field.type);
DexEncodedField baseField = appInfo.resolveField(field).getResolvedField();
- if (baseField != null && baseField.holder() != field.holder) {
+ if (baseField != null && baseField.getHolderType() != field.holder) {
field = baseField.field;
}
addType(field.holder);
@@ -215,7 +215,7 @@
noObfuscationTypes.add(field.holder);
}
if (baseField.accessFlags.isVisibilityDependingOnPackage()) {
- keepPackageNames.add(baseField.holder().getPackageName());
+ keepPackageNames.add(baseField.getHolderType().getPackageName());
}
typeFields.add(field);
}
@@ -236,7 +236,7 @@
noObfuscationTypes.add(method.holder);
}
if (definition.accessFlags.isVisibilityDependingOnPackage()) {
- keepPackageNames.add(definition.holder().getPackageName());
+ keepPackageNames.add(definition.getHolderType().getPackageName());
}
typeMethods.add(method);
}
@@ -496,7 +496,7 @@
if (encodedMethod.accessFlags.isStatic()) {
append("<clinit>");
} else {
- String holderName = encodedMethod.holder().toSourceString();
+ String holderName = encodedMethod.getHolderType().toSourceString();
String constructorName = holderName.substring(holderName.lastIndexOf('.') + 1);
append(constructorName);
}
diff --git a/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java b/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
index 93fc650..28d7592 100644
--- a/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
@@ -302,7 +302,7 @@
private void updateHints(LiveIntervals intervals) {
for (Phi phi : intervals.getValue().uniquePhiUsers()) {
- if (!phi.isValueOnStack()) {
+ if (!phi.isValueOnStack() && phi.getLiveIntervals().getHint() == null) {
phi.getLiveIntervals().setHint(intervals, unhandled);
for (Value value : phi.getOperands()) {
value.getLiveIntervals().setHint(intervals, unhandled);
diff --git a/src/main/java/com/android/tools/r8/cf/CfVersion.java b/src/main/java/com/android/tools/r8/cf/CfVersion.java
index d90696c..f606b93 100644
--- a/src/main/java/com/android/tools/r8/cf/CfVersion.java
+++ b/src/main/java/com/android/tools/r8/cf/CfVersion.java
@@ -3,14 +3,14 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf;
-import com.android.tools.r8.utils.structural.DefaultCompareToVisitor;
import com.android.tools.r8.utils.structural.Equatable;
import com.android.tools.r8.utils.structural.HashCodeVisitor;
-import com.android.tools.r8.utils.structural.Ordered;
+import com.android.tools.r8.utils.structural.StructuralAccept;
+import com.android.tools.r8.utils.structural.StructuralItem;
import com.android.tools.r8.utils.structural.StructuralSpecification;
import org.objectweb.asm.Opcodes;
-public final class CfVersion implements Ordered<CfVersion> {
+public final class CfVersion implements StructuralItem<CfVersion> {
public static final CfVersion V1_1 = new CfVersion(Opcodes.V1_1);
public static final CfVersion V1_2 = new CfVersion(Opcodes.V1_2);
@@ -47,23 +47,28 @@
return version;
}
- private static void accept(StructuralSpecification<CfVersion, ?> spec) {
+ private static void specify(StructuralSpecification<CfVersion, ?> spec) {
spec.withInt(CfVersion::major).withInt(CfVersion::minor);
}
@Override
+ public CfVersion self() {
+ return this;
+ }
+
+ @Override
+ public StructuralAccept<CfVersion> getStructuralAccept() {
+ return CfVersion::specify;
+ }
+
+ @Override
public boolean equals(Object o) {
return Equatable.equalsImpl(this, o);
}
@Override
public int hashCode() {
- return HashCodeVisitor.run(this, CfVersion::accept);
- }
-
- @Override
- public int compareTo(CfVersion other) {
- return DefaultCompareToVisitor.run(this, other, CfVersion::accept);
+ return HashCodeVisitor.run(this, CfVersion::specify);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java b/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java
index 4574386..ec4b22f 100644
--- a/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java
+++ b/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java
@@ -238,8 +238,8 @@
if (argumentIndex < 0) {
argumentType =
code.method().isInstanceInitializer()
- ? new ThisInstanceInfo(instruction.asArgument(), code.method().holder())
- : createInitializedType(code.method().holder());
+ ? new ThisInstanceInfo(instruction.asArgument(), code.method().getHolderType())
+ : createInitializedType(code.method().getHolderType());
} else {
argumentType =
createInitializedType(code.method().method.proto.parameters.values[argumentIndex]);
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java b/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
index c05c20d..7cfcf5b 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -51,8 +52,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
}
public Opcode getOpcode() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java
index 70b41e1..44699bf 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -43,8 +44,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java
index ae086f3..a8d3546 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -41,8 +42,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
}
public MemberType getType() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java
index eb43f6b..fa27c39 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -43,8 +44,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
}
private int getStoreType() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java b/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
index a391dbb..b4b33b1 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import java.util.ListIterator;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -43,8 +44,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return type.compareTo(((CfCheckCast) other).type);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ type.acceptCompareTo(((CfCheckCast) other).type, visitor);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfCmp.java b/src/main/java/com/android/tools/r8/cf/code/CfCmp.java
index 23b0c8b..d1d4bb9 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfCmp.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfCmp.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -48,8 +49,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
}
public Bias getBias() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java b/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
index ced7984..7931528 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import java.util.ListIterator;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
@@ -39,8 +40,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return type.compareTo(((CfConstClass) other).type);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ type.acceptCompareTo(((CfConstClass) other).type, visitor);
}
public DexType getType() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
index 9c3b478..17c2abe 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import java.util.ListIterator;
import org.objectweb.asm.MethodVisitor;
@@ -43,8 +44,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return handle.compareTo(((CfConstMethodHandle) other).handle);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ handle.acceptCompareTo(((CfConstMethodHandle) other).handle, visitor);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
index c285c80..85f125e 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import java.util.ListIterator;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
@@ -43,8 +44,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return type.compareTo(((CfConstMethodType) other).type);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ type.acceptCompareTo(((CfConstMethodType) other).type, visitor);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java b/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java
index 1835f18..7575c49 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -43,8 +44,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java b/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java
index e48643e..23f3a2d 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java
@@ -20,7 +20,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
-import java.util.Comparator;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -40,10 +40,12 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return Comparator.comparing(CfConstNumber::getRawValue)
- .thenComparing(CfConstNumber::getType)
- .compare(this, (CfConstNumber) other);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ visitor.visit(
+ this,
+ (CfConstNumber) other,
+ spec -> spec.withLong(CfConstNumber::getRawValue).withItem(CfConstNumber::getType));
}
public ValueType getType() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstString.java b/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
index 7ac89da..827e77e 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import org.objectweb.asm.MethodVisitor;
public class CfConstString extends CfInstruction {
@@ -35,8 +36,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return string.compareTo(other.asConstString().string);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ string.acceptCompareTo(other.asConstString().string, visitor);
}
public DexString getString() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java b/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java
index b42194f..e880a23 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import java.util.ListIterator;
import org.objectweb.asm.MethodVisitor;
@@ -42,8 +43,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return item.referenceCompareTo(((CfDexItemBasedConstString) other).item);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ visitor.visitDexReference(item, ((CfDexItemBasedConstString) other).item);
}
public DexReference getItem() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
index 0915f0d..978a43d 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
@@ -23,7 +23,8 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
-import java.util.Comparator;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
import java.util.ListIterator;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -34,6 +35,10 @@
private final DexField field;
private final DexField declaringField;
+ private static void specify(StructuralSpecification<CfFieldInstruction, ?> spec) {
+ spec.withInt(f -> f.opcode).withItem(f -> f.field).withItem(f -> f.declaringField);
+ }
+
public CfFieldInstruction(int opcode, DexField field, DexField declaringField) {
this.opcode = opcode;
this.field = field;
@@ -55,10 +60,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return Comparator.comparing(CfFieldInstruction::getField)
- .thenComparing(field -> field.declaringField)
- .compare(this, (CfFieldInstruction) other);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ visitor.visit(this, other.asFieldInstruction(), CfFieldInstruction::specify);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
index cdcb3ec..d89ef90 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
import java.util.ArrayDeque;
@@ -152,10 +153,11 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
// The frame should be determined by the code so it should for equal iff the code is equal.
// Thus we just require the frame to be in place.
- return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
}
private static class InitializedType extends FrameType {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfGoto.java b/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
index 0867a89..7b3ccf8 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -35,8 +36,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return helper.compareLabels(target, ((CfGoto) other).target);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ helper.compareLabels(target, ((CfGoto) other).target, visitor);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIf.java b/src/main/java/com/android/tools/r8/cf/code/CfIf.java
index ddca593..6e00da7 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfIf.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfIf.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -43,11 +44,12 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
CfIf otherIf = (CfIf) other;
assert kind == otherIf.kind;
assert type == otherIf.type;
- return helper.compareLabels(target, otherIf.target);
+ helper.compareLabels(target, otherIf.target, visitor);
}
public ValueType getType() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java b/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
index efedff8..35f9af3 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -43,11 +44,12 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
CfIfCmp otherIf = (CfIfCmp) other;
assert kind == otherIf.kind;
assert type == otherIf.type;
- return helper.compareLabels(target, otherIf.target);
+ helper.compareLabels(target, otherIf.target, visitor);
}
public Type getKind() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIinc.java b/src/main/java/com/android/tools/r8/cf/code/CfIinc.java
index 9e59ba6..04a7599 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfIinc.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfIinc.java
@@ -19,7 +19,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
-import java.util.Comparator;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -39,10 +39,12 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return Comparator.comparingInt(CfIinc::getLocalIndex)
- .thenComparing(CfIinc::getIncrement)
- .compare(this, (CfIinc) other);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ visitor.visit(
+ this,
+ (CfIinc) other,
+ spec -> spec.withInt(CfIinc::getLocalIndex).withInt(CfIinc::getIncrement));
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java b/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
index 53437ec..4d311fe 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import java.util.ListIterator;
import org.objectweb.asm.MethodVisitor;
@@ -48,8 +49,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return clazz.compareTo(((CfInitClass) other).clazz);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ clazz.acceptCompareTo(((CfInitClass) other).clazz, visitor);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java b/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
index 1bbb32a..85d9b59 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
@@ -20,6 +20,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import java.util.ListIterator;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -42,8 +43,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return type.compareTo(other.asInstanceOf().type);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ type.acceptCompareTo(other.asInstanceOf().type, visitor);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
index 3c36d33..d4986b3 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import java.util.ListIterator;
import org.objectweb.asm.MethodVisitor;
@@ -60,11 +61,17 @@
* <p>If an instruction is uniquely determined by the "compare id" then the override should simply
* call '{@code CfCompareHelper::compareIdUniquelyDeterminesEquality}'.
*/
- public abstract int internalCompareTo(CfInstruction other, CfCompareHelper helper);
+ public abstract void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper);
- public final int compareTo(CfInstruction o, CfCompareHelper helper) {
+ public final void acceptCompareTo(
+ CfInstruction o, CompareToVisitor visitor, CfCompareHelper helper) {
int diff = getCompareToId() - o.getCompareToId();
- return diff != 0 ? diff : internalCompareTo(o, helper);
+ if (diff == 0) {
+ internalAcceptCompareTo(o, visitor, helper);
+ } else {
+ visitor.visitInt(getCompareToId(), o.getCompareToId());
+ }
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
index 3e07e17..4d1af96 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -33,6 +33,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import java.util.Arrays;
import java.util.ListIterator;
import org.objectweb.asm.MethodVisitor;
@@ -59,10 +60,13 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
CfInvoke otherInvoke = other.asInvoke();
- int itfDiff = Boolean.compare(itf, otherInvoke.itf);
- return itfDiff != 0 ? itfDiff : method.compareTo(otherInvoke.method);
+ visitor.visit(
+ this,
+ otherInvoke,
+ spec -> spec.withBool(CfInvoke::isInterface).withItem(CfInvoke::getMethod));
}
public DexMethod getMethod() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
index 92384c8..fc1f3e5 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
@@ -26,6 +26,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
@@ -48,8 +49,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return callSite.compareTo(((CfInvokeDynamic) other).callSite);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ callSite.acceptCompareTo(((CfInvokeDynamic) other).callSite, visitor);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java b/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java
index ed6c16f..d8cd219 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java
@@ -20,6 +20,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import org.objectweb.asm.MethodVisitor;
public class CfJsrRet extends CfInstruction {
@@ -41,7 +42,8 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
throw error();
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLabel.java b/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
index 2bfdd93..dc6d863 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
@@ -38,8 +39,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return helper.compareLabels(this, other.asLabel());
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ helper.compareLabels(this, other.asLabel(), visitor);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLoad.java b/src/main/java/com/android/tools/r8/cf/code/CfLoad.java
index 3160d6e..4e48880 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfLoad.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLoad.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -40,8 +41,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return Integer.compare(var, other.asLoad().var);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ visitor.visitInt(var, other.asLoad().var);
}
private int getLoadType() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java b/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
index d7f2c7d..d6264f7 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -53,8 +54,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
}
public NumericType getType() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java b/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java
index 8225673..520c168 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -42,8 +43,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
index a504f8c..ff9ea71 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
@@ -21,7 +21,7 @@
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.InternalOptions;
-import java.util.Comparator;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import java.util.ListIterator;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -50,10 +50,12 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return Comparator.comparingInt(CfMultiANewArray::getDimensions)
- .thenComparing(CfMultiANewArray::getType)
- .compare(this, ((CfMultiANewArray) other));
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ visitor.visit(
+ this,
+ (CfMultiANewArray) other,
+ spec -> spec.withInt(CfMultiANewArray::getDimensions).withItem(CfMultiANewArray::getType));
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNeg.java b/src/main/java/com/android/tools/r8/cf/code/CfNeg.java
index a7b8c32..dce1028 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNeg.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNeg.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -43,8 +44,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNew.java b/src/main/java/com/android/tools/r8/cf/code/CfNew.java
index 41f468c..87a407d 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNew.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNew.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import java.util.ListIterator;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -43,8 +44,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return type.compareTo(((CfNew) other).type);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ type.acceptCompareTo(((CfNew) other).type, visitor);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
index 414bd58..0dbf78e 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import java.util.ListIterator;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -46,8 +47,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return type.compareTo(((CfNewArray) other).type);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ type.acceptCompareTo(((CfNewArray) other).type, visitor);
}
private int getPrimitiveTypeCode() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNop.java b/src/main/java/com/android/tools/r8/cf/code/CfNop.java
index 87123a4..0a69673b 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNop.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -29,8 +30,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java b/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java
index a7e60e4..35c78a7 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -45,8 +46,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
}
public NumericType getFromType() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfPosition.java b/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
index 233a3ef..df8612e 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import org.objectweb.asm.MethodVisitor;
public class CfPosition extends CfInstruction {
@@ -37,10 +38,14 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- CfPosition otherPosition = (CfPosition) other;
- int lineDiff = position.line - otherPosition.position.line;
- return lineDiff != 0 ? lineDiff : helper.compareLabels(label, otherPosition.label);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ visitor.visit(
+ this,
+ (CfPosition) other,
+ spec ->
+ spec.withInt(p -> p.position.line)
+ .withCustomItem(p -> p.label, helper.labelAcceptor()));
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfReturn.java b/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
index 8305860..b09fbea 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -42,8 +43,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
}
private int getOpcode() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java b/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
index 687d903..c52efe3 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -34,8 +35,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java
index c28cb32..c6c39be 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -87,8 +88,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStore.java b/src/main/java/com/android/tools/r8/cf/code/CfStore.java
index debb573..e424474 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfStore.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfStore.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -41,8 +42,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return Integer.compare(var, other.asStore().var);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ visitor.visitInt(var, other.asStore().var);
}
private int getStoreType() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java b/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
index ddfd927..86abb2e 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
@@ -3,8 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf.code;
-import static com.android.tools.r8.utils.ComparatorUtils.listComparator;
-
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
@@ -21,10 +19,9 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.utils.ComparatorUtils;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
-import java.util.Comparator;
import java.util.List;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
@@ -54,12 +51,17 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
assert kind == ((CfSwitch) other).kind;
- return Comparator.comparing(CfSwitch::getDefaultTarget, helper::compareLabels)
- .thenComparing(insn -> insn.keys, ComparatorUtils::compareIntArray)
- .thenComparing(CfSwitch::getSwitchTargets, listComparator(helper::compareLabels))
- .compare(this, (CfSwitch) other);
+
+ visitor.visit(
+ this,
+ (CfSwitch) other,
+ spec ->
+ spec.withCustomItem(CfSwitch::getDefaultTarget, helper.labelAcceptor())
+ .withIntArray(i -> i.keys)
+ .withCustomItemCollection(CfSwitch::getSwitchTargets, helper.labelAcceptor()));
}
public Kind getKind() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfThrow.java b/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
index b165711..7243175 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -35,8 +36,9 @@
}
@Override
- public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ public void internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java b/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java
index a93f320..6cc3b13 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java
@@ -10,9 +10,8 @@
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.CatchHandlers;
import com.android.tools.r8.ir.conversion.CfBuilder;
-import com.android.tools.r8.utils.ComparatorUtils;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import java.util.ArrayList;
-import java.util.Comparator;
import java.util.List;
public class CfTryCatch {
@@ -49,12 +48,15 @@
return new CfTryCatch(start, end, guards, targets);
}
- public int compareTo(CfTryCatch other, CfCompareHelper helper) {
- return Comparator.comparing((CfTryCatch c) -> c.start, helper::compareLabels)
- .thenComparing(c -> c.end, helper::compareLabels)
- .thenComparing(c -> c.guards, ComparatorUtils.listComparator())
- .thenComparing(c -> c.targets, ComparatorUtils.listComparator(helper::compareLabels))
- .compare(this, other);
+ public void acceptCompareTo(CfTryCatch other, CompareToVisitor visitor, CfCompareHelper helper) {
+ visitor.visit(
+ this,
+ other,
+ spec ->
+ spec.withCustomItem(c -> c.start, helper.labelAcceptor())
+ .withCustomItem(c -> c.end, helper.labelAcceptor())
+ .withItemCollection(c -> c.guards)
+ .withCustomItemCollection(c -> c.targets, helper.labelAcceptor()));
}
public void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 2ad4409..ab72b04 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -54,6 +54,8 @@
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.Timing.TimingMerger;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ObjectArrays;
import it.unimi.dsi.fastutil.objects.Reference2LongMap;
@@ -229,7 +231,8 @@
}
public void write(ExecutorService executorService) throws IOException, ExecutionException {
- appView.appInfo().app().timing.begin("DexApplication.write");
+ Timing timing = appView.appInfo().app().timing;
+ timing.begin("DexApplication.write");
ProguardMapId proguardMapId = null;
if (proguardMapSupplier != null && options.proguardMapConsumer != null) {
proguardMapId = proguardMapSupplier.writeProguardMap();
@@ -248,19 +251,27 @@
}
}
try {
+ timing.begin("Insert Attribute Annotations");
// TODO(b/151313715): Move this to the writer threads.
insertAttributeAnnotations();
+ timing.end();
// Each DexCallSite must have its instruction offset set for sorting.
if (options.isGeneratingDex()) {
+ timing.begin("Set call-site contexts");
setCallSiteContexts(executorService);
+ timing.end();
}
// Generate the dex file contents.
List<Future<Boolean>> dexDataFutures = new ArrayList<>();
+ timing.begin("Distribute");
List<VirtualFile> virtualFiles = distribute(executorService);
+ timing.end();
if (options.encodeChecksums) {
+ timing.begin("Encode checksums");
encodeChecksums(virtualFiles);
+ timing.end();
}
assert markers == null
|| markers.isEmpty()
@@ -270,69 +281,25 @@
true);
// TODO(b/151313617): Sorting annotations mutates elements so run single threaded on main.
+ timing.begin("Sort Annotations");
SortAnnotations sortAnnotations = new SortAnnotations(namingLens);
appView.appInfo().classes().forEach((clazz) -> clazz.addDependencies(sortAnnotations));
+ timing.end();
- for (VirtualFile virtualFile : virtualFiles) {
- if (virtualFile.isEmpty()) {
- continue;
- }
- dexDataFutures.add(
- executorService.submit(
- () -> {
- ProgramConsumer consumer;
- ByteBufferProvider byteBufferProvider;
- if (programConsumer != null) {
- consumer = programConsumer;
- byteBufferProvider = programConsumer;
- } else if (virtualFile.getPrimaryClassDescriptor() != null) {
- consumer = options.getDexFilePerClassFileConsumer();
- byteBufferProvider = options.getDexFilePerClassFileConsumer();
- } else {
- if (virtualFile.getFeatureSplit() != null) {
- ProgramConsumer featureConsumer =
- virtualFile.getFeatureSplit().getProgramConsumer();
- assert featureConsumer instanceof DexIndexedConsumer;
- consumer = featureConsumer;
- byteBufferProvider = (DexIndexedConsumer) featureConsumer;
- } else {
- consumer = options.getDexIndexedConsumer();
- byteBufferProvider = options.getDexIndexedConsumer();
- }
- }
- ObjectToOffsetMapping objectMapping =
- virtualFile.computeMapping(appView, graphLens, namingLens, initClassLens);
- MethodToCodeObjectMapping codeMapping =
- rewriteCodeWithJumboStrings(
- objectMapping, virtualFile.classes(), appView.appInfo().app());
- ByteBufferResult result =
- writeDexFile(objectMapping, codeMapping, byteBufferProvider);
- ByteDataView data =
- new ByteDataView(
- result.buffer.array(), result.buffer.arrayOffset(), result.length);
- if (consumer instanceof DexFilePerClassFileConsumer) {
- ((DexFilePerClassFileConsumer) consumer)
- .accept(
- virtualFile.getPrimaryClassDescriptor(),
- data,
- virtualFile.getClassDescriptors(),
- options.reporter);
- } else {
- ((DexIndexedConsumer) consumer)
- .accept(
- virtualFile.getId(),
- data,
- virtualFile.getClassDescriptors(),
- options.reporter);
- }
- // Release use of the backing buffer now that accept has returned.
- data.invalidate();
- byteBufferProvider.releaseByteBuffer(result.buffer.asByteBuffer());
- return true;
- }));
- }
- // Wait for all files to be processed before moving on.
- ThreadUtils.awaitFutures(dexDataFutures);
+ TimingMerger merger =
+ timing.beginMerger("Write files", ThreadUtils.getNumberOfThreads(executorService));
+ Collection<Timing> timings =
+ ThreadUtils.processItemsWithResults(
+ virtualFiles,
+ virtualFile -> {
+ Timing fileTiming = Timing.create("VirtualFile " + virtualFile.getId(), options);
+ writeVirtualFile(virtualFile, fileTiming);
+ fileTiming.end();
+ return fileTiming;
+ },
+ executorService);
+ merger.add(timings);
+ merger.end();
// A consumer can manage the generated keep rules.
if (options.desugaredLibraryKeepRuleConsumer != null && !desugaredLibraryCodeToKeep.isNop()) {
assert !options.isDesugaredLibraryCompilation();
@@ -343,10 +310,64 @@
// Supply info to all additional resource consumers.
supplyAdditionalConsumers(appView.appInfo().app(), appView, graphLens, namingLens, options);
} finally {
- appView.appInfo().app().timing.end();
+ timing.end();
}
}
+ private void writeVirtualFile(VirtualFile virtualFile, Timing timing) {
+ if (virtualFile.isEmpty()) {
+ return;
+ }
+ ProgramConsumer consumer;
+ ByteBufferProvider byteBufferProvider;
+ if (programConsumer != null) {
+ consumer = programConsumer;
+ byteBufferProvider = programConsumer;
+ } else if (virtualFile.getPrimaryClassDescriptor() != null) {
+ consumer = options.getDexFilePerClassFileConsumer();
+ byteBufferProvider = options.getDexFilePerClassFileConsumer();
+ } else {
+ if (virtualFile.getFeatureSplit() != null) {
+ ProgramConsumer featureConsumer = virtualFile.getFeatureSplit().getProgramConsumer();
+ assert featureConsumer instanceof DexIndexedConsumer;
+ consumer = featureConsumer;
+ byteBufferProvider = (DexIndexedConsumer) featureConsumer;
+ } else {
+ consumer = options.getDexIndexedConsumer();
+ byteBufferProvider = options.getDexIndexedConsumer();
+ }
+ }
+ timing.begin("Compute object offset mapping");
+ ObjectToOffsetMapping objectMapping =
+ virtualFile.computeMapping(appView, graphLens, namingLens, initClassLens, timing);
+ timing.end();
+ timing.begin("Rewrite jumbo strings");
+ MethodToCodeObjectMapping codeMapping =
+ rewriteCodeWithJumboStrings(objectMapping, virtualFile.classes(), appView.appInfo().app());
+ timing.end();
+ timing.begin("Write bytes");
+ ByteBufferResult result = writeDexFile(objectMapping, codeMapping, byteBufferProvider);
+ ByteDataView data =
+ new ByteDataView(result.buffer.array(), result.buffer.arrayOffset(), result.length);
+ timing.end();
+ timing.begin("Pass bytes to consumer");
+ if (consumer instanceof DexFilePerClassFileConsumer) {
+ ((DexFilePerClassFileConsumer) consumer)
+ .accept(
+ virtualFile.getPrimaryClassDescriptor(),
+ data,
+ virtualFile.getClassDescriptors(),
+ options.reporter);
+ } else {
+ ((DexIndexedConsumer) consumer)
+ .accept(virtualFile.getId(), data, virtualFile.getClassDescriptors(), options.reporter);
+ }
+ timing.end();
+ // Release use of the backing buffer now that accept has returned.
+ data.invalidate();
+ byteBufferProvider.releaseByteBuffer(result.buffer.asByteBuffer());
+ }
+
public static void supplyAdditionalConsumers(
DexApplication application,
AppView<?> appView,
diff --git a/src/main/java/com/android/tools/r8/dex/DexReader.java b/src/main/java/com/android/tools/r8/dex/DexReader.java
index 7e3f1dc..18c0beb 100644
--- a/src/main/java/com/android/tools/r8/dex/DexReader.java
+++ b/src/main/java/com/android/tools/r8/dex/DexReader.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.DexVersion;
+import com.android.tools.r8.utils.StringUtils;
import java.io.IOException;
import java.nio.BufferUnderflowException;
import java.nio.ByteOrder;
@@ -47,8 +48,22 @@
}
int index = 0;
for (byte prefixByte : DEX_FILE_MAGIC_PREFIX) {
- if (buffer.get(index++) != prefixByte) {
- throw new CompilationError("Dex file has invalid header", origin);
+ byte actualByte = buffer.get(index++);
+ if (actualByte != prefixByte) {
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append(
+ "Dex file has invalid header, expected "
+ + prefixByte
+ + " got "
+ + actualByte
+ + ". Next bytes are ");
+ for (int i = 0; i < 10; i++) {
+ if (buffer.hasRemaining()) {
+ stringBuilder.append(StringUtils.hexString(buffer.get(), 2));
+ stringBuilder.append(",");
+ }
+ }
+ throw new CompilationError(stringBuilder.toString(), origin);
}
}
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index 108df1d..bce34ff 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -34,6 +34,7 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.SetUtils;
+import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterators;
import com.google.common.collect.Sets;
@@ -209,7 +210,11 @@
}
public ObjectToOffsetMapping computeMapping(
- AppView<?> appView, GraphLens graphLens, NamingLens namingLens, InitClassLens initClassLens) {
+ AppView<?> appView,
+ GraphLens graphLens,
+ NamingLens namingLens,
+ InitClassLens initClassLens,
+ Timing timing) {
assert transaction.isEmpty();
return new ObjectToOffsetMapping(
appView,
@@ -223,7 +228,8 @@
indexedItems.fields,
indexedItems.strings,
indexedItems.callSites,
- indexedItems.methodHandles);
+ indexedItems.methodHandles,
+ timing);
}
void addClass(DexProgramClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/graph/AccessFlags.java b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
index de254fc..910153a 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
@@ -4,12 +4,15 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.utils.structural.StructuralAccept;
+import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.function.BooleanSupplier;
/** Access flags common to classes, methods and fields. */
-public abstract class AccessFlags<T extends AccessFlags<T>> {
+public abstract class AccessFlags<T extends AccessFlags<T>> implements StructuralItem<T> {
protected static final int BASE_FLAGS
= Constants.ACC_PUBLIC
@@ -53,8 +56,18 @@
this.modifiedFlags = modifiedFlags;
}
+ protected static <T extends AccessFlags<T>> void specify(StructuralSpecification<T, ?> spec) {
+ spec.withInt(a -> a.originalFlags).withInt(a -> a.modifiedFlags);
+ }
+
+ @Override
+ public StructuralAccept<T> getStructuralAccept() {
+ return AccessFlags::specify;
+ }
+
public abstract T copy();
+ @Override
public abstract T self();
public int materialize() {
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 70db016..1e7ec9c 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -94,6 +94,10 @@
private Map<DexClass, DexValueString> sourceDebugExtensions = new IdentityHashMap<>();
+ // When input has been (partially) desugared these are the classes which has been library
+ // desugared. This information is populated in the IR converter.
+ private Set<DexProgramClass> alreadyLibraryDesugared = null;
+
private AppView(
T appInfo,
WholeProgramOptimizations wholeProgramOptimizations,
@@ -663,4 +667,17 @@
}
});
}
+
+ public void setAlreadyLibraryDesugared(Set<DexProgramClass> alreadyLibraryDesugared) {
+ assert this.alreadyLibraryDesugared == null;
+ this.alreadyLibraryDesugared = alreadyLibraryDesugared;
+ }
+
+ public boolean isAlreadyLibraryDesugared(DexProgramClass clazz) {
+ if (!options().desugarSpecificOptions().allowAllDesugaredInput) {
+ return false;
+ }
+ assert alreadyLibraryDesugared != null;
+ return alreadyLibraryDesugared.contains(clazz);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index c17ff09..6d3cedc 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -41,19 +41,20 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
+import com.android.tools.r8.utils.structural.StructuralAccept;
+import com.android.tools.r8.utils.structural.StructuralItem;
import com.google.common.base.Strings;
import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
-import it.unimi.dsi.fastutil.objects.Reference2IntMap;
-import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
-import java.util.Comparator;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.ListIterator;
@@ -63,7 +64,7 @@
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
-public class CfCode extends Code implements Comparable<CfCode> {
+public class CfCode extends Code implements StructuralItem<CfCode> {
public enum StackMapStatus {
NOT_VERIFIED,
@@ -111,12 +112,16 @@
return end;
}
- public int compareTo(LocalVariableInfo other, CfCompareHelper helper) {
- return Comparator.comparingInt(LocalVariableInfo::getIndex)
- .thenComparing(LocalVariableInfo::getStart, helper::compareLabels)
- .thenComparing(LocalVariableInfo::getEnd, helper::compareLabels)
- .thenComparing(LocalVariableInfo::getLocal)
- .compare(this, other);
+ public void acceptCompareTo(
+ LocalVariableInfo other, CompareToVisitor visitor, CfCompareHelper helper) {
+ visitor.visit(
+ this,
+ other,
+ spec ->
+ spec.withInt(LocalVariableInfo::getIndex)
+ .withCustomItem(LocalVariableInfo::getStart, helper.labelAcceptor())
+ .withCustomItem(LocalVariableInfo::getEnd, helper.labelAcceptor())
+ .withItem(LocalVariableInfo::getLocal));
}
@Override
@@ -153,6 +158,16 @@
this.localVariables = localVariables;
}
+ @Override
+ public CfCode self() {
+ return this;
+ }
+
+ @Override
+ public StructuralAccept<CfCode> getStructuralAccept() {
+ throw new Unreachable();
+ }
+
public DexType getOriginalHolder() {
return originalHolder;
}
@@ -229,38 +244,25 @@
}
@Override
- public int compareTo(CfCode o) {
- // Fast path by checking sizes.
- int sizeDiff =
- Comparator.comparingInt((CfCode c) -> c.instructions.size())
- .thenComparingInt(c -> c.tryCatchRanges.size())
- .thenComparingInt(c -> localVariables.size())
- .compare(this, o);
- if (sizeDiff != 0) {
- return sizeDiff;
- }
- // In the slow case, compute label maps and compare collections in full.
- Reference2IntMap<CfLabel> labels1 = getLabelOrdering(instructions);
- Reference2IntMap<CfLabel> labels2 = getLabelOrdering(o.instructions);
- int labelDiff = labels1.size() - labels2.size();
- if (labelDiff != 0) {
- return labelDiff;
- }
- CfCompareHelper helper = new CfCompareHelper(labels1, labels2);
- return Comparator.comparing((CfCode c) -> c.instructions, helper.instructionComparator())
- .thenComparing(c -> c.tryCatchRanges, helper.tryCatchRangesComparator())
- .thenComparing(c -> c.localVariables, helper.localVariablesComparator())
- .compare(this, o);
+ public void acceptHashing(HashingVisitor visitor) {
+ // Rather than hash the entire content, hash the sizes and each instruction "type" which
+ // should provide a fast yet reasonably distinct key.
+ visitor.visitInt(instructions.size());
+ visitor.visitInt(tryCatchRanges.size());
+ visitor.visitInt(localVariables.size());
+ instructions.forEach(i -> visitor.visitInt(i.getCompareToId()));
}
- private static Reference2IntMap<CfLabel> getLabelOrdering(List<CfInstruction> instructions) {
- Reference2IntMap<CfLabel> ordering = new Reference2IntOpenHashMap<>();
- for (CfInstruction instruction : instructions) {
- if (instruction.isLabel()) {
- ordering.put(instruction.asLabel(), ordering.size());
- }
- }
- return ordering;
+ @Override
+ public void acceptCompareTo(CfCode other, CompareToVisitor visitor) {
+ CfCompareHelper helper = new CfCompareHelper(this, other);
+ visitor.visit(
+ this,
+ other,
+ spec ->
+ spec.withCustomItemCollection(c -> c.instructions, helper.instructionAcceptor())
+ .withCustomItemCollection(c -> c.tryCatchRanges, helper.tryCatchRangeAcceptor())
+ .withCustomItemCollection(c -> c.localVariables, helper.localVariableAcceptor()));
}
private boolean shouldAddParameterNames(DexEncodedMethod method, AppView<?> appView) {
@@ -758,7 +760,7 @@
origin, appView.graphLens().getOriginalMethodSignature(method.method), appView),
appView);
}
- DexType context = appView.graphLens().lookupType(method.holder());
+ DexType context = appView.graphLens().lookupType(method.getHolderType());
DexType returnType = appView.graphLens().lookupType(method.method.getReturnType());
RewrittenPrototypeDescription rewrittenDescription = RewrittenPrototypeDescription.none();
if (applyProtoTypeChanges) {
diff --git a/src/main/java/com/android/tools/r8/graph/CfCompareHelper.java b/src/main/java/com/android/tools/r8/graph/CfCompareHelper.java
index b05814e..0203b0c 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCompareHelper.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCompareHelper.java
@@ -6,10 +6,13 @@
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfLabel;
import com.android.tools.r8.cf.code.CfTryCatch;
-import com.android.tools.r8.utils.ComparatorUtils;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.graph.CfCode.LocalVariableInfo;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
+import com.android.tools.r8.utils.structural.StructuralAcceptor;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
-import java.util.Comparator;
-import java.util.List;
+import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
import org.objectweb.asm.Opcodes;
public class CfCompareHelper {
@@ -42,35 +45,101 @@
}
// Helper to signal that the concrete instruction is uniquely determined by its ID/opcode.
- public static int compareIdUniquelyDeterminesEquality(
+ public static void compareIdUniquelyDeterminesEquality(
CfInstruction instruction1, CfInstruction instruction2) {
assert instruction1.getClass() == instruction2.getClass();
assert instruction1.getCompareToId() == instruction2.getCompareToId();
assert instruction1.toString().equals(instruction2.toString());
- return 0;
}
- private final Reference2IntMap<CfLabel> labels1;
- private final Reference2IntMap<CfLabel> labels2;
-
- public CfCompareHelper(Reference2IntMap<CfLabel> labels1, Reference2IntMap<CfLabel> labels2) {
- this.labels1 = labels1;
- this.labels2 = labels2;
+ private static Reference2IntMap<CfLabel> getLabelOrdering(CfCode code) {
+ Reference2IntMap<CfLabel> ordering = new Reference2IntOpenHashMap<>();
+ for (CfInstruction instruction : code.getInstructions()) {
+ if (instruction.isLabel()) {
+ ordering.put(instruction.asLabel(), ordering.size());
+ }
+ }
+ return ordering;
}
- public int compareLabels(CfLabel label1, CfLabel label2) {
- return labels1.getInt(label1) - labels2.getInt(label2);
+ private final CfCode code1;
+ private final CfCode code2;
+ private StructuralAcceptor<CfLabel> lazyLabelAcceptor = null;
+
+ public CfCompareHelper(CfCode code1, CfCode code2) {
+ this.code1 = code1;
+ this.code2 = code2;
}
- public Comparator<List<CfInstruction>> instructionComparator() {
- return ComparatorUtils.listComparator((x, y) -> x.compareTo(y, this));
+ public void compareLabels(CfLabel label1, CfLabel label2, CompareToVisitor visitor) {
+ labelAcceptor().acceptCompareTo(label1, label2, visitor);
}
- public Comparator<List<CfTryCatch>> tryCatchRangesComparator() {
- return ComparatorUtils.listComparator((x, y) -> x.compareTo(y, this));
+ public StructuralAcceptor<CfLabel> labelAcceptor() {
+ if (lazyLabelAcceptor == null) {
+ lazyLabelAcceptor =
+ new StructuralAcceptor<CfLabel>() {
+ private final Reference2IntMap<CfLabel> labels1 = getLabelOrdering(code1);
+ private final Reference2IntMap<CfLabel> labels2 = getLabelOrdering(code2);
+
+ @Override
+ public void acceptCompareTo(CfLabel item1, CfLabel item2, CompareToVisitor visitor) {
+ visitor.visitInt(labels1.getInt(item1), labels2.getInt(item2));
+ }
+
+ @Override
+ public void acceptHashing(CfLabel item, HashingVisitor visitor) {
+ throw new Unimplemented();
+ }
+ };
+ }
+ return lazyLabelAcceptor;
}
- public Comparator<List<CfCode.LocalVariableInfo>> localVariablesComparator() {
- return ComparatorUtils.listComparator((x, y) -> x.compareTo(y, this));
+ public StructuralAcceptor<CfInstruction> instructionAcceptor() {
+ CfCompareHelper helper = this;
+ return new StructuralAcceptor<CfInstruction>() {
+ @Override
+ public void acceptCompareTo(
+ CfInstruction item1, CfInstruction item2, CompareToVisitor visitor) {
+ item1.acceptCompareTo(item2, visitor, helper);
+ }
+
+ @Override
+ public void acceptHashing(CfInstruction item, HashingVisitor visitor) {
+ throw new Unimplemented();
+ }
+ };
+ }
+
+ public StructuralAcceptor<CfTryCatch> tryCatchRangeAcceptor() {
+ CfCompareHelper helper = this;
+ return new StructuralAcceptor<CfTryCatch>() {
+ @Override
+ public void acceptCompareTo(CfTryCatch item1, CfTryCatch item2, CompareToVisitor visitor) {
+ item1.acceptCompareTo(item2, visitor, helper);
+ }
+
+ @Override
+ public void acceptHashing(CfTryCatch item, HashingVisitor visitor) {
+ throw new Unimplemented();
+ }
+ };
+ }
+
+ public StructuralAcceptor<LocalVariableInfo> localVariableAcceptor() {
+ CfCompareHelper helper = this;
+ return new StructuralAcceptor<LocalVariableInfo>() {
+ @Override
+ public void acceptCompareTo(
+ LocalVariableInfo item1, LocalVariableInfo item2, CompareToVisitor visitor) {
+ item1.acceptCompareTo(item2, visitor, helper);
+ }
+
+ @Override
+ public void acceptHashing(LocalVariableInfo item, HashingVisitor visitor) {
+ throw new Unimplemented();
+ }
+ };
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java b/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java
index 333b8a7..56027f2 100644
--- a/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java
@@ -5,13 +5,15 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.structural.StructuralAccept;
+import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap.Entry;
import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
-import java.util.Comparator;
-public class DebugLocalInfo implements Comparable<DebugLocalInfo> {
+public class DebugLocalInfo implements StructuralItem<DebugLocalInfo> {
public enum PrintLevel {
NONE,
@@ -25,6 +27,12 @@
public final DexType type;
public final DexString signature;
+ private static void specify(StructuralSpecification<DebugLocalInfo, ?> spec) {
+ spec.withItem(info -> info.name)
+ .withItem(info -> info.type)
+ .withNullableItem(info -> info.signature);
+ }
+
public DebugLocalInfo(DexString name, DexType type, DexString signature) {
this.name = name;
this.type = type;
@@ -32,11 +40,13 @@
}
@Override
- public int compareTo(DebugLocalInfo other) {
- return Comparator.comparing((DebugLocalInfo info) -> info.name)
- .thenComparing(info -> info.type)
- .thenComparing(info -> info.signature, Comparator.nullsFirst(DexString::compareTo))
- .compare(this, other);
+ public DebugLocalInfo self() {
+ return this;
+ }
+
+ @Override
+ public StructuralAccept<DebugLocalInfo> getStructuralAccept() {
+ return DebugLocalInfo::specify;
}
public static boolean localsInfoMapsEqual(
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
index 05010c8..5f223f8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
@@ -16,6 +16,9 @@
import com.android.tools.r8.ir.desugar.CovariantReturnTypeAnnotationTransformer;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Pair;
+import com.android.tools.r8.utils.structural.StructuralAccept;
+import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -23,7 +26,7 @@
import java.util.TreeSet;
import java.util.function.Function;
-public class DexAnnotation extends DexItem {
+public class DexAnnotation extends DexItem implements StructuralItem<DexAnnotation> {
public static final DexAnnotation[] EMPTY_ARRAY = {};
public static final int VISIBILITY_BUILD = 0x00;
public static final int VISIBILITY_RUNTIME = 0x01;
@@ -31,11 +34,25 @@
public final int visibility;
public final DexEncodedAnnotation annotation;
+ private static void specify(StructuralSpecification<DexAnnotation, ?> spec) {
+ spec.withInt(a -> a.visibility).withItem(a -> a.annotation);
+ }
+
public DexAnnotation(int visibility, DexEncodedAnnotation annotation) {
this.visibility = visibility;
this.annotation = annotation;
}
+ @Override
+ public DexAnnotation self() {
+ return this;
+ }
+
+ @Override
+ public StructuralAccept<DexAnnotation> getStructuralAccept() {
+ return DexAnnotation::specify;
+ }
+
public DexType getAnnotationType() {
return annotation.type;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationElement.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationElement.java
index 237838f..fc764ff 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationElement.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationElement.java
@@ -5,18 +5,35 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.utils.structural.StructuralAccept;
+import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
-public class DexAnnotationElement extends DexItem {
+public class DexAnnotationElement extends DexItem implements StructuralItem<DexAnnotationElement> {
public static final DexAnnotationElement[] EMPTY_ARRAY = {};
public final DexString name;
public final DexValue value;
+ private static void specify(StructuralSpecification<DexAnnotationElement, ?> spec) {
+ spec.withItem(e -> e.name).withItem(e -> e.value);
+ }
+
public DexAnnotationElement(DexString name, DexValue value) {
this.name = name;
this.value = value;
}
+ @Override
+ public DexAnnotationElement self() {
+ return this;
+ }
+
+ @Override
+ public StructuralAccept<DexAnnotationElement> getStructuralAccept() {
+ return DexAnnotationElement::specify;
+ }
+
public DexValue getValue() {
return value;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
index ba71338..86188d6 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
@@ -9,6 +9,9 @@
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.ArrayUtils;
+import com.android.tools.r8.utils.structural.StructuralAccept;
+import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
import com.google.common.collect.Sets;
import java.util.Arrays;
import java.util.List;
@@ -18,7 +21,8 @@
import java.util.function.Predicate;
import java.util.stream.Stream;
-public class DexAnnotationSet extends CachedHashValueDexItem {
+public class DexAnnotationSet extends CachedHashValueDexItem
+ implements StructuralItem<DexAnnotationSet> {
public static final DexAnnotationSet[] EMPTY_ARRAY = {};
@@ -29,10 +33,24 @@
public final DexAnnotation[] annotations;
private int sorted = UNSORTED;
+ private static void specify(StructuralSpecification<DexAnnotationSet, ?> spec) {
+ spec.withItemArray(a -> a.annotations);
+ }
+
public DexAnnotationSet(DexAnnotation[] annotations) {
this.annotations = annotations;
}
+ @Override
+ public DexAnnotationSet self() {
+ return this;
+ }
+
+ @Override
+ public StructuralAccept<DexAnnotationSet> getStructuralAccept() {
+ return DexAnnotationSet::specify;
+ }
+
public static DexType findDuplicateEntryType(DexAnnotation[] annotations) {
return findDuplicateEntryType(Arrays.asList(annotations));
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexCallSite.java b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
index b38e016..5d521bc 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCallSite.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
@@ -10,6 +10,9 @@
import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
import com.android.tools.r8.graph.DexValue.DexValueMethodType;
import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.android.tools.r8.utils.structural.StructuralAccept;
+import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
import com.google.common.io.BaseEncoding;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -22,7 +25,7 @@
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.InvokeDynamicInsnNode;
-public final class DexCallSite extends IndexedDexItem implements Comparable<DexCallSite> {
+public final class DexCallSite extends IndexedDexItem implements StructuralItem<DexCallSite> {
public final DexString methodName;
public final DexProto methodProto;
@@ -30,6 +33,7 @@
public final DexMethodHandle bootstrapMethod;
public final List<DexValue> bootstrapArgs;
+ // Lazy computed encoding derived from the above immutable fields.
private DexEncodedArray encodedArray = null;
// Only used for sorting for deterministic output. This is the method and the instruction
@@ -37,6 +41,20 @@
private DexMethod method;
private int instructionOffset = -1;
+ private static void specify(StructuralSpecification<DexCallSite, ?> spec) {
+ spec
+ // Use the possibly absent "context" info as the major key for sorting.
+ // TODO(b/171867022): Investigate if this is needed now that a call-site can be sorted based
+ // on its content directly.
+ .withNullableItem(c -> c.method)
+ .withInt(c -> c.instructionOffset)
+ // Actual call-site content.
+ .withItem(c -> c.methodName)
+ .withItem(c -> c.methodProto)
+ .withItem(c -> c.bootstrapMethod)
+ .withItemCollection(c -> c.bootstrapArgs);
+ }
+
DexCallSite(
DexString methodName,
DexProto methodProto,
@@ -84,6 +102,16 @@
return application.getCallSite(name, desc, bootstrapMethod, bootstrapArgs);
}
+ @Override
+ public DexCallSite self() {
+ return this;
+ }
+
+ @Override
+ public StructuralAccept<DexCallSite> getStructuralAccept() {
+ return DexCallSite::specify;
+ }
+
public void setContext(DexMethod method, int instructionOffset) {
assert method != null;
assert instructionOffset >= 0;
@@ -155,17 +183,6 @@
return new HashBuilder().build();
}
- @Override
- public int compareTo(DexCallSite other) {
- assert method != null && other.method != null;
- int methodCompare = method.compareTo(other.method);
- if (methodCompare != 0) {
- return methodCompare;
- }
- assert (instructionOffset - other.instructionOffset) != 0;
- return instructionOffset - other.instructionOffset;
- }
-
private final class HashBuilder {
private ByteArrayOutputStream bytes;
private ObjectOutputStream out;
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 5215ada..be3a802 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -130,6 +130,10 @@
return interfaces;
}
+ public void setInterfaces(DexTypeList interfaces) {
+ this.interfaces = interfaces;
+ }
+
public DexString getSourceFile() {
return sourceFile;
}
@@ -378,7 +382,7 @@
}
private boolean verifyCorrectnessOfFieldHolder(DexEncodedField field) {
- assert field.holder() == type
+ assert field.getHolderType() == type
: "Expected field `"
+ field.field.toSourceString()
+ "` to have holder `"
@@ -495,7 +499,8 @@
}
private boolean isSignaturePolymorphicMethod(DexEncodedMethod method, DexItemFactory factory) {
- assert method.holder() == factory.methodHandleType || method.holder() == factory.varHandleType;
+ assert method.getHolderType() == factory.methodHandleType
+ || method.getHolderType() == factory.varHandleType;
return method.accessFlags.isVarargs()
&& method.accessFlags.isNative()
&& method.method.proto.parameters.size() == 1
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java
index ddb14ec..fbd5e7a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java
@@ -16,7 +16,7 @@
public DexClassAndMember(DexClass holder, D definition) {
assert holder != null;
assert definition != null;
- assert holder.type == definition.holder();
+ assert holder.type == definition.getHolderType();
this.holder = holder;
this.definition = definition;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
index e73c267..32743f0 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
@@ -73,7 +73,7 @@
int argumentRegister = code.registerSize - code.incomingRegisterSize;
if (!method.accessFlags.isStatic()) {
DexString name = factory.thisName;
- DexType type = method.holder();
+ DexType type = method.getHolderType();
startArgument(argumentRegister, name, type);
argumentRegister += ValueType.fromDexType(type).requiredRegisters();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java
index 051dd92..bcefcd2 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java
@@ -6,11 +6,14 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.utils.ArrayUtils;
+import com.android.tools.r8.utils.structural.StructuralAccept;
+import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
import java.util.Arrays;
import java.util.function.Consumer;
import java.util.function.Function;
-public class DexEncodedAnnotation extends DexItem {
+public class DexEncodedAnnotation extends DexItem implements StructuralItem<DexEncodedAnnotation> {
private static final int UNSORTED = 0;
@@ -19,11 +22,25 @@
private int sorted = UNSORTED;
+ private static void specify(StructuralSpecification<DexEncodedAnnotation, ?> spec) {
+ spec.withItem(a -> a.type).withItemArray(a -> a.elements);
+ }
+
public DexEncodedAnnotation(DexType type, DexAnnotationElement[] elements) {
this.type = type;
this.elements = elements;
}
+ @Override
+ public DexEncodedAnnotation self() {
+ return this;
+ }
+
+ @Override
+ public StructuralAccept<DexEncodedAnnotation> getStructuralAccept() {
+ return DexEncodedAnnotation::specify;
+ }
+
public void collectIndexedItems(IndexedItemCollection indexedItems) {
type.collectIndexedItems(indexedItems);
for (DexAnnotationElement element : elements) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index 16879f0..133ebe5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -20,8 +20,12 @@
import com.android.tools.r8.ir.optimize.info.MutableFieldOptimizationInfo;
import com.android.tools.r8.kotlin.KotlinFieldLevelInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.structural.StructuralAccept;
+import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
-public class DexEncodedField extends DexEncodedMember<DexEncodedField, DexField> {
+public class DexEncodedField extends DexEncodedMember<DexEncodedField, DexField>
+ implements StructuralItem<DexEncodedField> {
public static final DexEncodedField[] EMPTY_ARRAY = {};
public final DexField field;
@@ -34,6 +38,16 @@
private FieldOptimizationInfo optimizationInfo = DefaultFieldOptimizationInfo.getInstance();
private KotlinFieldLevelInfo kotlinMemberInfo = NO_KOTLIN_INFO;
+ private static void specify(StructuralSpecification<DexEncodedField, ?> spec) {
+ spec.withItem(f -> f.field)
+ .withItem(f -> f.accessFlags)
+ .withNullableItem(f -> f.staticValue)
+ .withBool(f -> f.deprecated)
+ // TODO(b/171867022): The generic signature should be part of the definition.
+ .withAssert(f -> f.genericSignature.hasNoSignature());
+ // TODO(b/171867022): Should the optimization info and member info be part of the definition?
+ }
+
public DexEncodedField(
DexField field,
FieldAccessFlags accessFlags,
@@ -60,6 +74,16 @@
this(field, accessFlags, genericSignature, annotations, staticValue, false);
}
+ @Override
+ public StructuralAccept<DexEncodedField> getStructuralAccept() {
+ return DexEncodedField::specify;
+ }
+
+ @Override
+ public DexEncodedField self() {
+ return this;
+ }
+
public DexType type() {
return field.type;
}
@@ -150,7 +174,7 @@
}
public ProgramField asProgramField(DexDefinitionSupplier definitions) {
- assert holder().isClassType();
+ assert getHolderType().isClassType();
DexProgramClass clazz = asProgramClassOrNull(definitions.definitionForHolder(field));
if (clazz != null) {
return new ProgramField(clazz, this);
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
index dd69340..bca8a6b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
@@ -12,11 +12,15 @@
super(annotations);
}
- public DexType holder() {
- return getReference().holder;
+ public abstract KotlinMemberLevelInfo getKotlinMemberInfo();
+
+ public DexType getHolderType() {
+ return getReference().getHolderType();
}
- public abstract KotlinMemberLevelInfo getKotlinMemberInfo();
+ public DexString getName() {
+ return getReference().getName();
+ }
@Override
public abstract R getReference();
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 e84e3e8..6fbb596 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -75,7 +75,9 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.Pair;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
import com.android.tools.r8.utils.structural.CompareToVisitorWithTypeEquivalence;
+import com.android.tools.r8.utils.structural.HashingVisitor;
import com.android.tools.r8.utils.structural.HashingVisitorWithTypeEquivalence;
import com.android.tools.r8.utils.structural.Ordered;
import com.android.tools.r8.utils.structural.RepresentativeMap;
@@ -325,17 +327,19 @@
// Visitor specifying the structure of the method with respect to its "synthetic" content.
// TODO(b/171867022): Generalize this so that it determines any method in full.
private static void syntheticSpecify(StructuralSpecification<DexEncodedMethod, ?> spec) {
- spec.withAssert(m1 -> m1.annotations().isEmpty())
- .withAssert(m1 -> m1.parameterAnnotationsList.isEmpty())
+ spec.withItem(m -> m.method)
+ .withItem(m -> m.accessFlags)
+ .withItem(m -> m.annotations())
+ .withItem(m -> m.parameterAnnotationsList)
+ .withNullableItem(m -> m.classFileVersion)
+ .withBool(m -> m.d8R8Synthesized)
+ // TODO(b/171867022): Make signatures structural and include it in the definition.
+ .withAssert(m -> m.genericSignature.hasNoSignature())
.withAssert(m1 -> m1.code != null)
- .withItem(DexEncodedMethod::getHolderType)
- .withItem(DexEncodedMethod::getName)
- .withInt(m -> m.getAccessFlags().getAsCfAccessFlags())
- .withItem(DexEncodedMethod::proto)
.withCustomItem(
DexEncodedMethod::getCode,
- (c1, c2, v) -> v.visit(c1, c2, DexEncodedMethod::compareCodeObject),
- (c, h) -> h.visit(c, DexEncodedMethod::hashCodeObject));
+ DexEncodedMethod::compareCodeObject,
+ DexEncodedMethod::hashCodeObject);
}
public void hashSyntheticContent(Hasher hasher, RepresentativeMap map) {
@@ -354,37 +358,28 @@
this, other, map, DexEncodedMethod::syntheticSpecify);
}
- private static int compareCodeObject(Code code1, Code code2) {
+ private static void compareCodeObject(Code code1, Code code2, CompareToVisitor visitor) {
if (code1.isCfCode() && code2.isCfCode()) {
- return code1.asCfCode().compareTo(code2.asCfCode());
- }
- if (code1.isDexCode() && code2.isDexCode()) {
- return code1.asDexCode().compareTo(code2.asDexCode());
- }
- throw new Unreachable(
- "Unexpected attempt to compare incompatible synthetic objects: " + code1 + " and " + code2);
- }
-
- private static void hashCodeObject(Code code, Hasher hasher) {
- // TODO(b/158159959): Implement a more precise hashing on code objects.
- if (code.isCfCode()) {
- CfCode cfCode = code.asCfCode();
- hasher.putInt(cfCode.getInstructions().size());
- for (CfInstruction instruction : cfCode.getInstructions()) {
- hasher.putInt(instruction.getClass().hashCode());
- }
+ code1.asCfCode().acceptCompareTo(code2.asCfCode(), visitor);
+ } else if (code1.isDexCode() && code2.isDexCode()) {
+ visitor.visit(code1.asDexCode(), code2.asDexCode(), DexCode::compareTo);
} else {
- assert code.isDexCode();
- hasher.putInt(code.hashCode());
+ throw new Unreachable(
+ "Unexpected attempt to compare incompatible synthetic objects: "
+ + code1
+ + " and "
+ + code2);
}
}
- public DexType getHolderType() {
- return getReference().holder;
- }
-
- public DexString getName() {
- return getReference().name;
+ private static void hashCodeObject(Code code, HashingVisitor visitor) {
+ if (code.isCfCode()) {
+ code.asCfCode().acceptHashing(visitor);
+ } else {
+ // TODO(b/158159959): Implement a more precise hashing on code objects.
+ assert code.isDexCode();
+ visitor.visitInt(code.hashCode());
+ }
}
public DexProto getProto() {
diff --git a/src/main/java/com/android/tools/r8/graph/DexField.java b/src/main/java/com/android/tools/r8/graph/DexField.java
index 5f95369..dbb2d08 100644
--- a/src/main/java/com/android/tools/r8/graph/DexField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexField.java
@@ -42,14 +42,6 @@
return DexField::accept;
}
- public DexType getHolderType() {
- return holder;
- }
-
- public DexString getName() {
- return name;
- }
-
public DexType getType() {
return type;
}
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 eee5f4b..dac5b16 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -30,6 +30,7 @@
import com.android.tools.r8.kotlin.Kotlin;
import com.android.tools.r8.utils.ArrayUtils;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.LRUCacheTable;
import com.google.common.base.Strings;
import com.google.common.collect.BiMap;
@@ -37,6 +38,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
@@ -46,9 +48,12 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
@@ -1828,6 +1833,19 @@
return createGloballyFreshMemberString(baseName, null);
}
+ public DexType createFreshTypeName(DexType type, Predicate<DexType> isFresh) {
+ return createFreshTypeName(type, isFresh, 0);
+ }
+
+ public DexType createFreshTypeName(DexType type, Predicate<DexType> isFresh, int index) {
+ while (true) {
+ DexType newType = type.addSuffixId(index++, this);
+ if (isFresh.test(newType)) {
+ return newType;
+ }
+ }
+ }
+
/**
* Tries to find a method name for insertion into the class {@code target} of the form
* baseName$holder$n, where {@code baseName} and {@code holder} are supplied by the user, and
@@ -1909,22 +1927,45 @@
}
public DexMethod createInstanceInitializerWithFreshProto(
+ DexMethod method, List<DexType> extraTypes, Predicate<DexMethod> isFresh) {
+ assert method.isInstanceInitializer(this);
+ return createInstanceInitializerWithFreshProto(
+ method.proto,
+ extraTypes,
+ proto -> Optional.of(method.withProto(proto, this)).filter(isFresh));
+ }
+
+ public DexMethod createInstanceInitializerWithFreshProto(
DexMethod method, DexType extraType, Predicate<DexMethod> isFresh) {
assert method.isInstanceInitializer(this);
return createInstanceInitializerWithFreshProto(
method.proto,
- extraType,
- proto -> Optional.of(createMethod(method.holder, proto, method.name)).filter(isFresh));
+ ImmutableList.of(extraType),
+ proto -> Optional.of(method.withProto(proto, this)).filter(isFresh));
}
private DexMethod createInstanceInitializerWithFreshProto(
- DexProto proto, DexType extraType, Function<DexProto, Optional<DexMethod>> isFresh) {
+ DexProto proto, List<DexType> extraTypes, Function<DexProto, Optional<DexMethod>> isFresh) {
+ assert !extraTypes.isEmpty();
+
+ Queue<Iterable<DexProto>> tryProtos = new LinkedList<>();
+ Iterator<DexProto> current = IterableUtils.singleton(proto).iterator();
+
+ int count = 0;
while (true) {
- Optional<DexMethod> object = isFresh.apply(proto);
+ assert count++ < 100;
+ if (!current.hasNext()) {
+ assert !tryProtos.isEmpty();
+ current = tryProtos.remove().iterator();
+ assert current.hasNext();
+ }
+ DexProto tryProto = current.next();
+ Optional<DexMethod> object = isFresh.apply(tryProto);
if (object.isPresent()) {
return object.get();
}
- proto = appendTypeToProto(proto, extraType);
+ tryProtos.add(
+ Iterables.transform(extraTypes, extraType -> appendTypeToProto(tryProto, extraType)));
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexMember.java b/src/main/java/com/android/tools/r8/graph/DexMember.java
index 84a1953..790fd33 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMember.java
@@ -31,6 +31,14 @@
return holder;
}
+ public DexType getHolderType() {
+ return holder;
+ }
+
+ public DexString getName() {
+ return name;
+ }
+
@Override
public boolean isDexMember() {
return true;
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 249e77e..97f9b6d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -50,14 +50,6 @@
visitor.visitDexMethod(this, other);
}
- public DexType getHolderType() {
- return holder;
- }
-
- public DexString getName() {
- return name;
- }
-
public DexType getParameter(int index) {
return proto.getParameter(index);
}
@@ -276,4 +268,8 @@
public DexMethod withName(DexString name, DexItemFactory dexItemFactory) {
return dexItemFactory.createMethod(holder, proto, name);
}
+
+ public DexMethod withProto(DexProto proto, DexItemFactory dexItemFactory) {
+ return dexItemFactory.createMethod(holder, proto, name);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexReference.java b/src/main/java/com/android/tools/r8/graph/DexReference.java
index d32c7ba..8a59614 100644
--- a/src/main/java/com/android/tools/r8/graph/DexReference.java
+++ b/src/main/java/com/android/tools/r8/graph/DexReference.java
@@ -63,7 +63,7 @@
return null;
}
- private int referenceTypeOrder() {
+ public int referenceTypeOrder() {
if (isDexType()) {
return 1;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexString.java b/src/main/java/com/android/tools/r8/graph/DexString.java
index 7a50fe1..264a7aa 100644
--- a/src/main/java/com/android/tools/r8/graph/DexString.java
+++ b/src/main/java/com/android/tools/r8/graph/DexString.java
@@ -46,9 +46,15 @@
throw new Unreachable();
}
+ /** DexString is a leaf item so we directly define its compareTo which avoids overhead. */
+ @Override
+ public int compareTo(DexString other) {
+ return internalCompareTo(other);
+ }
+
@Override
public void acceptCompareTo(DexString other, CompareToVisitor visitor) {
- visitor.visitDexString(this, other, DexString::internalCompareTo);
+ visitor.visitDexString(this, other);
}
@Override
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 39aea8c..0b13d80 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -444,6 +444,14 @@
return dexItemFactory.createType(newDescriptorString);
}
+ public DexType addSuffixId(int index, DexItemFactory dexItemFactory) {
+ if (index == 0) {
+ return this;
+ }
+ assert index > 0;
+ return addSuffix("$" + index, dexItemFactory);
+ }
+
public DexType toArrayType(int dimensions, DexItemFactory dexItemFactory) {
return dexItemFactory.createType(descriptor.toArrayDescriptor(dimensions, dexItemFactory));
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexTypeList.java b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
index a641039..595c5dd 100644
--- a/src/main/java/com/android/tools/r8/graph/DexTypeList.java
+++ b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
@@ -3,18 +3,20 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import static com.android.tools.r8.utils.PredicateUtils.not;
+
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
-import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.utils.ArrayUtils;
-import com.android.tools.r8.utils.structural.CompareToVisitor;
-import com.android.tools.r8.utils.structural.HashingVisitor;
import com.android.tools.r8.utils.structural.StructuralAccept;
import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
import com.google.common.collect.Iterators;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Iterator;
import java.util.function.Consumer;
+import java.util.function.Predicate;
import java.util.stream.Stream;
public class DexTypeList extends DexItem implements Iterable<DexType>, StructuralItem<DexTypeList> {
@@ -23,6 +25,10 @@
public final DexType[] values;
+ private static void specify(StructuralSpecification<DexTypeList, ?> spec) {
+ spec.withItemArray(ts -> ts.values);
+ }
+
public static DexTypeList empty() {
return theEmptyTypeList;
}
@@ -36,10 +42,28 @@
this.values = values;
}
- @Override
- public StructuralAccept<DexTypeList> getStructuralAccept() {
- // Structural accept is never accessed as all accept methods are defined directly.
- throw new Unreachable();
+ public DexTypeList(Collection<DexType> values) {
+ this(values.toArray(DexType.EMPTY_ARRAY));
+ }
+
+ public static DexTypeList create(DexType[] values) {
+ return values.length == 0 ? DexTypeList.empty() : new DexTypeList(values);
+ }
+
+ public static DexTypeList create(Collection<DexType> values) {
+ return values.isEmpty() ? DexTypeList.empty() : new DexTypeList(values);
+ }
+
+ public DexTypeList keepIf(Predicate<DexType> predicate) {
+ DexType[] filtered = ArrayUtils.filter(DexType[].class, values, predicate);
+ if (filtered != values) {
+ return DexTypeList.create(filtered);
+ }
+ return this;
+ }
+
+ public DexTypeList removeIf(Predicate<DexType> predicate) {
+ return keepIf(not(predicate));
}
@Override
@@ -48,13 +72,8 @@
}
@Override
- public void acceptCompareTo(DexTypeList other, CompareToVisitor visitor) {
- visitor.visitDexTypeList(this, other);
- }
-
- @Override
- public void acceptHashing(HashingVisitor visitor) {
- visitor.visitDexTypeList(this);
+ public StructuralAccept<DexTypeList> getStructuralAccept() {
+ return DexTypeList::specify;
}
public boolean contains(DexType type) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexValue.java b/src/main/java/com/android/tools/r8/graph/DexValue.java
index 273c69a..a54daf8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexValue.java
+++ b/src/main/java/com/android/tools/r8/graph/DexValue.java
@@ -23,12 +23,16 @@
import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.EncodedValueUtils;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
+import com.android.tools.r8.utils.structural.StructuralAccept;
+import com.android.tools.r8.utils.structural.StructuralItem;
import java.util.Arrays;
import java.util.function.Consumer;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Type;
-public abstract class DexValue extends DexItem {
+public abstract class DexValue extends DexItem implements StructuralItem<DexValue> {
public enum DexValueKind {
BYTE(0x00),
@@ -104,6 +108,41 @@
}
}
+ @Override
+ public DexValue self() {
+ return this;
+ }
+
+ @Override
+ public final StructuralAccept<DexValue> getStructuralAccept() {
+ // DexValue is not generic at its base type (and can't as we use it as a polymorphic value),
+ // so each concrete value must implement polymorphic accept functions. This base class
+ // implements (most of) the polymorphic checks and concrete types implement the internal
+ // variants for that type.
+ throw new Unreachable();
+ }
+
+ @Override
+ public final void acceptCompareTo(DexValue other, CompareToVisitor visitor) {
+ // Order first on 'kind', only equal kinds then forward to the 'kind' specific internal compare.
+ if (getValueKind() != other.getValueKind()) {
+ visitor.visitInt(getValueKind().toByte(), other.getValueKind().toByte());
+ } else {
+ internalAcceptCompareTo(other, visitor);
+ }
+ }
+
+ @Override
+ public final void acceptHashing(HashingVisitor visitor) {
+ // Always hash the 'kind' which ensures that raw values of different type are distinct.
+ visitor.visitInt(getValueKind().toByte());
+ internalAcceptHashing(visitor);
+ }
+
+ abstract void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor);
+
+ abstract void internalAcceptHashing(HashingVisitor visitor);
+
public static final DexValue[] EMPTY_ARRAY = {};
public abstract DexValueKind getValueKind();
@@ -438,6 +477,16 @@
return value == DEFAULT.value ? DEFAULT : new DexValueByte(value);
}
+ @Override
+ void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+ visitor.visitInt(value, other.asDexValueByte().value);
+ }
+
+ @Override
+ void internalAcceptHashing(HashingVisitor visitor) {
+ visitor.visitInt(value);
+ }
+
public byte getValue() {
return value;
}
@@ -521,6 +570,16 @@
return value == DEFAULT.value ? DEFAULT : new DexValueShort(value);
}
+ @Override
+ void internalAcceptHashing(HashingVisitor visitor) {
+ visitor.visitInt(value);
+ }
+
+ @Override
+ void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+ visitor.visitInt(value, other.asDexValueShort().getValue());
+ }
+
public short getValue() {
return value;
}
@@ -603,6 +662,16 @@
return value == DEFAULT.value ? DEFAULT : new DexValueChar(value);
}
+ @Override
+ void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+ visitor.visitInt(value, other.asDexValueChar().value);
+ }
+
+ @Override
+ void internalAcceptHashing(HashingVisitor visitor) {
+ visitor.visitInt(value);
+ }
+
public char getValue() {
return value;
}
@@ -689,6 +758,16 @@
return value == DEFAULT.value ? DEFAULT : new DexValueInt(value);
}
+ @Override
+ void internalAcceptHashing(HashingVisitor visitor) {
+ visitor.visitInt(value);
+ }
+
+ @Override
+ void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+ visitor.visitInt(value, other.asDexValueInt().value);
+ }
+
public int getValue() {
return value;
}
@@ -771,6 +850,16 @@
return value == DEFAULT.value ? DEFAULT : new DexValueLong(value);
}
+ @Override
+ void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+ visitor.visitLong(value, other.asDexValueLong().value);
+ }
+
+ @Override
+ void internalAcceptHashing(HashingVisitor visitor) {
+ visitor.visitLong(value);
+ }
+
public long getValue() {
return value;
}
@@ -853,6 +942,16 @@
return Float.compare(value, DEFAULT.value) == 0 ? DEFAULT : new DexValueFloat(value);
}
+ @Override
+ void internalAcceptHashing(HashingVisitor visitor) {
+ visitor.visitFloat(value);
+ }
+
+ @Override
+ void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+ visitor.visitFloat(value, other.asDexValueFloat().value);
+ }
+
public float getValue() {
return value;
}
@@ -941,6 +1040,16 @@
return Double.compare(value, DEFAULT.value) == 0 ? DEFAULT : new DexValueDouble(value);
}
+ @Override
+ void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+ visitor.visitDouble(value, other.asDexValueDouble().value);
+ }
+
+ @Override
+ void internalAcceptHashing(HashingVisitor visitor) {
+ visitor.visitDouble(value);
+ }
+
public double getValue() {
return value;
}
@@ -1087,6 +1196,18 @@
}
@Override
+ void internalAcceptHashing(HashingVisitor visitor) {
+ value.acceptHashing(visitor);
+ }
+
+ @Override
+ void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+ if (DexItemBasedValueString.compareAndCheckValueStrings(this, other, visitor)) {
+ value.acceptCompareTo(other.asDexValueString().value, visitor);
+ }
+ }
+
+ @Override
public void collectIndexedItems(IndexedItemCollection indexedItems) {
value.collectIndexedItems(indexedItems);
}
@@ -1143,6 +1264,20 @@
public static class DexItemBasedValueString extends NestedDexValue<DexReference> {
+ // Helper to ensure a consistent order on DexValueString and DexItemBasedValueString which are
+ // both defined to have kind 'string'.
+ static boolean compareAndCheckValueStrings(DexValue v1, DexValue v2, CompareToVisitor visitor) {
+ assert v1.getValueKind() == DexValueKind.STRING;
+ assert v2.getValueKind() == DexValueKind.STRING;
+ int order1 = v1.isDexItemBasedValueString() ? 1 : 0;
+ int order2 = v2.isDexItemBasedValueString() ? 1 : 0;
+ boolean equal = order1 == order2;
+ if (!equal) {
+ visitor.visitInt(order1, order2);
+ }
+ return equal;
+ }
+
private final NameComputationInfo<?> nameComputationInfo;
public DexItemBasedValueString(DexReference value, NameComputationInfo<?> nameComputationInfo) {
@@ -1151,6 +1286,18 @@
}
@Override
+ void internalAcceptHashing(HashingVisitor visitor) {
+ visitor.visitDexReference(value);
+ }
+
+ @Override
+ void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+ if (compareAndCheckValueStrings(this, other, visitor)) {
+ visitor.visitDexReference(value, other.asDexItemBasedValueString().value);
+ }
+ }
+
+ @Override
public void collectIndexedItems(IndexedItemCollection indexedItems) {
value.collectIndexedItems(indexedItems);
}
@@ -1221,6 +1368,16 @@
}
@Override
+ void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+ value.acceptCompareTo(other.asDexValueType().value, visitor);
+ }
+
+ @Override
+ void internalAcceptHashing(HashingVisitor visitor) {
+ value.acceptHashing(visitor);
+ }
+
+ @Override
public DexValueKind getValueKind() {
return DexValueKind.TYPE;
}
@@ -1253,6 +1410,16 @@
}
@Override
+ void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+ value.acceptCompareTo(other.asDexValueField().value, visitor);
+ }
+
+ @Override
+ void internalAcceptHashing(HashingVisitor visitor) {
+ value.acceptHashing(visitor);
+ }
+
+ @Override
public DexValueKind getValueKind() {
return DexValueKind.FIELD;
}
@@ -1285,6 +1452,16 @@
}
@Override
+ void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+ value.acceptCompareTo(other.asDexValueMethod().value, visitor);
+ }
+
+ @Override
+ void internalAcceptHashing(HashingVisitor visitor) {
+ value.acceptHashing(visitor);
+ }
+
+ @Override
public DexValueKind getValueKind() {
return DexValueKind.METHOD;
}
@@ -1317,6 +1494,16 @@
}
@Override
+ void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+ value.acceptCompareTo(other.asDexValueEnum().value, visitor);
+ }
+
+ @Override
+ void internalAcceptHashing(HashingVisitor visitor) {
+ value.acceptHashing(visitor);
+ }
+
+ @Override
public DexValueKind getValueKind() {
return DexValueKind.ENUM;
}
@@ -1349,6 +1536,16 @@
}
@Override
+ void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+ value.acceptCompareTo(other.asDexValueMethodType().value, visitor);
+ }
+
+ @Override
+ void internalAcceptHashing(HashingVisitor visitor) {
+ value.acceptHashing(visitor);
+ }
+
+ @Override
public boolean isDexValueMethodType() {
return true;
}
@@ -1382,6 +1579,16 @@
this.values = values;
}
+ @Override
+ void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+ visitor.visitItemArray(values, other.asDexValueArray().values);
+ }
+
+ @Override
+ void internalAcceptHashing(HashingVisitor visitor) {
+ visitor.visitItemArray(values);
+ }
+
public void forEachElement(Consumer<DexValue> consumer) {
for (DexValue value : values) {
consumer.accept(value);
@@ -1481,6 +1688,16 @@
this.value = value;
}
+ @Override
+ void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+ value.acceptCompareTo(other.asDexValueAnnotation().value, visitor);
+ }
+
+ @Override
+ void internalAcceptHashing(HashingVisitor visitor) {
+ value.acceptHashing(visitor);
+ }
+
public DexEncodedAnnotation getValue() {
return value;
}
@@ -1567,6 +1784,17 @@
private DexValueNull() {
}
+ @Override
+ void internalAcceptHashing(HashingVisitor visitor) {
+ assert this == NULL;
+ }
+
+ @Override
+ void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+ assert this == NULL;
+ assert other == NULL;
+ }
+
public Object getValue() {
return null;
}
@@ -1653,6 +1881,16 @@
return value ? TRUE : FALSE;
}
+ @Override
+ void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+ visitor.visitBool(value, other.asDexValueBoolean().value);
+ }
+
+ @Override
+ void internalAcceptHashing(HashingVisitor visitor) {
+ visitor.visitBool(value);
+ }
+
public boolean getValue() {
return value;
}
@@ -1729,6 +1967,16 @@
}
@Override
+ void internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) {
+ value.acceptCompareTo(other.asDexValueMethodHandle().value, visitor);
+ }
+
+ @Override
+ void internalAcceptHashing(HashingVisitor visitor) {
+ value.acceptHashing(visitor);
+ }
+
+ @Override
public boolean isDexValueMethodHandle() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java b/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
index b3f9a6f..4483fc5 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
@@ -60,7 +60,7 @@
SuccessfulFieldResolutionResult(
DexClass initialResolutionHolder, DexClass resolvedHolder, DexEncodedField resolvedField) {
- assert resolvedHolder.type == resolvedField.holder();
+ assert resolvedHolder.type == resolvedField.getHolderType();
this.initialResolutionHolder = initialResolutionHolder;
this.resolvedHolder = resolvedHolder;
this.resolvedField = resolvedField;
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index 398ed87..5a10844 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -14,6 +14,8 @@
import com.android.tools.r8.utils.Action;
import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.SetUtils;
+import com.android.tools.r8.utils.collections.BidirectionalManyToManyRepresentativeMap;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
@@ -229,7 +231,8 @@
protected final Map<DexField, DexField> fieldMap = new IdentityHashMap<>();
protected final BiMap<DexField, DexField> originalFieldSignatures = HashBiMap.create();
- protected final BiMap<DexMethod, DexMethod> originalMethodSignatures = HashBiMap.create();
+ protected final BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures =
+ new BidirectionalOneToOneHashMap<>();
public void map(DexType from, DexType to) {
if (from == to) {
@@ -965,7 +968,8 @@
// Maps that store the original signature of fields and methods that have been affected, for
// example, by vertical class merging. Needed to generate a correct Proguard map in the end.
protected final BiMap<DexField, DexField> originalFieldSignatures;
- protected BiMap<DexMethod, DexMethod> originalMethodSignatures;
+ protected BidirectionalManyToManyRepresentativeMap<DexMethod, DexMethod>
+ originalMethodSignatures;
// Overrides this if the sub type needs to be a nested lens while it doesn't have any mappings
// at all, e.g., publicizer lens that changes invocation type only.
@@ -978,7 +982,7 @@
Map<DexMethod, DexMethod> methodMap,
Map<DexField, DexField> fieldMap,
BiMap<DexField, DexField> originalFieldSignatures,
- BiMap<DexMethod, DexMethod> originalMethodSignatures,
+ BidirectionalManyToManyRepresentativeMap<DexMethod, DexMethod> originalMethodSignatures,
GraphLens previousLens,
DexItemFactory dexItemFactory) {
super(dexItemFactory, previousLens);
@@ -1138,15 +1142,11 @@
@Override
protected DexMethod internalGetPreviousMethodSignature(DexMethod method) {
- return originalMethodSignatures != null
- ? originalMethodSignatures.getOrDefault(method, method)
- : method;
+ return originalMethodSignatures.getRepresentativeValueOrDefault(method, method);
}
protected DexMethod internalGetNextMethodSignature(DexMethod method) {
- return originalMethodSignatures != null
- ? originalMethodSignatures.inverse().getOrDefault(method, method)
- : method;
+ return originalMethodSignatures.getRepresentativeKeyOrDefault(method, method);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/MethodCollection.java b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
index 3200163..9339630 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
@@ -342,7 +342,7 @@
}
private boolean verifyCorrectnessOfMethodHolder(DexEncodedMethod method) {
- assert method.holder() == holder.type
+ assert method.getHolderType() == holder.type
: "Expected method `"
+ method.method.toSourceString()
+ "` to have holder `"
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
index b5ce56a..61d9439 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
@@ -7,6 +7,9 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.structural.CompareToVisitorWithStringTable;
+import com.android.tools.r8.utils.structural.CompareToVisitorWithTypeTable;
import it.unimi.dsi.fastutil.objects.Reference2IntLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntMap.Entry;
@@ -17,6 +20,7 @@
import java.util.Comparator;
import java.util.List;
import java.util.function.Consumer;
+import java.util.function.ToIntFunction;
import java.util.stream.Collectors;
public class ObjectToOffsetMapping {
@@ -53,7 +57,8 @@
Collection<DexField> fields,
Collection<DexString> strings,
Collection<DexCallSite> callSites,
- Collection<DexMethodHandle> methodHandles) {
+ Collection<DexMethodHandle> methodHandles,
+ Timing timing) {
assert appView != null;
assert graphLens != null;
assert classes != null;
@@ -69,18 +74,42 @@
this.namingLens = namingLens;
this.initClassLens = initClassLens;
this.lensCodeRewriter = new LensCodeRewriterUtils(appView);
+ timing.begin("Sort strings");
+ this.strings = createSortedMap(strings, DexString::compareTo, this::setFirstJumboString);
+ timing.end();
+ timing.begin("Sort types");
+ this.types = createSortedMap(types, compareWithStringTable(), this::failOnOverflow);
+ timing.end();
+ timing.begin("Sort classes");
this.classes = sortClasses(appView.appInfo(), classes, namingLens);
- this.protos = createSortedMap(protos, compare(namingLens), this::failOnOverflow);
- this.types = createSortedMap(types, compare(namingLens), this::failOnOverflow);
- this.methods = createSortedMap(methods, compare(namingLens), this::failOnOverflow);
- this.fields = createSortedMap(fields, compare(namingLens), this::failOnOverflow);
- this.strings = createSortedMap(strings, compare(namingLens), this::setFirstJumboString);
+ timing.end();
+ timing.begin("Sort protos");
+ this.protos = createSortedMap(protos, compareWithTypeTable(), this::failOnOverflow);
+ timing.end();
+ timing.begin("Sort methods");
+ this.methods = createSortedMap(methods, compareWithTypeTable(), this::failOnOverflow);
+ timing.end();
+ timing.begin("Sort fields");
+ this.fields = createSortedMap(fields, compareWithTypeTable(), this::failOnOverflow);
+ timing.end();
+ timing.begin("Sort call-sites");
this.callSites = createSortedMap(callSites, DexCallSite::compareTo, this::failOnOverflow);
- this.methodHandles = createSortedMap(methodHandles, compare(namingLens), this::failOnOverflow);
+ timing.end();
+ timing.begin("Sort method handles");
+ this.methodHandles =
+ createSortedMap(methodHandles, compareWithTypeTable(), this::failOnOverflow);
+ timing.end();
}
- private static <T extends NamingLensComparable<T>> Comparator<T> compare(NamingLens namingLens) {
- return (a, b) -> a.compareToWithNamingLens(b, namingLens);
+ private <T extends NamingLensComparable<T>> Comparator<T> compareWithStringTable() {
+ return (a, b) ->
+ CompareToVisitorWithStringTable.run(
+ a, b, namingLens, (ToIntFunction<DexString>) strings::getInt);
+ }
+
+ private <T extends NamingLensComparable<T>> Comparator<T> compareWithTypeTable() {
+ return (a, b) ->
+ CompareToVisitorWithTypeTable.run(a, b, namingLens, strings::getInt, types::getInt);
}
private void setFirstJumboString(DexString string) {
@@ -95,7 +124,7 @@
private <T> Reference2IntLinkedOpenHashMap<T> createSortedMap(
Collection<T> items, Comparator<T> comparator, Consumer<T> onUInt16Overflow) {
if (items.isEmpty()) {
- return null;
+ return new Reference2IntLinkedOpenHashMap<>();
}
// Sort items and compute the offset mapping for each in sorted order.
ArrayList<T> sorted = new ArrayList<>(items);
diff --git a/src/main/java/com/android/tools/r8/graph/ParameterAnnotationsList.java b/src/main/java/com/android/tools/r8/graph/ParameterAnnotationsList.java
index 4b21c2b..fb642d6 100644
--- a/src/main/java/com/android/tools/r8/graph/ParameterAnnotationsList.java
+++ b/src/main/java/com/android/tools/r8/graph/ParameterAnnotationsList.java
@@ -6,6 +6,9 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.utils.ArrayUtils;
+import com.android.tools.r8.utils.structural.StructuralAccept;
+import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
import java.util.Arrays;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -35,7 +38,8 @@
* ParameterAnnotationsList#isMissing(int)} accessor is used to determine whether a given parameter
* is missing in the ParameterAnnotations attribute.
*/
-public class ParameterAnnotationsList extends DexItem {
+public class ParameterAnnotationsList extends DexItem
+ implements StructuralItem<ParameterAnnotationsList> {
private static final ParameterAnnotationsList EMPTY_PARAMETER_ANNOTATIONS_LIST =
new ParameterAnnotationsList();
@@ -43,6 +47,10 @@
private final DexAnnotationSet[] values;
private final int missingParameterAnnotations;
+ private static void specify(StructuralSpecification<ParameterAnnotationsList, ?> spec) {
+ spec.withItemArray(a -> a.values).withInt(a -> a.missingParameterAnnotations);
+ }
+
public static ParameterAnnotationsList empty() {
return EMPTY_PARAMETER_ANNOTATIONS_LIST;
}
@@ -62,6 +70,16 @@
this.missingParameterAnnotations = missingParameterAnnotations;
}
+ @Override
+ public ParameterAnnotationsList self() {
+ return this;
+ }
+
+ @Override
+ public StructuralAccept<ParameterAnnotationsList> getStructuralAccept() {
+ return ParameterAnnotationsList::specify;
+ }
+
public int getAnnotableParameterCount() {
return size();
}
@@ -77,6 +95,7 @@
return true;
}
if (other instanceof ParameterAnnotationsList) {
+ // TODO(b/172999904): Why does equals not include missingParameterAnnotations?
return Arrays.equals(values, ((ParameterAnnotationsList) other).values);
}
return false;
diff --git a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
index 4db00fc..09533b0 100644
--- a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
@@ -136,12 +136,12 @@
assert initialResolutionHolder != null;
assert resolvedHolder != null;
assert resolvedMethod != null;
- assert resolvedHolder.type == resolvedMethod.holder();
+ assert resolvedHolder.type == resolvedMethod.getHolderType();
this.resolvedHolder = resolvedHolder;
this.resolvedMethod = resolvedMethod;
this.initialResolutionHolder = initialResolutionHolder;
assert !resolvedMethod.isPrivateMethod()
- || initialResolutionHolder.type == resolvedMethod.holder();
+ || initialResolutionHolder.type == resolvedMethod.getHolderType();
}
@Override
@@ -370,7 +370,7 @@
// It appears as if this check is also in place for non-initializer methods too.
// See NestInvokeSpecialMethodAccessWithIntermediateTest.
if ((target.isInstanceInitializer() || target.isPrivateMethod())
- && target.holder() != symbolicReference.type) {
+ && target.getHolderType() != symbolicReference.type) {
return null;
}
// Runtime exceptions:
@@ -610,7 +610,7 @@
}
if (candidate == null || candidate == DexEncodedMethod.SENTINEL) {
// We cannot find a target above the resolved method.
- if (current.type == overrideTarget.holder()) {
+ if (current.type == overrideTarget.getHolderType()) {
return null;
}
current = current.superType == null ? null : appInfo.definitionFor(current.superType);
@@ -689,7 +689,7 @@
}
// For package private methods, a valid override has to be inside the package.
assert resolvedMethod.accessFlags.isPackagePrivate();
- return resolvedMethod.holder().isSamePackage(candidate.holder());
+ return resolvedMethod.getHolderType().isSamePackage(candidate.getHolderType());
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
index 8a44fd0..c8fc0a8 100644
--- a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
+++ b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
@@ -396,6 +396,12 @@
return rewrittenReturnInfo != null;
}
+ public boolean requiresRewritingAtCallSite() {
+ return hasRewrittenReturnInfo()
+ || numberOfExtraParameters() > 0
+ || argumentInfoCollection.numberOfRemovedArguments() > 0;
+ }
+
public RewrittenTypeInfo getRewrittenReturnInfo() {
return rewrittenReturnInfo;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
index 7cf3029..26ed21c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
@@ -25,9 +25,10 @@
private final Builder lensBuilder;
public ClassInstanceFieldsMerger(
- HorizontalClassMergerGraphLens.Builder lensBuilder, DexProgramClass target) {
+ HorizontalClassMergerGraphLens.Builder lensBuilder, MergeGroup group) {
this.lensBuilder = lensBuilder;
- target
+ group
+ .getTarget()
.instanceFields()
.forEach(field -> fieldMappings.computeIfAbsent(field, ignore -> new ArrayList<>()));
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index f75f969..04b4b68 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -11,12 +11,12 @@
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.FieldAccessFlags;
import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
import com.android.tools.r8.graph.ProgramMethod;
@@ -25,26 +25,28 @@
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
- * The class merger is responsible for moving methods from {@link ClassMerger#toMergeGroup} into the
- * class {@link ClassMerger#target}. While performing merging, this class tracks which methods have
- * been moved, as well as which fields have been remapped in the {@link ClassMerger#lensBuilder}.
+ * The class merger is responsible for moving methods from the sources in {@link ClassMerger#group}
+ * into the target of {@link ClassMerger#group}. While performing merging, this class tracks which
+ * methods have been moved, as well as which fields have been remapped in the {@link
+ * ClassMerger#lensBuilder}.
*/
public class ClassMerger {
+
public static final String CLASS_ID_FIELD_NAME = "$r8$classId";
- private final DexProgramClass target;
- private final List<DexProgramClass> toMergeGroup;
+ private final MergeGroup group;
private final DexItemFactory dexItemFactory;
private final HorizontalClassMergerGraphLens.Builder lensBuilder;
private final HorizontallyMergedClasses.Builder mergedClassesBuilder;
@@ -56,58 +58,41 @@
private final ClassInstanceFieldsMerger classInstanceFieldsMerger;
private final Collection<VirtualMethodMerger> virtualMethodMergers;
private final Collection<ConstructorMerger> constructorMergers;
- private final DexField classIdField;
private ClassMerger(
AppView<AppInfoWithLiveness> appView,
HorizontalClassMergerGraphLens.Builder lensBuilder,
HorizontallyMergedClasses.Builder mergedClassesBuilder,
FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder,
- DexProgramClass target,
- List<DexProgramClass> toMergeGroup,
- DexField classIdField,
+ MergeGroup group,
Collection<VirtualMethodMerger> virtualMethodMergers,
Collection<ConstructorMerger> constructorMergers) {
this.lensBuilder = lensBuilder;
this.mergedClassesBuilder = mergedClassesBuilder;
this.fieldAccessChangesBuilder = fieldAccessChangesBuilder;
- this.target = target;
- this.toMergeGroup = toMergeGroup;
- this.classIdField = classIdField;
+ this.group = group;
this.virtualMethodMergers = virtualMethodMergers;
this.constructorMergers = constructorMergers;
this.dexItemFactory = appView.dexItemFactory();
- this.classStaticFieldsMerger = new ClassStaticFieldsMerger(appView, lensBuilder, target);
- this.classInstanceFieldsMerger = new ClassInstanceFieldsMerger(lensBuilder, target);
+ this.classStaticFieldsMerger = new ClassStaticFieldsMerger(appView, lensBuilder, group);
+ this.classInstanceFieldsMerger = new ClassInstanceFieldsMerger(lensBuilder, group);
buildClassIdentifierMap();
}
- /** Returns an iterable over all classes that should be merged into the target class. */
- public Iterable<DexProgramClass> getToMergeClasses() {
- return toMergeGroup;
- }
-
- /**
- * Returns an iterable over both the target class as well as all classes that should be merged
- * into the target class.
- */
- public Iterable<DexProgramClass> getClasses() {
- return Iterables.concat(Collections.singleton(target), getToMergeClasses());
+ MergeGroup getGroup() {
+ return group;
}
void buildClassIdentifierMap() {
- classIdentifiers.put(target.type, 0);
- for (DexProgramClass toMerge : toMergeGroup) {
- classIdentifiers.put(toMerge.type, classIdentifiers.size());
- }
+ classIdentifiers.put(group.getTarget().getType(), 0);
+ group.forEachSource(clazz -> classIdentifiers.put(clazz.getType(), classIdentifiers.size()));
}
void mergeDirectMethods(SyntheticArgumentClass syntheticArgumentClass) {
- mergeDirectMethods(target);
- toMergeGroup.forEach(this::mergeDirectMethods);
-
+ mergeDirectMethods(group.getTarget());
+ group.forEachSource(this::mergeDirectMethods);
mergeConstructors(syntheticArgumentClass);
}
@@ -118,7 +103,8 @@
assert !definition.isClassInitializer();
if (!definition.isInstanceInitializer()) {
- DexMethod newMethod = method.getReference().withHolder(target.type, dexItemFactory);
+ DexMethod newMethod =
+ method.getReference().withHolder(group.getTarget().getType(), dexItemFactory);
if (!classMethodsBuilder.isFresh(newMethod)) {
newMethod = renameDirectMethod(method);
}
@@ -144,7 +130,7 @@
method.getDefinition().method.name.toSourceString(),
method.getHolderType(),
method.getDefinition().proto(),
- target.type,
+ group.getTarget().getType(),
classMethodsBuilder::isFresh);
}
@@ -164,38 +150,47 @@
merger ->
merger.merge(
classMethodsBuilder, lensBuilder, fieldAccessChangesBuilder, classIdentifiers));
- toMergeGroup.forEach(clazz -> clazz.getMethodCollection().clearVirtualMethods());
+ group.forEachSource(clazz -> clazz.getMethodCollection().clearVirtualMethods());
}
void appendClassIdField() {
DexEncodedField encodedField =
new DexEncodedField(
- classIdField,
+ group.getClassIdField(),
FieldAccessFlags.fromSharedAccessFlags(
Constants.ACC_PUBLIC + Constants.ACC_FINAL + Constants.ACC_SYNTHETIC),
FieldTypeSignature.noSignature(),
DexAnnotationSet.empty(),
null);
- target.appendInstanceField(encodedField);
+ group.getTarget().appendInstanceField(encodedField);
}
void mergeStaticFields() {
- toMergeGroup.forEach(classStaticFieldsMerger::addFields);
- classStaticFieldsMerger.merge(target);
- toMergeGroup.forEach(clazz -> clazz.setStaticFields(null));
+ group.forEachSource(classStaticFieldsMerger::addFields);
+ classStaticFieldsMerger.merge(group.getTarget());
+ group.forEachSource(clazz -> clazz.setStaticFields(null));
}
void fixAccessFlags() {
- if (Iterables.any(toMergeGroup, not(DexProgramClass::isAbstract))) {
- target.getAccessFlags().demoteFromAbstract();
+ if (Iterables.any(group.getSources(), not(DexProgramClass::isAbstract))) {
+ group.getTarget().getAccessFlags().demoteFromAbstract();
}
- if (Iterables.any(toMergeGroup, not(DexProgramClass::isFinal))) {
- target.getAccessFlags().demoteFromFinal();
+ if (Iterables.any(group.getSources(), not(DexProgramClass::isFinal))) {
+ group.getTarget().getAccessFlags().demoteFromFinal();
+ }
+ }
+
+ private void mergeInterfaces() {
+ DexTypeList previousInterfaces = group.getTarget().getInterfaces();
+ Set<DexType> interfaces = Sets.newLinkedHashSet(previousInterfaces);
+ group.forEachSource(clazz -> Iterables.addAll(interfaces, clazz.getInterfaces()));
+ if (interfaces.size() > previousInterfaces.size()) {
+ group.getTarget().setInterfaces(new DexTypeList(interfaces));
}
}
void mergeInstanceFields() {
- toMergeGroup.forEach(
+ group.forEachSource(
clazz -> {
classInstanceFieldsMerger.addFields(clazz);
clazz.setInstanceFields(null);
@@ -207,65 +202,71 @@
fixAccessFlags();
appendClassIdField();
+ mergeInterfaces();
+
mergeVirtualMethods();
mergeDirectMethods(syntheticArgumentClass);
- classMethodsBuilder.setClassMethods(target);
+ classMethodsBuilder.setClassMethods(group.getTarget());
mergeStaticFields();
mergeInstanceFields();
- mergedClassesBuilder.addMergeGroup(target, toMergeGroup);
+ mergedClassesBuilder.addMergeGroup(group);
}
public static class Builder {
private final AppView<AppInfoWithLiveness> appView;
- private final DexProgramClass target;
- private final List<DexProgramClass> toMergeGroup = new ArrayList<>();
+ private final MergeGroup group;
private final Map<DexProto, ConstructorMerger.Builder> constructorMergerBuilders =
new LinkedHashMap<>();
+ private final List<ConstructorMerger.Builder> unmergedConstructorBuilders = new ArrayList<>();
private final Map<Wrapper<DexMethod>, VirtualMethodMerger.Builder> virtualMethodMergerBuilders =
new LinkedHashMap<>();
- public Builder(AppView<AppInfoWithLiveness> appView, DexProgramClass target) {
+ public Builder(AppView<AppInfoWithLiveness> appView, MergeGroup group) {
this.appView = appView;
- this.target = target;
+ this.group = group;
+ }
+
+ private Builder setup() {
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ DexProgramClass target = group.iterator().next();
+ // TODO(b/165498187): ensure the name for the field is fresh
+ group.setClassIdField(
+ dexItemFactory.createField(
+ target.getType(), dexItemFactory.intType, CLASS_ID_FIELD_NAME));
+ group.setTarget(target);
setupForMethodMerging(target);
- }
-
- public Builder mergeClass(DexProgramClass toMerge) {
- setupForMethodMerging(toMerge);
- toMergeGroup.add(toMerge);
+ group.forEachSource(this::setupForMethodMerging);
return this;
}
- public Builder addClassesToMerge(List<DexProgramClass> toMerge) {
- toMerge.forEach(this::mergeClass);
- return this;
- }
-
- void setupForMethodMerging(DexProgramClass toMerge) {
+ private void setupForMethodMerging(DexProgramClass toMerge) {
toMerge.forEachProgramDirectMethod(
method -> {
DexEncodedMethod definition = method.getDefinition();
assert !definition.isClassInitializer();
-
if (definition.isInstanceInitializer()) {
addConstructor(method);
}
});
-
toMerge.forEachProgramVirtualMethod(this::addVirtualMethod);
}
- void addConstructor(ProgramMethod method) {
+ private void addConstructor(ProgramMethod method) {
assert method.getDefinition().isInstanceInitializer();
- constructorMergerBuilders
- .computeIfAbsent(
- method.getDefinition().getProto(), ignore -> new ConstructorMerger.Builder(appView))
- .add(method.getDefinition());
+ if (appView.options().enableHorizontalClassMergingConstructorMerging) {
+ constructorMergerBuilders
+ .computeIfAbsent(
+ method.getDefinition().getProto(), ignore -> new ConstructorMerger.Builder(appView))
+ .add(method.getDefinition());
+ } else {
+ unmergedConstructorBuilders.add(
+ new ConstructorMerger.Builder(appView).add(method.getDefinition()));
+ }
}
- void addVirtualMethod(ProgramMethod method) {
+ private void addVirtualMethod(ProgramMethod method) {
assert method.getDefinition().isNonPrivateVirtualMethod();
virtualMethodMergerBuilders
.computeIfAbsent(
@@ -274,21 +275,21 @@
.add(method);
}
+ private Collection<ConstructorMerger.Builder> getConstructorMergerBuilders() {
+ return appView.options().enableHorizontalClassMergingConstructorMerging
+ ? constructorMergerBuilders.values()
+ : unmergedConstructorBuilders;
+ }
+
public ClassMerger build(
- AppView<AppInfoWithLiveness> appView,
HorizontallyMergedClasses.Builder mergedClassesBuilder,
HorizontalClassMergerGraphLens.Builder lensBuilder,
FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder) {
- DexItemFactory dexItemFactory = appView.dexItemFactory();
- // TODO(b/165498187): ensure the name for the field is fresh
- DexField classIdField =
- dexItemFactory.createField(target.type, dexItemFactory.intType, CLASS_ID_FIELD_NAME);
-
+ setup();
List<VirtualMethodMerger> virtualMethodMergers =
new ArrayList<>(virtualMethodMergerBuilders.size());
for (VirtualMethodMerger.Builder builder : virtualMethodMergerBuilders.values()) {
- virtualMethodMergers.add(
- builder.build(appView, target, classIdField, toMergeGroup.size() + 1));
+ virtualMethodMergers.add(builder.build(appView, group));
}
// Try and merge the functions with the most arguments first, to avoid using synthetic
// arguments if possible.
@@ -296,8 +297,8 @@
List<ConstructorMerger> constructorMergers =
new ArrayList<>(constructorMergerBuilders.size());
- for (ConstructorMerger.Builder builder : constructorMergerBuilders.values()) {
- constructorMergers.addAll(builder.build(appView, target, classIdField));
+ for (ConstructorMerger.Builder builder : getConstructorMergerBuilders()) {
+ constructorMergers.addAll(builder.build(appView, group));
}
// Try and merge the functions with the most arguments first, to avoid using synthetic
@@ -310,9 +311,7 @@
lensBuilder,
mergedClassesBuilder,
fieldAccessChangesBuilder,
- target,
- toMergeGroup,
- classIdField,
+ group,
virtualMethodMergers,
constructorMergers);
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMethodsBuilder.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMethodsBuilder.java
index 2516cad..13c01eb 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMethodsBuilder.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMethodsBuilder.java
@@ -34,9 +34,9 @@
}
public void setClassMethods(DexProgramClass clazz) {
- assert virtualMethods.stream().allMatch(method -> method.holder() == clazz.type);
+ assert virtualMethods.stream().allMatch(method -> method.getHolderType() == clazz.type);
assert virtualMethods.stream().allMatch(method -> method.belongsToVirtualPool());
- assert directMethods.stream().allMatch(method -> method.holder() == clazz.type);
+ assert directMethods.stream().allMatch(method -> method.getHolderType() == clazz.type);
assert directMethods.stream().allMatch(method -> method.belongsToDirectPool());
clazz.setVirtualMethods(virtualMethods);
clazz.setDirectMethods(directMethods);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassStaticFieldsMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassStaticFieldsMerger.java
index e287090..7bc85d1 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassStaticFieldsMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassStaticFieldsMerger.java
@@ -15,21 +15,20 @@
public class ClassStaticFieldsMerger {
private final Builder lensBuilder;
- private final DexProgramClass target;
+ private final MergeGroup group;
private final Map<DexField, DexEncodedField> targetFields = new LinkedHashMap<>();
private final DexItemFactory dexItemFactory;
- private final AppView<?> appView;
public ClassStaticFieldsMerger(
- AppView<?> appView,
- HorizontalClassMergerGraphLens.Builder lensBuilder,
- DexProgramClass target) {
- this.appView = appView;
+ AppView<?> appView, HorizontalClassMergerGraphLens.Builder lensBuilder, MergeGroup group) {
this.lensBuilder = lensBuilder;
- this.target = target;
+ this.group = group;
// Add mappings for all target fields.
- target.staticFields().forEach(field -> targetFields.put(field.getReference(), field));
+ group
+ .getTarget()
+ .staticFields()
+ .forEach(field -> targetFields.put(field.getReference(), field));
this.dexItemFactory = appView.dexItemFactory();
}
@@ -40,10 +39,11 @@
private void addField(DexEncodedField field) {
DexField oldFieldReference = field.getReference();
- DexField templateReference = field.getReference().withHolder(target.type, dexItemFactory);
+ DexField templateReference =
+ field.getReference().withHolder(group.getTarget().getType(), dexItemFactory);
DexField newFieldReference =
dexItemFactory.createFreshFieldNameWithHolderSuffix(
- templateReference, field.holder(), this::isFresh);
+ templateReference, field.getHolderType(), this::isFresh);
field = field.toTypeSubstitutedField(newFieldReference);
targetFields.put(newFieldReference, field);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
index 2e2020d..d2cd444 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
@@ -9,10 +9,8 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
@@ -35,24 +33,19 @@
public class ConstructorMerger {
private final AppView<?> appView;
- private final DexProgramClass target;
+ private final MergeGroup group;
private final Collection<DexEncodedMethod> constructors;
private final DexItemFactory dexItemFactory;
- private final DexField classIdField;
ConstructorMerger(
- AppView<?> appView,
- DexProgramClass target,
- Collection<DexEncodedMethod> constructors,
- DexField classIdField) {
+ AppView<?> appView, MergeGroup group, Collection<DexEncodedMethod> constructors) {
this.appView = appView;
- this.target = target;
+ this.group = group;
this.constructors = constructors;
- this.classIdField = classIdField;
// Constructors should not be empty and all constructors should have the same prototype.
assert !constructors.isEmpty();
- assert constructors.stream().map(constructor -> constructor.proto()).distinct().count() == 1;
+ assert constructors.stream().map(DexEncodedMethod::proto).distinct().count() == 1;
this.dexItemFactory = appView.dexItemFactory();
}
@@ -103,12 +96,10 @@
return this;
}
- public List<ConstructorMerger> build(
- AppView<?> appView, DexProgramClass target, DexField classIdField) {
+ public List<ConstructorMerger> build(AppView<?> appView, MergeGroup group) {
assert constructorGroups.stream().noneMatch(List::isEmpty);
return ListUtils.map(
- constructorGroups,
- constructors -> new ConstructorMerger(appView, target, constructors, classIdField));
+ constructorGroups, constructors -> new ConstructorMerger(appView, group, constructors));
}
}
@@ -121,17 +112,17 @@
DexMethod method =
dexItemFactory.createFreshMethodName(
"constructor",
- constructor.holder(),
+ constructor.getHolderType(),
constructor.proto(),
- target.type,
+ group.getTarget().getType(),
classMethodsBuilder::isFresh);
DexEncodedMethod encodedMethod = constructor.toTypeSubstitutedMethod(method);
encodedMethod.getMutableOptimizationInfo().markForceInline();
- encodedMethod.accessFlags.unsetConstructor();
- encodedMethod.accessFlags.unsetPublic();
- encodedMethod.accessFlags.unsetProtected();
- encodedMethod.accessFlags.setPrivate();
+ encodedMethod.getAccessFlags().unsetConstructor();
+ encodedMethod.getAccessFlags().unsetPublic();
+ encodedMethod.getAccessFlags().unsetProtected();
+ encodedMethod.getAccessFlags().setPrivate();
classMethodsBuilder.addDirectMethod(encodedMethod);
return method;
@@ -169,8 +160,8 @@
DexMethod methodReferenceTemplate = generateReferenceMethodTemplate();
DexMethod newConstructorReference =
dexItemFactory.createInstanceInitializerWithFreshProto(
- methodReferenceTemplate.withHolder(target.type, dexItemFactory),
- syntheticArgumentClass.getArgumentClass(),
+ methodReferenceTemplate.withHolder(group.getTarget().getType(), dexItemFactory),
+ syntheticArgumentClass.getArgumentClasses(),
classMethodsBuilder::isFresh);
int extraNulls = newConstructorReference.getArity() - methodReferenceTemplate.getArity();
@@ -179,7 +170,7 @@
new ConstructorEntryPointSynthesizedCode(
typeConstructorClassMap,
newConstructorReference,
- classIdField,
+ group.getClassIdField(),
appView.graphLens().getOriginalMethodSignature(representativeConstructorReference));
DexEncodedMethod newConstructor =
new DexEncodedMethod(
@@ -223,6 +214,7 @@
classMethodsBuilder.addDirectMethod(newConstructor);
- fieldAccessChangesBuilder.fieldWrittenByMethod(classIdField, newConstructorReference);
+ fieldAccessChangesBuilder.fieldWrittenByMethod(
+ group.getClassIdField(), newConstructorReference);
}
}
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 8dfa67c..0821a4c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -9,27 +9,27 @@
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.horizontalclassmerging.policies.AllInstantiatedOrUninstantiated;
import com.android.tools.r8.horizontalclassmerging.policies.CheckAbstractClasses;
-import com.android.tools.r8.horizontalclassmerging.policies.ClassesHaveSameInterfaces;
import com.android.tools.r8.horizontalclassmerging.policies.DontInlinePolicy;
import com.android.tools.r8.horizontalclassmerging.policies.DontMergeIntoLessVisible;
import com.android.tools.r8.horizontalclassmerging.policies.DontMergeSynchronizedClasses;
import com.android.tools.r8.horizontalclassmerging.policies.IgnoreSynthetics;
+import com.android.tools.r8.horizontalclassmerging.policies.LimitGroups;
import com.android.tools.r8.horizontalclassmerging.policies.NoAnnotations;
import com.android.tools.r8.horizontalclassmerging.policies.NoClassesOrMembersWithAnnotations;
+import com.android.tools.r8.horizontalclassmerging.policies.NoDirectRuntimeTypeChecks;
import com.android.tools.r8.horizontalclassmerging.policies.NoEnums;
+import com.android.tools.r8.horizontalclassmerging.policies.NoIndirectRuntimeTypeChecks;
import com.android.tools.r8.horizontalclassmerging.policies.NoInnerClasses;
import com.android.tools.r8.horizontalclassmerging.policies.NoInterfaces;
import com.android.tools.r8.horizontalclassmerging.policies.NoKeepRules;
import com.android.tools.r8.horizontalclassmerging.policies.NoKotlinLambdas;
import com.android.tools.r8.horizontalclassmerging.policies.NoKotlinMetadata;
import com.android.tools.r8.horizontalclassmerging.policies.NoNativeMethods;
-import com.android.tools.r8.horizontalclassmerging.policies.NoRuntimeTypeChecks;
import com.android.tools.r8.horizontalclassmerging.policies.NoServiceLoaders;
import com.android.tools.r8.horizontalclassmerging.policies.NoStaticClassInitializer;
-import com.android.tools.r8.horizontalclassmerging.policies.NotEntryPoint;
import com.android.tools.r8.horizontalclassmerging.policies.NotMatchedByNoHorizontalClassMerging;
import com.android.tools.r8.horizontalclassmerging.policies.NotVerticallyMergedIntoSubtype;
-import com.android.tools.r8.horizontalclassmerging.policies.PreventChangingVisibility;
+import com.android.tools.r8.horizontalclassmerging.policies.PreserveMethodCharacteristics;
import com.android.tools.r8.horizontalclassmerging.policies.PreventMergeIntoMainDex;
import com.android.tools.r8.horizontalclassmerging.policies.PreventMethodImplementation;
import com.android.tools.r8.horizontalclassmerging.policies.RespectPackageBoundaries;
@@ -61,10 +61,10 @@
DirectMappedDexApplication.Builder appBuilder,
MainDexTracingResult mainDexTracingResult,
RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
- List<DexProgramClass> initialGroup = appView.appInfo().classesWithDeterministicOrder();
+ MergeGroup initialGroup = new MergeGroup(appView.appInfo().classesWithDeterministicOrder());
// Run the policies on all program classes to produce a final grouping.
- Collection<List<DexProgramClass>> groups =
+ Collection<MergeGroup> groups =
new SimplePolicyExecutor()
.run(
Collections.singletonList(initialGroup),
@@ -88,7 +88,8 @@
initializeClassMergers(
mergedClassesBuilder, lensBuilder, fieldAccessChangesBuilder, groups);
Iterable<DexProgramClass> allMergeClasses =
- Iterables.concat(Iterables.transform(classMergers, ClassMerger::getClasses));
+ Iterables.concat(
+ Iterables.transform(classMergers, classMerger -> classMerger.getGroup().getClasses()));
// Merge the classes.
SyntheticArgumentClass syntheticArgumentClass =
@@ -109,7 +110,6 @@
new NotMatchedByNoHorizontalClassMerging(appView),
new SameFields(),
new NoInterfaces(),
- new ClassesHaveSameInterfaces(),
new NoAnnotations(),
new NoEnums(appView),
new CheckAbstractClasses(appView),
@@ -123,18 +123,19 @@
new NoKotlinLambdas(appView),
new NoServiceLoaders(appView),
new NotVerticallyMergedIntoSubtype(appView),
- new NoRuntimeTypeChecks(runtimeTypeCheckInfo),
- new NotEntryPoint(appView.dexItemFactory()),
+ new NoDirectRuntimeTypeChecks(runtimeTypeCheckInfo),
+ new NoIndirectRuntimeTypeChecks(appView, runtimeTypeCheckInfo),
new PreventMethodImplementation(appView),
new DontInlinePolicy(appView, mainDexTracingResult),
new PreventMergeIntoMainDex(appView, mainDexTracingResult),
new AllInstantiatedOrUninstantiated(appView),
new SameParentClass(),
new SameNestHost(),
- new PreventChangingVisibility(),
+ new PreserveMethodCharacteristics(),
new SameFeatureSplit(appView),
new RespectPackageBoundaries(appView),
new DontMergeSynchronizedClasses(appView),
+ new LimitGroups(appView),
// TODO(b/166577694): no policies should be run after this policy, as it would
// potentially break tests
new DontMergeIntoLessVisible()
@@ -150,20 +151,15 @@
HorizontallyMergedClasses.Builder mergedClassesBuilder,
HorizontalClassMergerGraphLens.Builder lensBuilder,
FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder,
- Collection<List<DexProgramClass>> groups) {
+ Collection<MergeGroup> groups) {
List<ClassMerger> classMergers = new ArrayList<>();
// TODO(b/166577694): Replace Collection<DexProgramClass> with MergeGroup
- for (List<DexProgramClass> group : groups) {
+ for (MergeGroup group : groups) {
assert !group.isEmpty();
-
- DexProgramClass target = group.stream().findFirst().get();
- group.remove(target);
-
ClassMerger merger =
- new ClassMerger.Builder(appView, target)
- .addClassesToMerge(group)
- .build(appView, mergedClassesBuilder, lensBuilder, fieldAccessChangesBuilder);
+ new ClassMerger.Builder(appView, group)
+ .build(mergedClassesBuilder, lensBuilder, fieldAccessChangesBuilder);
classMergers.add(merger);
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
index cc0528f..16f853e 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
@@ -10,8 +10,10 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription;
import com.android.tools.r8.ir.conversion.ExtraParameter;
import com.android.tools.r8.utils.IterableUtils;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
import com.google.common.collect.BiMap;
import java.util.ArrayList;
import java.util.Collections;
@@ -22,7 +24,7 @@
import java.util.function.Function;
public class HorizontalClassMergerGraphLens extends NestedGraphLens {
- private final AppView<?> appView;
+
private final Map<DexMethod, List<ExtraParameter>> methodExtraParameters;
private final Map<DexMethod, DexMethod> extraOriginalMethodSignatures;
private final HorizontallyMergedClasses mergedClasses;
@@ -35,7 +37,7 @@
Map<DexField, DexField> fieldMap,
Map<DexMethod, DexMethod> methodMap,
BiMap<DexField, DexField> originalFieldSignatures,
- BiMap<DexMethod, DexMethod> originalMethodSignatures,
+ BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures,
Map<DexMethod, DexMethod> extraOriginalMethodSignatures,
Map<DexField, DexField> extraOriginalFieldSignatures,
GraphLens previousLens) {
@@ -47,19 +49,31 @@
originalMethodSignatures,
previousLens,
appView.dexItemFactory());
- this.appView = appView;
this.methodExtraParameters = methodExtraParameters;
this.extraOriginalFieldSignatures = extraOriginalFieldSignatures;
this.extraOriginalMethodSignatures = extraOriginalMethodSignatures;
this.mergedClasses = mergedClasses;
}
+ private boolean isSynthesizedByHorizontalClassMerging(DexMethod method) {
+ return methodExtraParameters.containsKey(method);
+ }
+
@Override
protected Iterable<DexType> internalGetOriginalTypes(DexType previous) {
return IterableUtils.prependSingleton(previous, mergedClasses.getSourcesFor(previous));
}
@Override
+ public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(DexMethod method) {
+ if (isSynthesizedByHorizontalClassMerging(method)) {
+ // If we are processing the call site, the arguments should be removed.
+ return RewrittenPrototypeDescription.none();
+ }
+ return super.lookupPrototypeChangesForMethodDefinition(method);
+ }
+
+ @Override
public DexMethod getOriginalMethodSignature(DexMethod method) {
DexMethod originalConstructor = extraOriginalMethodSignatures.get(method);
if (originalConstructor == null) {
@@ -127,7 +141,7 @@
methodExtraParameters,
fieldMap.getForwardMap(),
methodMap.getForwardMap(),
- inverseFieldMap.getBiMap(),
+ inverseFieldMap.getBiMap().getForwardBacking(),
inverseMethodMap.getBiMap(),
inverseMethodMap.getExtraMap(),
inverseFieldMap.getExtraMap(),
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
index 5432ef1..76ab41a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
@@ -5,12 +5,10 @@
package com.android.tools.r8.horizontalclassmerging;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.classmerging.MergedClasses;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap;
-import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
@@ -84,10 +82,8 @@
return new HorizontallyMergedClasses(mergedClasses);
}
- public void addMergeGroup(DexProgramClass target, Collection<DexProgramClass> toMergeGroup) {
- for (DexProgramClass clazz : toMergeGroup) {
- mergedClasses.put(clazz.type, target.type);
- }
+ public void addMergeGroup(MergeGroup group) {
+ group.forEachSource(clazz -> mergedClasses.put(clazz.getType(), group.getTarget().getType()));
}
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneInverseMap.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneInverseMap.java
index 8da9b0f..973162f 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneInverseMap.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneInverseMap.java
@@ -4,20 +4,20 @@
package com.android.tools.r8.horizontalclassmerging;
-import com.google.common.collect.BiMap;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
import java.util.Map;
/** The inverse of a {@link ManyToOneMap} used for generating graph lens maps. */
public class ManyToOneInverseMap<K, V> {
- private final BiMap<V, K> biMap;
+ private final BidirectionalOneToOneHashMap<V, K> biMap;
private final Map<V, K> extraMap;
- ManyToOneInverseMap(BiMap<V, K> biMap, Map<V, K> extraMap) {
+ ManyToOneInverseMap(BidirectionalOneToOneHashMap<V, K> biMap, Map<V, K> extraMap) {
this.biMap = biMap;
this.extraMap = extraMap;
}
- public BiMap<V, K> getBiMap() {
+ public BidirectionalOneToOneHashMap<V, K> getBiMap() {
return biMap;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneMap.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneMap.java
index 675d24c..5204021 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneMap.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneMap.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.horizontalclassmerging;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import java.util.HashMap;
@@ -49,7 +50,7 @@
}
public ManyToOneInverseMap<K, V> inverse(Function<Set<K>, K> pickRepresentative) {
- BiMap<V, K> biMap = HashBiMap.create();
+ BidirectionalOneToOneHashMap<V, K> biMap = new BidirectionalOneToOneHashMap<>();
Map<V, K> extraMap = new HashMap<>();
for (Entry<V, Set<K>> entry : inverseMap.entrySet()) {
K representative = representativeMap.get(entry.getKey());
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/MergeGroup.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/MergeGroup.java
new file mode 100644
index 0000000..02e4684
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/MergeGroup.java
@@ -0,0 +1,128 @@
+/*
+ * // Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+ * // for details. All rights reserved. Use of this source code is governed by a
+ * // BSD-style license that can be found in the LICENSE file.
+ */
+
+package com.android.tools.r8.horizontalclassmerging;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.utils.IteratorUtils;
+import com.google.common.collect.Iterables;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+public class MergeGroup implements Iterable<DexProgramClass> {
+
+ public static class Metadata {}
+
+ private final LinkedList<DexProgramClass> classes;
+
+ private DexField classIdField;
+ private DexProgramClass target = null;
+ private Metadata metadata = null;
+
+ public MergeGroup() {
+ this.classes = new LinkedList<>();
+ }
+
+ public MergeGroup(Collection<DexProgramClass> classes) {
+ this();
+ addAll(classes);
+ }
+
+ public void applyMetadataFrom(MergeGroup group) {
+ if (metadata == null) {
+ metadata = group.metadata;
+ }
+ }
+
+ public void add(DexProgramClass clazz) {
+ classes.add(clazz);
+ }
+
+ public void addAll(Collection<DexProgramClass> classes) {
+ this.classes.addAll(classes);
+ }
+
+ public void addFirst(DexProgramClass clazz) {
+ classes.addFirst(clazz);
+ }
+
+ public void forEachSource(Consumer<DexProgramClass> consumer) {
+ assert hasTarget();
+ for (DexProgramClass clazz : classes) {
+ if (clazz != target) {
+ consumer.accept(clazz);
+ }
+ }
+ }
+
+ public LinkedList<DexProgramClass> getClasses() {
+ return classes;
+ }
+
+ public boolean hasClassIdField() {
+ return classIdField != null;
+ }
+
+ public DexField getClassIdField() {
+ assert hasClassIdField();
+ return classIdField;
+ }
+
+ public void setClassIdField(DexField classIdField) {
+ this.classIdField = classIdField;
+ }
+
+ public Iterable<DexProgramClass> getSources() {
+ assert hasTarget();
+ return Iterables.filter(classes, clazz -> clazz != target);
+ }
+
+ public boolean hasTarget() {
+ return target != null;
+ }
+
+ public DexProgramClass getTarget() {
+ return target;
+ }
+
+ public void setTarget(DexProgramClass target) {
+ assert classes.contains(target);
+ this.target = target;
+ }
+
+ public boolean isTrivial() {
+ return size() < 2;
+ }
+
+ public boolean isEmpty() {
+ return classes.isEmpty();
+ }
+
+ @Override
+ public Iterator<DexProgramClass> iterator() {
+ return classes.iterator();
+ }
+
+ public int size() {
+ return classes.size();
+ }
+
+ public DexProgramClass removeFirst(Predicate<DexProgramClass> predicate) {
+ return IteratorUtils.removeFirst(iterator(), predicate);
+ }
+
+ public boolean removeIf(Predicate<DexProgramClass> predicate) {
+ return classes.removeIf(predicate);
+ }
+
+ public DexProgramClass removeLast() {
+ return classes.removeLast();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/MultiClassPolicy.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/MultiClassPolicy.java
index eb5d4d0..b21f233 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/MultiClassPolicy.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/MultiClassPolicy.java
@@ -4,25 +4,17 @@
package com.android.tools.r8.horizontalclassmerging;
-import com.android.tools.r8.graph.DexProgramClass;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.List;
public abstract class MultiClassPolicy extends Policy {
- // TODO(b/165577835): Move to a virtual method on MergeGroup.
- protected boolean isTrivial(Collection<DexProgramClass> group) {
- return group.size() < 2;
- }
-
/**
* Remove all groups containing no or only a single class, as there is no point in merging these.
*/
- protected Collection<List<DexProgramClass>> removeTrivialGroups(
- Collection<List<DexProgramClass>> groups) {
+ protected Collection<MergeGroup> removeTrivialGroups(Collection<MergeGroup> groups) {
assert !(groups instanceof ArrayList);
- groups.removeIf(this::isTrivial);
+ groups.removeIf(MergeGroup::isTrivial);
return groups;
}
@@ -34,5 +26,5 @@
* merged. If the policy detects no issues then `group` will be returned unchanged. If classes
* cannot be merged with any other classes they are returned as singleton lists.
*/
- public abstract Collection<List<DexProgramClass>> apply(List<DexProgramClass> group);
+ public abstract Collection<MergeGroup> apply(MergeGroup group);
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/MultiClassSameReferencePolicy.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/MultiClassSameReferencePolicy.java
index bf460d9..3cea5125 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/MultiClassSameReferencePolicy.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/MultiClassSameReferencePolicy.java
@@ -7,16 +7,15 @@
import com.android.tools.r8.graph.DexProgramClass;
import java.util.Collection;
import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.List;
import java.util.Map;
public abstract class MultiClassSameReferencePolicy<T> extends MultiClassPolicy {
+
@Override
- public final Collection<List<DexProgramClass>> apply(List<DexProgramClass> group) {
- Map<T, List<DexProgramClass>> groups = new LinkedHashMap<>();
+ public final Collection<MergeGroup> apply(MergeGroup group) {
+ Map<T, MergeGroup> groups = new LinkedHashMap<>();
for (DexProgramClass clazz : group) {
- groups.computeIfAbsent(getMergeKey(clazz), ignore -> new LinkedList<>()).add(clazz);
+ groups.computeIfAbsent(getMergeKey(clazz), ignore -> new MergeGroup()).add(clazz);
}
removeTrivialGroups(groups.values());
return groups.values();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyExecutor.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyExecutor.java
index a5f58d2..b94adf8 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyExecutor.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyExecutor.java
@@ -4,9 +4,7 @@
package com.android.tools.r8.horizontalclassmerging;
-import com.android.tools.r8.graph.DexProgramClass;
import java.util.Collection;
-import java.util.List;
public abstract class PolicyExecutor {
@@ -15,6 +13,6 @@
* policies registered to this policy executor on the class groups yielding a new collection of
* class groups.
*/
- public abstract Collection<List<DexProgramClass>> run(
- Collection<List<DexProgramClass>> classes, Collection<Policy> policies);
+ public abstract Collection<MergeGroup> run(
+ Collection<MergeGroup> classes, Collection<Policy> policies);
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java
index 92130de..b2fdb3d 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java
@@ -4,12 +4,10 @@
package com.android.tools.r8.horizontalclassmerging;
-import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.utils.IterableUtils;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
-import java.util.List;
-import java.util.stream.Collectors;
/**
* This is a simple policy executor that ensures regular sequential execution of policies. It should
@@ -19,11 +17,11 @@
public class SimplePolicyExecutor extends PolicyExecutor {
// TODO(b/165506334): if performing mutable operation ensure that linked lists are used
- private LinkedList<List<DexProgramClass>> applySingleClassPolicy(
- SingleClassPolicy policy, LinkedList<List<DexProgramClass>> groups) {
- Iterator<List<DexProgramClass>> i = groups.iterator();
+ private LinkedList<MergeGroup> applySingleClassPolicy(
+ SingleClassPolicy policy, LinkedList<MergeGroup> groups) {
+ Iterator<MergeGroup> i = groups.iterator();
while (i.hasNext()) {
- Collection<DexProgramClass> group = i.next();
+ MergeGroup group = i.next();
int previousNumberOfClasses = group.size();
group.removeIf(clazz -> !policy.canMerge(clazz));
policy.numberOfRemovedClasses += previousNumberOfClasses - group.size();
@@ -34,30 +32,29 @@
return groups;
}
- private LinkedList<List<DexProgramClass>> applyMultiClassPolicy(
- MultiClassPolicy policy, LinkedList<List<DexProgramClass>> groups) {
+ private LinkedList<MergeGroup> applyMultiClassPolicy(
+ MultiClassPolicy policy, LinkedList<MergeGroup> groups) {
// For each group apply the multi class policy and add all the new groups together.
- return groups.stream()
- .flatMap(
- group -> {
- int previousNumberOfClasses = group.size();
- Collection<List<DexProgramClass>> newGroups = policy.apply(group);
- policy.numberOfRemovedClasses += previousNumberOfClasses;
- for (List<DexProgramClass> newGroup : newGroups) {
- policy.numberOfRemovedClasses -= newGroup.size();
- }
- return newGroups.stream();
- })
- .collect(Collectors.toCollection(LinkedList::new));
+ LinkedList<MergeGroup> newGroups = new LinkedList<>();
+ groups.forEach(
+ group -> {
+ int previousNumberOfClasses = group.size();
+ Collection<MergeGroup> policyGroups = policy.apply(group);
+ policyGroups.forEach(newGroup -> newGroup.applyMetadataFrom(group));
+ policy.numberOfRemovedClasses +=
+ previousNumberOfClasses - IterableUtils.sumInt(policyGroups, MergeGroup::size);
+ newGroups.addAll(policyGroups);
+ });
+ return newGroups;
}
@Override
- public Collection<List<DexProgramClass>> run(
- Collection<List<DexProgramClass>> inputGroups, Collection<Policy> policies) {
- LinkedList<List<DexProgramClass>> linkedGroups;
+ public Collection<MergeGroup> run(
+ Collection<MergeGroup> inputGroups, Collection<Policy> policies) {
+ LinkedList<MergeGroup> linkedGroups;
if (inputGroups instanceof LinkedList) {
- linkedGroups = (LinkedList<List<DexProgramClass>>) inputGroups;
+ linkedGroups = (LinkedList<MergeGroup>) inputGroups;
} else {
linkedGroups = new LinkedList<>(inputGroups);
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
index 28ec4a9..b6e1f53 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
@@ -17,7 +17,9 @@
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
/**
* Lets assume we are merging a class A that looks like:
@@ -39,29 +41,32 @@
public class SyntheticArgumentClass {
public static final String SYNTHETIC_CLASS_SUFFIX = "$r8$HorizontalClassMergingArgument";
- private final DexType syntheticClassType;
+ private final List<DexType> syntheticClassTypes;
- private SyntheticArgumentClass(DexType syntheticClassType) {
- this.syntheticClassType = syntheticClassType;
+ private SyntheticArgumentClass(List<DexType> syntheticClassTypes) {
+ this.syntheticClassTypes = syntheticClassTypes;
}
- public DexType getArgumentClass() {
- return syntheticClassType;
+ public List<DexType> getArgumentClasses() {
+ return syntheticClassTypes;
}
public static class Builder {
- public SyntheticArgumentClass build(
+ private DexType synthesizeClass(
AppView<AppInfoWithLiveness> appView,
DirectMappedDexApplication.Builder appBuilder,
- Iterable<DexProgramClass> mergeClasses) {
+ DexProgramClass context,
+ boolean requiresMainDex,
+ int index) {
- // Find a fresh name in an existing package.
- DexProgramClass context = mergeClasses.iterator().next();
DexType syntheticClassType =
- context.type.addSuffix(SYNTHETIC_CLASS_SUFFIX, appView.dexItemFactory());
-
- boolean requiresMainDex = appView.appInfo().getMainDexClasses().containsAnyOf(mergeClasses);
+ appView
+ .dexItemFactory()
+ .createFreshTypeName(
+ context.type.addSuffix(SYNTHETIC_CLASS_SUFFIX, appView.dexItemFactory()),
+ type -> appView.appInfo().definitionForWithoutExistenceAssert(type) == null,
+ index);
DexProgramClass clazz =
new DexProgramClass(
@@ -88,7 +93,26 @@
appBuilder.addSynthesizedClass(clazz);
appView.appInfo().addSynthesizedClass(clazz, requiresMainDex);
- return new SyntheticArgumentClass(clazz.type);
+ return clazz.type;
+ }
+
+ public SyntheticArgumentClass build(
+ AppView<AppInfoWithLiveness> appView,
+ DirectMappedDexApplication.Builder appBuilder,
+ Iterable<DexProgramClass> mergeClasses) {
+
+ // Find a fresh name in an existing package.
+ DexProgramClass context = mergeClasses.iterator().next();
+
+ boolean requiresMainDex = appView.appInfo().getMainDexClasses().containsAnyOf(mergeClasses);
+
+ List<DexType> syntheticArgumentTypes = new ArrayList<>();
+ for (int i = 0; i < appView.options().horizontalClassMergingSyntheticArgumentCount; i++) {
+ syntheticArgumentTypes.add(
+ synthesizeClass(appView, appBuilder, context, requiresMainDex, i));
+ }
+
+ return new SyntheticArgumentClass(syntheticArgumentTypes);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
index 49399bf..da61076 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -256,7 +256,7 @@
newMethodReference =
dexItemFactory.createInstanceInitializerWithFreshProto(
newMethodReference,
- syntheticArgumentClass.getArgumentClass(),
+ syntheticArgumentClass.getArgumentClasses(),
tryMethod -> !newMethods.contains(tryMethod.getSignature()));
int extraNulls = newMethodReference.getArity() - originalMethodReference.getArity();
lensBuilder.addExtraParameters(
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
index 258b1c7..fe3a898 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
@@ -32,22 +31,19 @@
import java.util.List;
public class VirtualMethodMerger {
- private final DexProgramClass target;
private final DexItemFactory dexItemFactory;
+ private final MergeGroup group;
private final List<ProgramMethod> methods;
- private final DexField classIdField;
private final AppView<AppInfoWithLiveness> appView;
private final DexMethod superMethod;
public VirtualMethodMerger(
AppView<AppInfoWithLiveness> appView,
- DexProgramClass target,
+ MergeGroup group,
List<ProgramMethod> methods,
- DexField classIdField,
DexMethod superMethod) {
this.dexItemFactory = appView.dexItemFactory();
- this.target = target;
- this.classIdField = classIdField;
+ this.group = group;
this.methods = methods;
this.appView = appView;
this.superMethod = superMethod;
@@ -84,15 +80,11 @@
return resolutionResult.getResolvedMethod().getReference();
}
- public VirtualMethodMerger build(
- AppView<AppInfoWithLiveness> appView,
- DexProgramClass target,
- DexField classIdField,
- int mergeGroupSize) {
+ public VirtualMethodMerger build(AppView<AppInfoWithLiveness> appView, MergeGroup group) {
// If not all the classes are in the merge group, find the fallback super method to call.
- DexMethod superMethod = methods.size() < mergeGroupSize ? superMethod(appView, target) : null;
-
- return new VirtualMethodMerger(appView, target, methods, classIdField, superMethod);
+ DexMethod superMethod =
+ methods.size() < group.size() ? superMethod(appView, group.getTarget()) : null;
+ return new VirtualMethodMerger(appView, group, methods, superMethod);
}
}
@@ -111,11 +103,11 @@
oldMethodReference.name.toSourceString(),
oldMethod.getHolderType(),
oldMethodReference.proto,
- target.type,
+ group.getTarget().getType(),
classMethodsBuilder::isFresh);
DexEncodedMethod encodedMethod = oldMethod.getDefinition().toTypeSubstitutedMethod(method);
- MethodAccessFlags flags = encodedMethod.accessFlags;
+ MethodAccessFlags flags = encodedMethod.getAccessFlags();
flags.unsetProtected();
flags.unsetPublic();
flags.setPrivate();
@@ -138,7 +130,7 @@
}
private DexMethod getNewMethodReference() {
- return ListUtils.first(methods).getReference().withHolder(target, dexItemFactory);
+ return ListUtils.first(methods).getReference().withHolder(group.getTarget(), dexItemFactory);
}
/**
@@ -190,7 +182,7 @@
}
}
- if (representative.getHolderType() == target.getType()) {
+ if (representative.getHolder() == group.getTarget()) {
classMethodsBuilder.addVirtualMethod(representative.getDefinition());
} else {
// If the method is not in the target type, move it.
@@ -262,7 +254,7 @@
AbstractSynthesizedCode synthesizedCode =
new VirtualMethodEntryPointSynthesizedCode(
classIdToMethodMap,
- classIdField,
+ group.getClassIdField(),
superMethod,
newMethodReference,
bridgeMethodReference);
@@ -292,6 +284,6 @@
classMethodsBuilder.addVirtualMethod(newMethod);
- fieldAccessChangesBuilder.fieldReadByMethod(classIdField, newMethod.method);
+ fieldAccessChangesBuilder.fieldReadByMethod(group.getClassIdField(), newMethod.method);
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/CheckAbstractClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/CheckAbstractClasses.java
index 4839801..6e5c711 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/CheckAbstractClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/CheckAbstractClasses.java
@@ -6,15 +6,17 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
+import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
+import com.android.tools.r8.horizontalclassmerging.policies.CheckAbstractClasses.AbstractClassification;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.collect.Lists;
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.List;
-public class CheckAbstractClasses extends MultiClassPolicy {
+public class CheckAbstractClasses extends MultiClassSameReferencePolicy<AbstractClassification> {
+
+ enum AbstractClassification {
+ ABSTRACT,
+ NOT_ABSTRACT
+ }
private final InternalOptions options;
@@ -23,28 +25,16 @@
}
@Override
- public Collection<List<DexProgramClass>> apply(List<DexProgramClass> group) {
- if (options.canUseAbstractMethodOnNonAbstractClass()) {
- // We can just make the target class non-abstract if one of the classes in the group
- // is non-abstract.
- return Lists.<List<DexProgramClass>>newArrayList(group);
- }
- List<DexProgramClass> abstractClasses = new LinkedList<>();
- List<DexProgramClass> nonAbstractClasses = new LinkedList<>();
- for (DexProgramClass clazz : group) {
- if (clazz.isAbstract()) {
- abstractClasses.add(clazz);
- } else {
- nonAbstractClasses.add(clazz);
- }
- }
- List<List<DexProgramClass>> newGroups = new LinkedList<>();
- if (abstractClasses.size() > 1) {
- newGroups.add(abstractClasses);
- }
- if (nonAbstractClasses.size() > 1) {
- newGroups.add(nonAbstractClasses);
- }
- return newGroups;
+ public boolean shouldSkipPolicy() {
+ // We can just make the target class non-abstract if one of the classes in the group
+ // is non-abstract.
+ return options.canUseAbstractMethodOnNonAbstractClass();
+ }
+
+ @Override
+ public AbstractClassification getMergeKey(DexProgramClass clazz) {
+ return clazz.isAbstract()
+ ? AbstractClassification.ABSTRACT
+ : AbstractClassification.NOT_ABSTRACT;
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/ClassesHaveSameInterfaces.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/ClassesHaveSameInterfaces.java
deleted file mode 100644
index 060e1a6..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/ClassesHaveSameInterfaces.java
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.horizontalclassmerging.policies;
-
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexTypeList;
-import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
-
-public class ClassesHaveSameInterfaces extends MultiClassSameReferencePolicy<DexTypeList> {
-
- @Override
- public DexTypeList getMergeKey(DexProgramClass clazz) {
- return clazz.interfaces.getSorted();
- }
-}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontMergeIntoLessVisible.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontMergeIntoLessVisible.java
index 4e9b69e..954a238 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontMergeIntoLessVisible.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontMergeIntoLessVisible.java
@@ -4,30 +4,21 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.horizontalclassmerging.MergeGroup;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
import java.util.Collection;
import java.util.Collections;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
public class DontMergeIntoLessVisible extends MultiClassPolicy {
- @Override
- public Collection<List<DexProgramClass>> apply(List<DexProgramClass> group) {
- Iterator<DexProgramClass> iterator = group.iterator();
- while (iterator.hasNext()) {
- DexProgramClass clazz = iterator.next();
- if (clazz.isPublic()) {
- iterator.remove();
- List<DexProgramClass> newGroup = new LinkedList<>();
- newGroup.add(clazz);
- newGroup.addAll(group);
- group = newGroup;
- break;
- }
- }
+ @Override
+ public Collection<MergeGroup> apply(MergeGroup group) {
+ DexProgramClass clazz = group.removeFirst(DexClass::isPublic);
+ if (clazz != null) {
+ group.addFirst(clazz);
+ }
return Collections.singletonList(group);
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontMergeSynchronizedClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontMergeSynchronizedClasses.java
index d8bebf9..4372869 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontMergeSynchronizedClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontMergeSynchronizedClasses.java
@@ -6,13 +6,13 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.horizontalclassmerging.MergeGroup;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
-import java.util.List;
public class DontMergeSynchronizedClasses extends MultiClassPolicy {
private final AppView<AppInfoWithLiveness> appView;
@@ -26,14 +26,14 @@
}
@Override
- public Collection<List<DexProgramClass>> apply(List<DexProgramClass> group) {
+ public Collection<MergeGroup> apply(MergeGroup group) {
// Gather all synchronized classes.
- Collection<List<DexProgramClass>> synchronizedGroups = new LinkedList<>();
+ Collection<MergeGroup> synchronizedGroups = new LinkedList<>();
group.removeIf(
clazz -> {
boolean synchronizationClass = isSynchronizationClass(clazz);
if (synchronizationClass) {
- List<DexProgramClass> synchronizedGroup = new LinkedList<>();
+ MergeGroup synchronizedGroup = new MergeGroup();
synchronizedGroup.add(clazz);
synchronizedGroups.add(synchronizedGroup);
}
@@ -44,7 +44,7 @@
return Collections.singletonList(group);
}
- Iterator<List<DexProgramClass>> synchronizedGroupIterator = synchronizedGroups.iterator();
+ Iterator<MergeGroup> synchronizedGroupIterator = synchronizedGroups.iterator();
for (DexProgramClass clazz : group) {
if (!synchronizedGroupIterator.hasNext()) {
synchronizedGroupIterator = synchronizedGroups.iterator();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitGroups.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitGroups.java
new file mode 100644
index 0000000..f2800aa
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitGroups.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.horizontalclassmerging.policies;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.horizontalclassmerging.MergeGroup;
+import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+
+public class LimitGroups extends MultiClassPolicy {
+
+ private final int maxGroupSize;
+
+ public LimitGroups(AppView<AppInfoWithLiveness> appView) {
+ maxGroupSize = appView.options().horizontalClassMergingMaxGroupSize;
+ assert maxGroupSize >= 2;
+ }
+
+ @Override
+ public Collection<MergeGroup> apply(MergeGroup group) {
+ if (group.size() <= maxGroupSize) {
+ return Collections.singletonList(group);
+ }
+
+ LinkedList<MergeGroup> newGroups = new LinkedList<>();
+ MergeGroup newGroup = createNewGroup(newGroups);
+ for (DexProgramClass clazz : group) {
+ if (newGroup.size() == maxGroupSize) {
+ newGroup = createNewGroup(newGroups);
+ }
+ newGroup.add(clazz);
+ }
+ if (newGroup.size() == 1) {
+ if (maxGroupSize == 2) {
+ MergeGroup removedGroup = newGroups.removeLast();
+ assert removedGroup == newGroup;
+ } else {
+ newGroup.add(newGroups.getFirst().removeLast());
+ }
+ }
+ return newGroups;
+ }
+
+ private MergeGroup createNewGroup(LinkedList<MergeGroup> newGroups) {
+ MergeGroup newGroup = new MergeGroup();
+ newGroups.add(newGroup);
+ return newGroup;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoRuntimeTypeChecks.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDirectRuntimeTypeChecks.java
similarity index 75%
rename from src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoRuntimeTypeChecks.java
rename to src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDirectRuntimeTypeChecks.java
index 010fee8..b0f3766 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoRuntimeTypeChecks.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDirectRuntimeTypeChecks.java
@@ -8,16 +8,15 @@
import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
import com.android.tools.r8.shaking.RuntimeTypeCheckInfo;
-public class NoRuntimeTypeChecks extends SingleClassPolicy {
+public class NoDirectRuntimeTypeChecks extends SingleClassPolicy {
private final RuntimeTypeCheckInfo runtimeTypeCheckInfo;
- public NoRuntimeTypeChecks(RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
+ public NoDirectRuntimeTypeChecks(RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
this.runtimeTypeCheckInfo = runtimeTypeCheckInfo;
}
@Override
public boolean canMerge(DexProgramClass clazz) {
- // We currently assume we only merge classes that implement the same set of interfaces.
return !runtimeTypeCheckInfo.isRuntimeCheckType(clazz);
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIndirectRuntimeTypeChecks.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIndirectRuntimeTypeChecks.java
new file mode 100644
index 0000000..7446787
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIndirectRuntimeTypeChecks.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.horizontalclassmerging.policies;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.RuntimeTypeCheckInfo;
+import it.unimi.dsi.fastutil.objects.Reference2BooleanMap;
+import it.unimi.dsi.fastutil.objects.Reference2BooleanOpenHashMap;
+
+public class NoIndirectRuntimeTypeChecks extends MultiClassSameReferencePolicy<DexTypeList> {
+
+ private final AppView<AppInfoWithLiveness> appView;
+ private final RuntimeTypeCheckInfo runtimeTypeCheckInfo;
+
+ private final Reference2BooleanMap<DexType> cache = new Reference2BooleanOpenHashMap<>();
+
+ public NoIndirectRuntimeTypeChecks(
+ AppView<AppInfoWithLiveness> appView, RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
+ this.appView = appView;
+ this.runtimeTypeCheckInfo = runtimeTypeCheckInfo;
+ }
+
+ @Override
+ public DexTypeList getMergeKey(DexProgramClass clazz) {
+ // Require that classes that implement an interface that has a runtime type check (directly or
+ // indirectly on a parent interface) are only merged with classes that implement the same
+ // interfaces.
+ return clazz
+ .getInterfaces()
+ .keepIf(this::computeInterfaceHasDirectOrIndirectRuntimeTypeCheck)
+ .getSorted();
+ }
+
+ private boolean computeInterfaceHasDirectOrIndirectRuntimeTypeCheck(DexType type) {
+ if (cache.containsKey(type)) {
+ return cache.getBoolean(type);
+ }
+ DexClass clazz = appView.definitionFor(type);
+ if (clazz == null || !clazz.isInterface()) {
+ cache.put(type, true);
+ return true;
+ }
+ if (!clazz.isProgramClass()) {
+ // Conservatively return true because there could be a type check in the library.
+ cache.put(type, true);
+ return true;
+ }
+ if (runtimeTypeCheckInfo.isRuntimeCheckType(clazz.asProgramClass())) {
+ cache.put(type, true);
+ return true;
+ }
+ for (DexType parentType : clazz.getInterfaces()) {
+ if (computeInterfaceHasDirectOrIndirectRuntimeTypeCheck(parentType)) {
+ cache.put(type, true);
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoOverlappingConstructors.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoOverlappingConstructors.java
deleted file mode 100644
index 245b391..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoOverlappingConstructors.java
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.horizontalclassmerging.policies;
-
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.IdentityHashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-
-public class NoOverlappingConstructors extends MultiClassPolicy {
-
- public void removeNonConflicting(Map<DexProto, Set<DexProgramClass>> overlappingConstructors) {
- Iterator<DexProto> i = overlappingConstructors.keySet().iterator();
- while (i.hasNext()) {
- DexProto proto = i.next();
- if (overlappingConstructors.get(proto).size() == 1) {
- i.remove();
- }
- }
- }
-
- private Set<DexProgramClass> sortedClassSet(Collection<DexProgramClass> classes) {
- Set<DexProgramClass> set =
- new TreeSet<DexProgramClass>(Comparator.comparing(DexProgramClass::getType));
- set.addAll(classes);
- return set;
- }
-
- @Override
- public Collection<List<DexProgramClass>> apply(List<DexProgramClass> group) {
- Map<DexProto, Set<DexProgramClass>> overlappingConstructors = new IdentityHashMap<>();
-
- for (DexProgramClass clazz : group) {
- clazz.forEachProgramDirectMethod(
- directMethod -> {
- DexEncodedMethod method = directMethod.getDefinition();
- if (method.isInstanceInitializer()) {
- overlappingConstructors
- .computeIfAbsent(method.getProto(), ignore -> new HashSet<DexProgramClass>())
- .add(clazz);
- }
- });
- }
-
- removeNonConflicting(overlappingConstructors);
-
- // This is probably related to the graph colouring problem so probably won't be efficient in
- // worst cases. We assume there won't be that many overlapping constructors so this should be
- // reasonable. Constructor merging should also make this obsolete.
- Collection<Set<DexProgramClass>> groups = new LinkedList<>();
- groups.add(sortedClassSet(group));
-
- for (Set<DexProgramClass> overlappingClasses : overlappingConstructors.values()) {
- Collection<Set<DexProgramClass>> newGroups = new LinkedList<>();
-
- // For every set of classes that cannot be in the same group, generate a new group with the
- // same constructor and with all remaining constructors.
-
- for (Set<DexProgramClass> existingGroup : groups) {
- Set<DexProgramClass> actuallyOverlapping = new HashSet<>(overlappingClasses);
- actuallyOverlapping.retainAll(existingGroup);
-
- if (actuallyOverlapping.size() <= 1) {
- newGroups.add(existingGroup);
- } else {
- Set<DexProgramClass> notOverlapping = new HashSet<>(existingGroup);
- notOverlapping.removeAll(overlappingClasses);
- for (DexProgramClass overlappingClass : actuallyOverlapping) {
- Set<DexProgramClass> newGroup = sortedClassSet(notOverlapping);
- newGroup.add(overlappingClass);
- newGroups.add(newGroup);
- }
- }
- }
-
- groups = newGroups;
- }
-
- // Ensure each class is only in a single group and remove singleton and empty groups.
- Set<DexProgramClass> assignedClasses = new HashSet<>();
-
- Iterator<Set<DexProgramClass>> i = groups.iterator();
- while (i.hasNext()) {
- Set<DexProgramClass> newGroup = i.next();
- newGroup.removeAll(assignedClasses);
- if (newGroup.size() <= 1) {
- i.remove();
- } else {
- assignedClasses.addAll(newGroup);
- }
- }
-
- // Map to collection
- Collection<List<DexProgramClass>> newGroups = new ArrayList<>();
- for (Set<DexProgramClass> newGroup : groups) {
- newGroups.add(new ArrayList<>(newGroup));
- }
- return newGroups;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotEntryPoint.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotEntryPoint.java
deleted file mode 100644
index 4bc65e5..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotEntryPoint.java
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.horizontalclassmerging.policies;
-
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
-
-public class NotEntryPoint extends SingleClassPolicy {
- private final DexString main;
-
- public NotEntryPoint(DexItemFactory factory) {
- main = factory.createString("main");
- }
-
- @Override
- public boolean canMerge(DexProgramClass program) {
- // TODO(b/165000217): Account for keep rules instead.
- for (DexEncodedMethod method : program.directMethods()) {
- if (method.method.name.equals(main)) {
- return false;
- }
- }
- return true;
- }
-}
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
new file mode 100644
index 0000000..adcde5c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
@@ -0,0 +1,105 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.horizontalclassmerging.policies;
+
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethodSignature;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.horizontalclassmerging.MergeGroup;
+import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
+import com.google.common.collect.Iterables;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Policy that enforces that methods are only merged if they have the same visibility and library
+ * method override information.
+ */
+public class PreserveMethodCharacteristics extends MultiClassPolicy {
+
+ static class MethodCharacteristics {
+
+ private final int visibilityOrdinal;
+
+ private MethodCharacteristics(DexEncodedMethod method) {
+ this.visibilityOrdinal = method.getAccessFlags().getVisibilityOrdinal();
+ }
+
+ @Override
+ public int hashCode() {
+ return visibilityOrdinal;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ MethodCharacteristics characteristics = (MethodCharacteristics) obj;
+ return visibilityOrdinal == characteristics.visibilityOrdinal;
+ }
+ }
+
+ public PreserveMethodCharacteristics() {}
+
+ public static class TargetGroup {
+
+ private final MergeGroup group = new MergeGroup();
+ private final Map<DexMethodSignature, MethodCharacteristics> methodMap = new HashMap<>();
+
+ public MergeGroup getGroup() {
+ return group;
+ }
+
+ public boolean tryAdd(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);
+ if (existingCharacteristics == null) {
+ newMethods.put(signature, methodCharacteristics);
+ continue;
+ }
+ if (!methodCharacteristics.equals(existingCharacteristics)) {
+ return false;
+ }
+ }
+ methodMap.putAll(newMethods);
+ group.add(clazz);
+ return true;
+ }
+ }
+
+ @Override
+ public Collection<MergeGroup> apply(MergeGroup group) {
+ List<TargetGroup> groups = new ArrayList<>();
+
+ for (DexProgramClass clazz : group) {
+ boolean added = Iterables.any(groups, targetGroup -> targetGroup.tryAdd(clazz));
+ if (!added) {
+ TargetGroup newGroup = new TargetGroup();
+ added = newGroup.tryAdd(clazz);
+ assert added;
+ groups.add(newGroup);
+ }
+ }
+
+ LinkedList<MergeGroup> newGroups = new LinkedList<>();
+ for (TargetGroup newGroup : groups) {
+ if (!newGroup.getGroup().isTrivial()) {
+ newGroups.add(newGroup.getGroup());
+ }
+ }
+ return newGroups;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventChangingVisibility.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventChangingVisibility.java
index 8b35a5c..cd58e10 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventChangingVisibility.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventChangingVisibility.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.horizontalclassmerging.MergeGroup;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.google.common.base.Equivalence.Wrapper;
@@ -15,7 +16,6 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -23,10 +23,10 @@
public PreventChangingVisibility() {}
public static class TargetGroup {
- private final List<DexProgramClass> group = new LinkedList<>();
+ private final MergeGroup group = new MergeGroup();
private final Map<Wrapper<DexMethod>, MethodAccessFlags> methodMap = new HashMap<>();
- public List<DexProgramClass> getGroup() {
+ public MergeGroup getGroup() {
return group;
}
@@ -53,7 +53,7 @@
}
@Override
- public Collection<List<DexProgramClass>> apply(List<DexProgramClass> group) {
+ public Collection<MergeGroup> apply(MergeGroup group) {
List<TargetGroup> groups = new ArrayList<>();
for (DexProgramClass clazz : group) {
@@ -66,13 +66,12 @@
}
}
- Collection<List<DexProgramClass>> newGroups = new ArrayList<>();
+ Collection<MergeGroup> newGroups = new ArrayList<>();
for (TargetGroup newGroup : groups) {
- if (!isTrivial(newGroup.getGroup())) {
+ if (!newGroup.getGroup().isTrivial()) {
newGroups.add(newGroup.getGroup());
}
}
-
return newGroups;
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMethodImplementation.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMethodImplementation.java
index b8083e1..dcbd291 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMethodImplementation.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMethodImplementation.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
+import com.android.tools.r8.horizontalclassmerging.MergeGroup;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
import com.android.tools.r8.horizontalclassmerging.SubtypingForrestForClasses;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -19,8 +20,6 @@
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.List;
import java.util.Map;
/**
@@ -144,13 +143,13 @@
}
@Override
- public Collection<List<DexProgramClass>> apply(List<DexProgramClass> group) {
+ public Collection<MergeGroup> apply(MergeGroup group) {
DexMethodSignatureSet signatures = DexMethodSignatureSet.createLinked();
for (DexProgramClass clazz : group) {
signatures.addAllMethods(clazz.methods());
}
- Map<DispatchSignature, List<DexProgramClass>> newGroups = new LinkedHashMap<>();
+ Map<DispatchSignature, MergeGroup> newGroups = new LinkedHashMap<>();
for (DexProgramClass clazz : group) {
DexMethodSignatureSet clazzReserved = computeReservedSignaturesForClass(clazz);
DispatchSignature dispatchSignature = new DispatchSignature();
@@ -166,7 +165,7 @@
}
dispatchSignature.addSignature(signature, category);
}
- newGroups.computeIfAbsent(dispatchSignature, ignore -> new LinkedList<>()).add(clazz);
+ newGroups.computeIfAbsent(dispatchSignature, ignore -> new MergeGroup()).add(clazz);
}
return removeTrivialGroups(newGroups.values());
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
index bd59a0e..914fc57 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
@@ -7,16 +7,14 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMember;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.horizontalclassmerging.MergeGroup;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.VerticalClassMerger.IllegalAccessDetector;
import com.android.tools.r8.utils.TraversalContinuation;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Iterator;
import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.List;
import java.util.Map;
public class RespectPackageBoundaries extends MultiClassPolicy {
@@ -58,30 +56,28 @@
/** Sort unrestricted classes into restricted classes if they are in the same package. */
void tryFindRestrictedPackage(
- LinkedList<DexProgramClass> unrestrictedClasses,
- Map<String, List<DexProgramClass>> restrictedClasses) {
- Iterator<DexProgramClass> i = unrestrictedClasses.iterator();
- while (i.hasNext()) {
- DexProgramClass clazz = i.next();
- Collection<DexProgramClass> restrictedPackage =
- restrictedClasses.get(clazz.type.getPackageDescriptor());
- if (restrictedPackage != null) {
- restrictedPackage.add(clazz);
- i.remove();
- }
- }
+ MergeGroup unrestrictedClasses, Map<String, MergeGroup> restrictedClasses) {
+ unrestrictedClasses.removeIf(
+ clazz -> {
+ MergeGroup restrictedPackage = restrictedClasses.get(clazz.type.getPackageDescriptor());
+ if (restrictedPackage != null) {
+ restrictedPackage.add(clazz);
+ return true;
+ }
+ return false;
+ });
}
@Override
- public Collection<List<DexProgramClass>> apply(List<DexProgramClass> group) {
- Map<String, List<DexProgramClass>> restrictedClasses = new LinkedHashMap<>();
- LinkedList<DexProgramClass> unrestrictedClasses = new LinkedList<>();
+ public Collection<MergeGroup> apply(MergeGroup group) {
+ Map<String, MergeGroup> restrictedClasses = new LinkedHashMap<>();
+ MergeGroup unrestrictedClasses = new MergeGroup();
// Sort all restricted classes into packages.
for (DexProgramClass clazz : group) {
if (shouldRestrictMergingAcrossPackageBoundary(clazz)) {
restrictedClasses
- .computeIfAbsent(clazz.type.getPackageDescriptor(), ignore -> new ArrayList<>())
+ .computeIfAbsent(clazz.getType().getPackageDescriptor(), ignore -> new MergeGroup())
.add(clazz);
} else {
unrestrictedClasses.add(clazz);
@@ -93,7 +89,7 @@
// TODO(b/166577694): Add the unrestricted classes to restricted groups, but ensure they aren't
// the merge target.
- Collection<List<DexProgramClass>> groups = new ArrayList<>(restrictedClasses.size() + 1);
+ Collection<MergeGroup> groups = new ArrayList<>(restrictedClasses.size() + 1);
if (unrestrictedClasses.size() > 1) {
groups.add(unrestrictedClasses);
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
index 43fe1ac..dbaf662 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
@@ -337,7 +337,7 @@
if (!resolutionResult.isSingleResolution()) {
return false;
}
- DexType holder = resolutionResult.getSingleTarget().holder();
+ DexType holder = resolutionResult.getSingleTarget().getHolderType();
return appView.isSubtype(holder, type).isTrue();
}
@@ -398,7 +398,7 @@
if (!resolutionResult.isSingleResolution()) {
return false;
}
- DexType holder = resolutionResult.getSingleTarget().holder();
+ DexType holder = resolutionResult.getSingleTarget().getHolderType();
return appView.isSubtype(holder, type).isTrue();
}
@@ -435,7 +435,7 @@
if (!resolutionResult.isSingleResolution()) {
return false;
}
- DexType holder = resolutionResult.getSingleTarget().holder();
+ DexType holder = resolutionResult.getSingleTarget().getHolderType();
return appView.isSubtype(holder, type).isTrue();
}
@@ -511,11 +511,11 @@
enqueue(clazz.type, visited, worklist);
} else if (definition.isDexEncodedField()) {
DexEncodedField field = definition.asDexEncodedField();
- enqueue(field.holder(), visited, worklist);
+ enqueue(field.getHolderType(), visited, worklist);
} else if (definition.isDexEncodedMethod()) {
assert instruction.isInvokeMethod();
DexEncodedMethod method = definition.asDexEncodedMethod();
- enqueue(method.holder(), visited, worklist);
+ enqueue(method.getHolderType(), visited, worklist);
enqueueInitializedClassesOnNormalExit(method, instruction.inValues(), visited, worklist);
} else {
assert false;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java
index 01ea16b..eb26afa 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java
@@ -117,8 +117,8 @@
DexEncodedField field =
appView.appInfo().resolveField(instruction.getField()).getResolvedField();
if (field != null) {
- if (field.holder().isClassType()) {
- markInitializedOnNormalExit(field.holder());
+ if (field.getHolderType().isClassType()) {
+ markInitializedOnNormalExit(field.getHolderType());
} else {
assert false : "Expected holder of field type to be a class type";
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
index 6e46731..2cf0214 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
@@ -21,7 +21,7 @@
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeNewArray;
import com.android.tools.r8.ir.code.NewArrayEmpty;
import com.android.tools.r8.ir.code.NewArrayFilledData;
@@ -255,7 +255,7 @@
}
// Find the single constructor invocation.
- InvokeMethod constructorInvoke =
+ InvokeDirect constructorInvoke =
newInstance.getUniqueConstructorInvoke(appView.dexItemFactory());
if (constructorInvoke == null || constructorInvoke.getInvokedMethod().holder != clazz.type) {
// Didn't find a (valid) constructor invocation, give up.
@@ -269,7 +269,7 @@
}
InstanceInitializerInfo initializerInfo =
- constructor.getOptimizationInfo().getInstanceInitializerInfo();
+ constructor.getOptimizationInfo().getInstanceInitializerInfo(constructorInvoke);
List<DexEncodedField> fields = clazz.getDirectAndIndirectInstanceFields(appView);
if (!fields.isEmpty()) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
index d0fd119..d0a86ef 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
@@ -162,7 +162,7 @@
singleTarget
.getDefinition()
.getOptimizationInfo()
- .getInstanceInitializerInfo()
+ .getInstanceInitializerInfo(invoke)
.fieldInitializationInfos();
// Synchronize on the lattice element (abstractInstanceFieldValuesForClass) in case we process
@@ -232,7 +232,7 @@
InstanceFieldInitializationInfo fieldInitializationInfo =
method
.getOptimizationInfo()
- .getInstanceInitializerInfo()
+ .getContextInsensitiveInstanceInitializerInfo()
.fieldInitializationInfos()
.get(field);
if (fieldInitializationInfo.isSingleValue()) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
index d42d5d1..443ac30 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
@@ -120,7 +120,7 @@
@Override
boolean isSubjectToOptimization(DexEncodedField field) {
- return !field.isStatic() && field.holder() == context.getHolderType();
+ return !field.isStatic() && field.getHolderType() == context.getHolderType();
}
@Override
@@ -147,7 +147,7 @@
singleTarget
.getDefinition()
.getOptimizationInfo()
- .getInstanceInitializerInfo()
+ .getInstanceInitializerInfo(invoke)
.fieldInitializationInfos();
for (DexEncodedField field : singleTarget.getHolder().instanceFields()) {
assert isSubjectToOptimization(field);
@@ -169,7 +169,7 @@
parentConstructor
.getDefinition()
.getOptimizationInfo()
- .getInstanceInitializerInfo()
+ .getInstanceInitializerInfo(parentConstructorCall)
.fieldInitializationInfos();
infos.forEach(
appView,
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
index c75b845..9be6c55 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
@@ -91,7 +91,7 @@
@Override
boolean isSubjectToOptimization(DexEncodedField field) {
return field.isStatic()
- && field.holder() == context.getHolderType()
+ && field.getHolderType() == context.getHolderType()
&& appView.appInfo().isFieldOnlyWrittenInMethod(field, context.getDefinition());
}
@@ -348,7 +348,7 @@
singleTarget
.getDefinition()
.getOptimizationInfo()
- .getInstanceInitializerInfo()
+ .getInstanceInitializerInfo(uniqueConstructorInvoke)
.fieldInitializationInfos();
if (initializationInfos.isEmpty()) {
return ObjectState.empty();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
index 04f9a85..e9b454a 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
@@ -123,7 +123,7 @@
*/
public void rewriteCode(DexEncodedMethod method, IRCode code) {
if (method.isClassInitializer()
- && classesWithRemovedExtensionFields.contains(method.holder())
+ && classesWithRemovedExtensionFields.contains(method.getHolderType())
&& code.metadata().mayHaveStaticPut()) {
rewriteClassInitializer(code);
}
@@ -176,12 +176,18 @@
clazz.forEachProgramMethodMatching(
definition ->
references.isFindLiteExtensionByNumberMethod(definition.getReference()),
- consumer::accept),
+ consumer),
lambda -> {
assert false;
});
}
+ public void handleFailedOrUnknownFieldResolution(DexField fieldReference, ProgramMethod context) {
+ if (references.isFindLiteExtensionByNumberMethod(context)) {
+ removedExtensionFields.add(fieldReference);
+ }
+ }
+
public boolean isDeadProtoExtensionField(DexField fieldReference) {
AppInfoWithLiveness appInfo = appView.appInfo();
FieldResolutionResult resolutionResult = appInfo.resolveField(fieldReference);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
index c44747f..d84295c 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
@@ -267,7 +267,8 @@
continue;
}
- DexProgramClass holder = asProgramClassOrNull(appView.definitionFor(field.holder()));
+ DexProgramClass holder =
+ asProgramClassOrNull(appView.definitionFor(field.getHolderType()));
if (holder == null) {
assert false;
continue;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java
index 5d771a5..c6fdbab 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java
@@ -73,7 +73,7 @@
DexEncodedField field =
appView.appInfo().resolveField(staticPut.getField()).getResolvedField();
if (field == null
- || field.holder() != context.getHolderType()
+ || field.getHolderType() != context.getHolderType()
|| instruction.instructionInstanceCanThrow(appView, context)) {
return ClassInitializerSideEffect.SIDE_EFFECTS_THAT_CANNOT_BE_POSTPONED;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
index 3c9bd86..8de1c91 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
@@ -95,7 +95,7 @@
if (!encodedField.isPublic()) {
return false;
}
- DexClass holder = appView.definitionFor(encodedField.holder());
+ DexClass holder = appView.definitionFor(encodedField.getHolderType());
if (holder == null) {
assert false;
return false;
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index c61ca0a..b4ff341 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -202,7 +202,7 @@
return singleTarget
.getDefinition()
.getOptimizationInfo()
- .getInstanceInitializerInfo()
+ .getInstanceInitializerInfo(this)
.readSet();
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
index 8928a4d..fdaf8f0 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
@@ -255,7 +255,9 @@
DexEncodedMethod singleTargetDefinition = singleTarget.getDefinition();
MethodOptimizationInfo optimizationInfo = singleTargetDefinition.getOptimizationInfo();
if (singleTargetDefinition.isInstanceInitializer()) {
- InstanceInitializerInfo initializerInfo = optimizationInfo.getInstanceInitializerInfo();
+ assert isInvokeDirect();
+ InstanceInitializerInfo initializerInfo =
+ optimizationInfo.getInstanceInitializerInfo(asInvokeDirect());
if (!initializerInfo.mayHaveOtherSideEffectsThanInstanceFieldAssignments()) {
return !isInvokeDirect();
}
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 0dd2895..889089f 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
@@ -12,6 +12,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.Sets;
import java.util.ListIterator;
import java.util.Set;
@@ -21,6 +22,7 @@
private BasicBlock currentBlock;
private InstructionListIterator currentBlockIterator;
+ private Set<BasicBlock> seenBlocks = Sets.newIdentityHashSet();
public LinearFlowInstructionListIterator(IRCode code, BasicBlock block) {
this(code, block, 0);
@@ -32,12 +34,17 @@
this.currentBlockIterator = block.listIterator(code, index);
// If index is pointing after the last instruction, and it is a goto with a linear edge,
// we have to move the pointer. This is achieved by calling previous and next.
+ seenBlocks.add(block);
if (index > 0) {
this.previous();
this.next();
}
}
+ public boolean hasVisitedBlock(BasicBlock basicBlock) {
+ return seenBlocks.contains(basicBlock);
+ }
+
@Override
public void replaceCurrentInstruction(Instruction newInstruction, Set<Value> affectedValues) {
currentBlockIterator.replaceCurrentInstruction(newInstruction, affectedValues);
@@ -146,6 +153,7 @@
target = candidate;
}
currentBlock = target;
+ seenBlocks.add(target);
currentBlockIterator = currentBlock.listIterator(code);
return currentBlockIterator.next();
}
@@ -183,6 +191,7 @@
return currentBlockIterator.previous();
}
currentBlock = target;
+ seenBlocks.add(target);
currentBlockIterator = currentBlock.listIterator(code, currentBlock.getInstructions().size());
// Iterate over the jump.
currentBlockIterator.previous();
diff --git a/src/main/java/com/android/tools/r8/ir/code/ValueType.java b/src/main/java/com/android/tools/r8/ir/code/ValueType.java
index 729cdcf..8b06849 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ValueType.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ValueType.java
@@ -9,14 +9,26 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.PrimitiveTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.utils.structural.StructuralAccept;
+import com.android.tools.r8.utils.structural.StructuralItem;
-public enum ValueType {
+public enum ValueType implements StructuralItem<ValueType> {
OBJECT,
INT,
FLOAT,
LONG,
DOUBLE;
+ @Override
+ public ValueType self() {
+ return this;
+ }
+
+ @Override
+ public StructuralAccept<ValueType> getStructuralAccept() {
+ return spec -> spec.withInt(Enum::ordinal);
+ }
+
public boolean isObject() {
return this == OBJECT;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
index 6756693..967fee0 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
@@ -262,7 +262,8 @@
return;
}
- DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(encodedField.holder()));
+ DexProgramClass clazz =
+ asProgramClassOrNull(appView.definitionFor(encodedField.getHolderType()));
if (clazz == null) {
return;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index 7f29860..e0841f7 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -331,7 +331,7 @@
}
}
return new CfCode(
- method.holder(),
+ method.getHolderType(),
stackHeightTracker.maxHeight,
registerAllocator.registersUsed(),
instructions,
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 74191f0..042441e 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -531,7 +531,7 @@
int argumentIndex = 0;
if (!method.isStatic()) {
- writeCallback.accept(register, method.holder());
+ writeCallback.accept(register, method.getHolderType());
addThisArgument(register);
argumentIndex++;
register++;
@@ -1530,7 +1530,7 @@
if (invocationMethod.holder == method.getHolderType()) {
DexEncodedMethod directTarget = method.getHolder().lookupDirectMethod(invocationMethod);
if (directTarget != null && !directTarget.isStatic()) {
- assert invocationMethod.holder == directTarget.holder();
+ assert invocationMethod.holder == directTarget.getHolderType();
type = Type.DIRECT;
}
}
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 ec95001..0974715 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
@@ -107,10 +107,12 @@
import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -480,7 +482,33 @@
executor, OptimizationFeedbackIgnore.getInstance());
DexApplication application = appView.appInfo().app();
timing.begin("IR conversion");
- ThreadUtils.processItems(application.classes(), this::convertMethods, executor);
+
+ if (appView.options().desugarSpecificOptions().allowAllDesugaredInput) {
+ // Classes which has already been through library desugaring will not go through IR
+ // processing again.
+ LibraryDesugaredChecker libraryDesugaredChecker = new LibraryDesugaredChecker(appView);
+ Set<DexProgramClass> alreadyLibraryDesugared = Sets.newConcurrentHashSet();
+ ThreadUtils.processItems(
+ application.classes(),
+ clazz -> {
+ if (libraryDesugaredChecker.isClassLibraryDesugared(clazz)) {
+ if (appView.options().desugarSpecificOptions().allowAllDesugaredInput) {
+ alreadyLibraryDesugared.add(clazz);
+ } else {
+ throw new CompilationError(
+ "Code for "
+ + clazz.getType().getDescriptor()
+ + "has already been library desugared.");
+ }
+ } else {
+ convertMethods(clazz);
+ }
+ },
+ executor);
+ appView.setAlreadyLibraryDesugared(alreadyLibraryDesugared);
+ } else {
+ ThreadUtils.processItems(application.classes(), this::convertMethods, executor);
+ }
// Build a new application with jumbo string info,
Builder<?> builder = application.builder();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index 20cc2c6..cf605e2 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -223,9 +223,11 @@
graphLens.lookupMethod(invokedMethod, method.getReference(), invoke.getType());
DexMethod actualTarget = lensLookup.getReference();
Invoke.Type actualInvokeType = lensLookup.getType();
- if (actualTarget != invokedMethod || invoke.getType() != actualInvokeType) {
- RewrittenPrototypeDescription prototypeChanges = lensLookup.getPrototypeChanges();
+ RewrittenPrototypeDescription prototypeChanges = lensLookup.getPrototypeChanges();
+ if (prototypeChanges.requiresRewritingAtCallSite()
+ || invoke.getType() != actualInvokeType
+ || actualTarget != invokedMethod) {
List<Value> newInValues;
ArgumentInfoCollection argumentInfoCollection =
prototypeChanges.getArgumentInfoCollection();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LibraryDesugaredChecker.java b/src/main/java/com/android/tools/r8/ir/conversion/LibraryDesugaredChecker.java
new file mode 100644
index 0000000..7707d9e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LibraryDesugaredChecker.java
@@ -0,0 +1,183 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.conversion;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.DexValue.DexValueArray;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
+
+public class LibraryDesugaredChecker {
+ private final AppView<?> appView;
+ private final DexString jDollarDescriptorPrefix;
+
+ LibraryDesugaredChecker(AppView<?> appView) {
+ this.appView = appView;
+ this.jDollarDescriptorPrefix = appView.dexItemFactory().createString("Lj$/");
+ }
+
+ public boolean isClassLibraryDesugared(DexProgramClass clazz) {
+ IsLibraryDesugaredTracer tracer =
+ new IsLibraryDesugaredTracer(appView, jDollarDescriptorPrefix, clazz);
+ tracer.run();
+ return tracer.isLibraryDesugared();
+ }
+
+ private static class IsLibraryDesugaredTracer extends UseRegistry {
+
+ private final DexString jDollarDescriptorPrefix;
+ private final AppView<?> appView;
+ private final DexProgramClass clazz;
+ private boolean isLibraryDesugared = false;
+
+ public IsLibraryDesugaredTracer(
+ AppView<?> appView, DexString jDollarDescriptorPrefix, DexProgramClass clazz) {
+ super(appView.dexItemFactory());
+ this.jDollarDescriptorPrefix = jDollarDescriptorPrefix;
+ this.appView = appView;
+ this.clazz = clazz;
+ }
+
+ public void run() {
+ registerClass(clazz);
+ }
+
+ public boolean isLibraryDesugared() {
+ return isLibraryDesugared;
+ }
+
+ private void registerClass(DexProgramClass clazz) {
+ if (clazz.superType != null) {
+ registerTypeReference(clazz.superType);
+ }
+ for (DexType implementsType : clazz.interfaces.values) {
+ registerTypeReference(implementsType);
+ }
+ if (isLibraryDesugared) {
+ return;
+ }
+ for (DexEncodedMethod method : clazz.methods()) {
+ registerMethod(new ProgramMethod(clazz, method));
+ if (isLibraryDesugared) {
+ return;
+ }
+ }
+ clazz.forEachField(this::registerField);
+ }
+
+ private void registerType(DexType type) {
+ isLibraryDesugared =
+ isLibraryDesugared || type.descriptor.startsWith(jDollarDescriptorPrefix);
+ }
+
+ private void registerField(DexField field) {
+ registerType(field.getHolderType());
+ registerType(field.getType());
+ }
+
+ private void registerMethod(DexMethod method) {
+ for (DexType type : method.getParameters().values) {
+ registerTypeReference(type);
+ }
+ registerTypeReference(method.getReturnType());
+ }
+
+ private void registerField(DexEncodedField field) {
+ registerField(field.getReference());
+ }
+
+ private void registerMethod(ProgramMethod method) {
+ registerMethod(method.getReference());
+ for (DexAnnotation annotation : method.getDefinition().annotations().annotations) {
+ if (annotation.annotation.type == appView.dexItemFactory().annotationThrows) {
+ DexValueArray dexValues = annotation.annotation.elements[0].value.asDexValueArray();
+ for (DexValue dexValType : dexValues.getValues()) {
+ registerType(dexValType.asDexValueType().value);
+ }
+ }
+ }
+ method.registerCodeReferences(this);
+ }
+
+ @Override
+ public void registerInitClass(DexType type) {
+ registerType(type);
+ }
+
+ @Override
+ public void registerInvokeVirtual(DexMethod method) {
+ registerMethod(method);
+ }
+
+ @Override
+ public void registerInvokeDirect(DexMethod method) {
+ registerMethod(method);
+ }
+
+ @Override
+ public void registerInvokeStatic(DexMethod method) {
+ registerMethod(method);
+ }
+
+ @Override
+ public void registerInvokeInterface(DexMethod method) {
+ registerMethod(method);
+ }
+
+ @Override
+ public void registerInvokeStatic(DexMethod method, boolean itf) {
+ registerMethod(method);
+ }
+
+ @Override
+ public void registerInvokeSuper(DexMethod method) {
+ registerMethod(method);
+ }
+
+ @Override
+ public void registerInstanceFieldRead(DexField field) {
+ registerField(field);
+ }
+
+ @Override
+ public void registerInstanceFieldWrite(DexField field) {
+ registerField(field);
+ }
+
+ @Override
+ public void registerNewInstance(DexType type) {
+ registerType(type);
+ }
+
+ @Override
+ public void registerStaticFieldRead(DexField field) {
+ registerField(field);
+ }
+
+ @Override
+ public void registerStaticFieldWrite(DexField field) {
+ registerField(field);
+ }
+
+ @Override
+ public void registerTypeReference(DexType type) {
+ registerType(type);
+ }
+
+ @Override
+ public void registerInstanceOf(DexType type) {
+ registerType(type);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
index 4379da1..44f5990 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
@@ -14,7 +14,7 @@
import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
-import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
+import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.BitSet;
import java.util.Set;
@@ -60,8 +60,8 @@
void setClassInlinerEligibility(DexEncodedMethod method, ClassInlinerEligibilityInfo eligibility);
- void setInstanceInitializerInfo(
- DexEncodedMethod method, InstanceInitializerInfo instanceInitializerInfo);
+ void setInstanceInitializerInfoCollection(
+ DexEncodedMethod method, InstanceInitializerInfoCollection instanceInitializerInfoCollection);
void setInitializerEnablingJavaVmAssertions(DexEncodedMethod 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 0884ab5..84fd203 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
@@ -437,7 +437,7 @@
iface -> {
for (int i = 0; i < extraInterfaceSignatures.size(); i++) {
if (extraInterfaceSignatures.get(i).type() == iface) {
- if (!appView.options().desugarSpecificOptions().allowDesugaredInput) {
+ if (!appView.options().desugarSpecificOptions().allowAllDesugaredInput) {
throw new CompilationError(
"Code has already been library desugared. Interface "
+ iface.getDescriptor()
@@ -476,6 +476,9 @@
ClassInfo superInfo,
MethodSignatures signatures,
Builder<DexEncodedMethod> additionalForwards) {
+ if (clazz.isProgramClass() && appView.isAlreadyLibraryDesugared(clazz.asProgramClass())) {
+ return;
+ }
for (Wrapper<DexMethod> wrapper : signatures.signatures) {
resolveForwardForSignature(
clazz,
@@ -553,7 +556,7 @@
private boolean isRetargetMethod(DexLibraryClass holder, DexEncodedMethod method) {
assert needsLibraryInfo();
- assert holder.type == method.holder();
+ assert holder.type == method.getHolderType();
assert method.isNonPrivateVirtualMethod();
if (method.isFinal()) {
return false;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
index 9356caa..cf85276 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
@@ -107,7 +107,7 @@
public void desugar(IRCode code) {
- if (wrapperSynthesizor.hasSynthesized(code.method().holder())) {
+ if (wrapperSynthesizor.hasSynthesized(code.method().getHolderType())) {
return;
}
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 214c17a..e04505d 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
@@ -446,6 +446,9 @@
}
SortedProgramMethodSet addedMethods = SortedProgramMethodSet.create();
for (DexProgramClass clazz : appView.appInfo().classes()) {
+ if (appView.isAlreadyLibraryDesugared(clazz)) {
+ continue;
+ }
if (clazz.superType == null) {
assert clazz.type == appView.dexItemFactory().objectType : clazz.type.toSourceString();
continue;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
index e2f3271..361ab4b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
@@ -221,7 +221,7 @@
DexTypeList interfaces =
isItf ? new DexTypeList(new DexType[] {wrappingType}) : DexTypeList.empty();
return classKind.create(
- wrapperField.holder(),
+ wrapperField.getHolderType(),
Kind.CF,
new SynthesizedOrigin("Desugared library API Converter", getClass()),
ClassAccessFlags.fromSharedAccessFlags(
@@ -258,24 +258,26 @@
// return v3;
Set<DexMethod> finalMethods = Sets.newIdentityHashSet();
for (DexEncodedMethod dexEncodedMethod : dexMethods) {
- DexClass holderClass = appView.definitionFor(dexEncodedMethod.holder());
+ DexClass holderClass = appView.definitionFor(dexEncodedMethod.getHolderType());
boolean isInterface;
if (holderClass == null) {
assert appView
.options()
.desugaredLibraryConfiguration
.getEmulateLibraryInterface()
- .containsValue(dexEncodedMethod.holder());
+ .containsValue(dexEncodedMethod.getHolderType());
isInterface = true;
} else {
isInterface = holderClass.isInterface();
}
DexMethod methodToInstall =
factory.createMethod(
- wrapperField.holder(), dexEncodedMethod.method.proto, dexEncodedMethod.method.name);
+ wrapperField.getHolderType(),
+ dexEncodedMethod.method.proto,
+ dexEncodedMethod.method.name);
CfCode cfCode;
if (dexEncodedMethod.isFinal()) {
- invalidWrappers.add(wrapperField.holder());
+ invalidWrappers.add(wrapperField.getHolderType());
finalMethods.add(dexEncodedMethod.method);
continue;
} else {
@@ -311,15 +313,15 @@
// return v3;
Set<DexMethod> finalMethods = Sets.newIdentityHashSet();
for (DexEncodedMethod dexEncodedMethod : dexMethods) {
- DexClass holderClass = appView.definitionFor(dexEncodedMethod.holder());
+ DexClass holderClass = appView.definitionFor(dexEncodedMethod.getHolderType());
assert holderClass != null || appView.options().isDesugaredLibraryCompilation();
boolean isInterface = holderClass == null || holderClass.isInterface();
DexMethod methodToInstall =
DesugaredLibraryAPIConverter.methodWithVivifiedTypeInSignature(
- dexEncodedMethod.method, wrapperField.holder(), appView);
+ dexEncodedMethod.method, wrapperField.getHolderType(), appView);
CfCode cfCode;
if (dexEncodedMethod.isFinal()) {
- invalidWrappers.add(wrapperField.holder());
+ invalidWrappers.add(wrapperField.getHolderType());
finalMethods.add(dexEncodedMethod.method);
continue;
} else {
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 ddcb3c7..6c1c172 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
@@ -336,7 +336,8 @@
// 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.holder()), invokedMethod);
+ amendDefaultMethod(
+ appInfo.definitionFor(encodedMethod.getHolderType()), invokedMethod);
instructions.replaceCurrentInstruction(
new InvokeStatic(defaultAsMethodOfCompanionClass(amendedMethod),
invokeSuper.outValue(), invokeSuper.arguments()));
@@ -348,7 +349,7 @@
DexEncodedMethod target =
appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, code.context());
if (target != null && target.isDefaultMethod()) {
- DexClass holder = appView.definitionFor(target.holder());
+ DexClass holder = appView.definitionFor(target.getHolderType());
if (holder.isLibraryClass() && holder.isInterface()) {
instructions.replaceCurrentInstruction(
new InvokeStatic(
@@ -368,7 +369,7 @@
.appInfoForDesugaring()
.lookupSuperTarget(invokeSuper.getInvokedMethod(), code.context());
if (dexEncodedMethod != null) {
- DexClass dexClass = appView.definitionFor(dexEncodedMethod.holder());
+ DexClass dexClass = appView.definitionFor(dexEncodedMethod.getHolderType());
if (dexClass != null && dexClass.isLibraryClass()) {
// Rewriting is required because the super invoke resolves into a missing
// method (method is on desugared library). Find out if it needs to be
@@ -508,8 +509,8 @@
// interfaces.
return null;
}
- if (!singleTarget.isAbstract() && isEmulatedInterface(singleTarget.holder())) {
- return singleTarget.holder();
+ if (!singleTarget.isAbstract() && isEmulatedInterface(singleTarget.getHolderType())) {
+ return singleTarget.getHolderType();
}
return null;
}
@@ -711,8 +712,8 @@
}
emulationMethods.add(
DexEncodedMethod.toEmulateDispatchLibraryMethod(
- method.holder(),
- emulateInterfaceLibraryMethod(method.method, method.holder(), factory),
+ method.getHolderType(),
+ emulateInterfaceLibraryMethod(method.method, method.getHolderType(), factory),
companionMethod,
libraryMethod,
extraDispatchCases,
@@ -1022,6 +1023,9 @@
// results, since each class implementing an emulated interface should also implement the
// rewritten one.
private void transformEmulatedInterfaces(DexProgramClass clazz) {
+ if (appView.isAlreadyLibraryDesugared(clazz)) {
+ return;
+ }
List<GenericSignature.ClassTypeSignature> newInterfaces = new ArrayList<>();
GenericSignature.ClassSignature classSignature = clazz.getClassSignature();
for (int i = 0; i < clazz.interfaces.size(); i++) {
@@ -1074,6 +1078,9 @@
// First we compute all desugaring *without* introducing forwarding methods.
for (DexProgramClass clazz : builder.getProgramClasses()) {
if (shouldProcess(clazz, flavour, false)) {
+ if (appView.isAlreadyLibraryDesugared(clazz)) {
+ continue;
+ }
processor.processClass(clazz);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriterFixup.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriterFixup.java
index 995f693..ed8077a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriterFixup.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriterFixup.java
@@ -53,7 +53,8 @@
return null;
}
// Map default methods to their companion methods.
- DexMethod mappedMethod = graphLens.getExtraOriginalMethodSignatures().inverse().get(method);
+ DexMethod mappedMethod =
+ graphLens.getExtraOriginalMethodSignatures().getRepresentativeKey(method);
if (mappedMethod != null) {
return mappedMethod;
}
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 02388be..88e1200 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
@@ -48,8 +48,9 @@
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;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableList;
import java.util.ArrayDeque;
import java.util.ArrayList;
@@ -451,7 +452,7 @@
} else {
assert code.isCfCode();
for (CfInstruction insn : code.asCfCode().getInstructions()) {
- if (insn instanceof CfInvoke && ((CfInvoke) insn).isInvokeSuper(method.holder())) {
+ if (insn instanceof CfInvoke && ((CfInvoke) insn).isInvokeSuper(method.getHolderType())) {
return false;
}
}
@@ -514,15 +515,16 @@
// are to static companion methods.
public static class InterfaceProcessorNestedGraphLens extends NestedGraphLens {
- private BiMap<DexMethod, DexMethod> extraOriginalMethodSignatures;
+ private BidirectionalManyToManyRepresentativeMap<DexMethod, DexMethod>
+ extraOriginalMethodSignatures;
public InterfaceProcessorNestedGraphLens(
Map<DexType, DexType> typeMap,
Map<DexMethod, DexMethod> methodMap,
Map<DexField, DexField> fieldMap,
BiMap<DexField, DexField> originalFieldSignatures,
- BiMap<DexMethod, DexMethod> originalMethodSignatures,
- BiMap<DexMethod, DexMethod> extraOriginalMethodSignatures,
+ BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures,
+ BidirectionalOneToOneHashMap<DexMethod, DexMethod> extraOriginalMethodSignatures,
GraphLens previousLens,
DexItemFactory dexItemFactory) {
super(
@@ -551,12 +553,13 @@
}
public void toggleMappingToExtraMethods() {
- BiMap<DexMethod, DexMethod> tmp = originalMethodSignatures;
+ BidirectionalManyToManyRepresentativeMap<DexMethod, DexMethod> tmp = originalMethodSignatures;
this.originalMethodSignatures = extraOriginalMethodSignatures;
this.extraOriginalMethodSignatures = tmp;
}
- public BiMap<DexMethod, DexMethod> getExtraOriginalMethodSignatures() {
+ public BidirectionalManyToManyRepresentativeMap<DexMethod, DexMethod>
+ getExtraOriginalMethodSignatures() {
return extraOriginalMethodSignatures;
}
@@ -577,16 +580,14 @@
@Override
protected DexMethod internalGetPreviousMethodSignature(DexMethod method) {
- return extraOriginalMethodSignatures.getOrDefault(
- method, originalMethodSignatures.getOrDefault(method, method));
+ return extraOriginalMethodSignatures.getRepresentativeValueOrDefault(
+ method, originalMethodSignatures.getRepresentativeValueOrDefault(method, method));
}
@Override
protected DexMethod internalGetNextMethodSignature(DexMethod method) {
- return originalMethodSignatures
- .inverse()
- .getOrDefault(
- method, extraOriginalMethodSignatures.inverse().getOrDefault(method, method));
+ return originalMethodSignatures.getRepresentativeKeyOrDefault(
+ method, extraOriginalMethodSignatures.getRepresentativeKeyOrDefault(method, method));
}
@Override
@@ -600,7 +601,8 @@
public static class Builder extends NestedGraphLens.Builder {
- private final BiMap<DexMethod, DexMethod> extraOriginalMethodSignatures = HashBiMap.create();
+ private final BidirectionalOneToOneHashMap<DexMethod, DexMethod>
+ extraOriginalMethodSignatures = new BidirectionalOneToOneHashMap<>();
public void recordCodeMovedToCompanionClass(DexMethod from, DexMethod to) {
assert from != to;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
index 45a8824..f661256 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
@@ -96,7 +96,7 @@
DexEncodedMethod targetMethod = context == null ? null : lookupTargetMethod(appInfo, context);
if (targetMethod != null) {
targetAccessFlags = targetMethod.accessFlags.copy();
- targetHolder = targetMethod.holder();
+ targetHolder = targetMethod.getHolderType();
} else {
targetAccessFlags = null;
targetHolder = null;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index 9688ae8..e4d6f65 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -42,6 +42,7 @@
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
import com.google.common.base.Suppliers;
import com.google.common.collect.BiMap;
@@ -440,7 +441,7 @@
Map<DexMethod, DexMethod> methodMap,
Map<DexField, DexField> fieldMap,
BiMap<DexField, DexField> originalFieldSignatures,
- BiMap<DexMethod, DexMethod> originalMethodSignatures,
+ BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures,
GraphLens previousLens,
DexItemFactory dexItemFactory) {
super(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
index c0a1269..6b6e4b0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
@@ -247,7 +247,7 @@
}
private DexMethod computeFieldBridge(DexEncodedField field, boolean isGet) {
- DexType holderType = field.holder();
+ DexType holderType = field.getHolderType();
DexType fieldType = field.field.type;
int bridgeParameterCount =
BooleanUtils.intValue(!field.isStatic()) + BooleanUtils.intValue(!isGet);
@@ -268,10 +268,10 @@
boolean invokeRequiresRewriting(DexEncodedMethod method, DexClassAndMethod context) {
assert method != null;
// Rewrite only when targeting other nest members private fields.
- if (!method.accessFlags.isPrivate() || method.holder() == context.getHolderType()) {
+ if (!method.accessFlags.isPrivate() || method.getHolderType() == context.getHolderType()) {
return false;
}
- DexClass methodHolder = definitionFor(method.holder());
+ DexClass methodHolder = definitionFor(method.getHolderType());
assert methodHolder != null; // from encodedMethod
return methodHolder.getNestHost() == context.getHolder().getNestHost();
}
@@ -279,10 +279,10 @@
boolean fieldAccessRequiresRewriting(DexEncodedField field, DexClassAndMethod context) {
assert field != null;
// Rewrite only when targeting other nest members private fields.
- if (!field.accessFlags.isPrivate() || field.holder() == context.getHolderType()) {
+ if (!field.accessFlags.isPrivate() || field.getHolderType() == context.getHolderType()) {
return false;
}
- DexClass fieldHolder = definitionFor(field.holder());
+ DexClass fieldHolder = definitionFor(field.getHolderType());
assert fieldHolder != null; // from encodedField
return fieldHolder.getNestHost() == context.getHolder().getNestHost();
}
@@ -304,7 +304,7 @@
}
DexMethod ensureFieldAccessBridge(DexEncodedField field, boolean isGet) {
- DexClass holder = definitionFor(field.holder());
+ DexClass holder = definitionFor(field.getHolderType());
assert holder != null;
DexMethod bridgeMethod = computeFieldBridge(field, isGet);
if (holderRequiresBridge(holder)) {
@@ -325,7 +325,7 @@
DexMethod ensureInvokeBridge(DexEncodedMethod method) {
// We add bridges only when targeting other nest members.
- DexClass holder = definitionFor(method.holder());
+ DexClass holder = definitionFor(method.getHolderType());
assert holder != null;
DexMethod bridgeMethod;
if (method.isInstanceInitializer()) {
@@ -517,7 +517,7 @@
}
public DexType getHolder() {
- return field.holder();
+ return field.getHolderType();
}
public DexField getField() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java b/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java
index 7f99055..8889ec2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
import com.android.tools.r8.graph.RewrittenPrototypeDescription;
import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.utils.collections.BidirectionalManyToManyRepresentativeMap;
import com.google.common.collect.ImmutableMap;
import java.util.IdentityHashMap;
import java.util.Map;
@@ -35,7 +36,7 @@
methodMap,
ImmutableMap.of(),
null,
- null,
+ BidirectionalManyToManyRepresentativeMap.empty(),
previousLens,
appView.dexItemFactory());
// No concurrent maps here, we do not want synchronization overhead.
@@ -110,7 +111,6 @@
@Override
public MethodLookupResult internalDescribeLookupMethod(
MethodLookupResult previous, DexMethod context) {
- assert originalMethodSignatures == null;
DexMethod bridge = methodMap.get(previous.getReference());
if (bridge == null) {
return previous;
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 23cd37d..d41fd32 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
@@ -102,7 +102,7 @@
new InvokeStatic(twrCloseResourceMethod, null, invoke.inValues()));
// Mark as a class referencing utility class.
- referencingClasses.add(appInfo.definitionFor(code.method().holder()).asProgramClass());
+ referencingClasses.add(appInfo.definitionFor(code.method().getHolderType()).asProgramClass());
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
index e4b35af..8e78a50 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
@@ -109,7 +109,7 @@
}
private AssertionTransformation getTransformationForMethod(DexEncodedMethod method) {
- return getTransformationForType(method.holder());
+ return getTransformationForType(method.getHolderType());
}
private AssertionTransformation getTransformationForType(DexType type) {
@@ -320,7 +320,7 @@
if (method.isClassInitializer()) {
clinit = method;
} else {
- DexClass clazz = appView.definitionFor(method.holder());
+ DexClass clazz = appView.definitionFor(method.getHolderType());
if (clazz == null) {
return;
}
@@ -337,7 +337,7 @@
if (current.isInvokeMethod()) {
InvokeMethod invoke = current.asInvokeMethod();
if (invoke.getInvokedMethod() == dexItemFactory.classMethods.desiredAssertionStatus) {
- if (method.holder() == dexItemFactory.kotlin.assertions.type) {
+ if (method.getHolderType() == dexItemFactory.kotlin.assertions.type) {
rewriteKotlinAssertionEnable(code, transformation, iterator, invoke);
} else {
iterator.replaceCurrentInstruction(code.createIntConstant(0, current.getLocalInfo()));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index 8a8d2d4..6e7d98b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -265,6 +265,14 @@
}
if (inliner.neverInline(invoke, resolutionResult, singleTarget, whyAreYouNotInliningReporter)) {
+ if (singleTarget.getDefinition().getOptimizationInfo().forceInline()) {
+ throw new Unreachable(
+ "Unexpected attempt to force inline method `"
+ + singleTarget.toSourceString()
+ + "` in `"
+ + context.toSourceString()
+ + "`.");
+ }
return null;
}
@@ -435,7 +443,7 @@
// Allow inlining a constructor into a constructor of the same class, as the constructor code
// is expected to adhere to the VM specification.
DexType callerMethodHolder = method.getHolderType();
- DexType calleeMethodHolder = inlinee.method().holder();
+ DexType calleeMethodHolder = inlinee.method().getHolderType();
// Calling a constructor on the same class from a constructor can always be inlined.
if (method.getDefinition().isInstanceInitializer()
&& callerMethodHolder == calleeMethodHolder) {
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 3abbf9b..b26bb34 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
@@ -7,7 +7,6 @@
import static com.google.common.base.Predicates.not;
import com.android.tools.r8.androidapi.AvailableApiExceptions;
-import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AccessControl;
import com.android.tools.r8.graph.AccessFlags;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -125,11 +124,6 @@
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod singleTargetReference = singleTarget.getReference();
- if (singleTarget.getDefinition().getOptimizationInfo().forceInline()
- && appInfo.isNeverInlineMethod(singleTargetReference)) {
- throw new Unreachable();
- }
-
if (appInfo.isPinned(singleTargetReference)) {
whyAreYouNotInliningReporter.reportPinned();
return true;
@@ -151,7 +145,7 @@
if (appInfo.noSideEffects.containsKey(invoke.getInvokedMethod())
|| appInfo.noSideEffects.containsKey(resolutionResult.getResolvedMethod().getReference())
|| appInfo.noSideEffects.containsKey(singleTargetReference)) {
- return true;
+ return !singleTarget.getDefinition().getOptimizationInfo().forceInline();
}
return false;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
index 1941743..976e869 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
@@ -211,7 +211,7 @@
resolutionResult, context, ResolutionResult::lookupInvokeStaticTarget);
if (!allowStaticInterfaceMethodCalls && target != null) {
// See b/120121170.
- DexClass methodClass = appView.definitionFor(graphLens.lookupType(target.holder()));
+ DexClass methodClass = appView.definitionFor(graphLens.lookupType(target.getHolderType()));
if (methodClass != null && methodClass.isInterface() && target.hasCode()) {
return ConstraintWithTarget.NEVER;
}
@@ -243,7 +243,7 @@
DexEncodedMethod alternativeDexEncodedMethod =
lookup.apply(resolutionResult, superContext, appView.appInfo());
if (alternativeDexEncodedMethod != null
- && alternativeDexEncodedMethod.holder() == superContext.type) {
+ && alternativeDexEncodedMethod.getHolderType() == superContext.type) {
return alternativeDexEncodedMethod;
}
return null;
@@ -370,7 +370,7 @@
// This will fail at runtime.
return ConstraintWithTarget.NEVER;
}
- DexType resolvedHolder = graphLens.lookupType(resolvedMember.holder());
+ DexType resolvedHolder = graphLens.lookupType(resolvedMember.getHolderType());
assert initialResolutionHolder != null;
ConstraintWithTarget memberConstraintWithTarget =
ConstraintWithTarget.deriveConstraint(
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 91f7642..8ba58f1 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
@@ -311,7 +311,11 @@
// Insert the definition of the replacement.
replacement.setPosition(position);
if (block.hasCatchHandlers()) {
- iterator.split(code, blocks).listIterator(code).add(replacement);
+ BasicBlock splitBlock = iterator.split(code, blocks, false);
+ splitBlock.listIterator(code).add(replacement);
+ assert !block.hasCatchHandlers();
+ assert splitBlock.hasCatchHandlers();
+ block.copyCatchHandlers(code, blocks, splitBlock, appView.options());
} else {
iterator.add(replacement);
}
@@ -403,13 +407,17 @@
} else {
assert current.isStaticGet();
replaceInstructionByInitClassIfPossible(
- current, target.holder(), code, iterator, context);
+ current, target.getHolderType(), code, iterator, context);
}
// Insert the definition of the replacement.
replacement.setPosition(position);
if (block.hasCatchHandlers()) {
- iterator.split(code, blocks).listIterator(code).add(replacement);
+ BasicBlock splitBlock = iterator.split(code, blocks, false);
+ splitBlock.listIterator(code).add(replacement);
+ assert !block.hasCatchHandlers();
+ assert splitBlock.hasCatchHandlers();
+ block.copyCatchHandlers(code, blocks, splitBlock, appView.options());
} else {
iterator.add(replacement);
}
@@ -502,7 +510,7 @@
}
replaceInstructionByInitClassIfPossible(
- current, field.holder(), code, iterator, code.context());
+ current, field.getHolderType(), code, iterator, code.context());
}
/**
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
index 868bcc5..3745120 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
@@ -366,7 +366,7 @@
}
InstanceInitializerInfo instanceInitializerInfo =
- singleTarget.getDefinition().getOptimizationInfo().getInstanceInitializerInfo();
+ singleTarget.getDefinition().getOptimizationInfo().getInstanceInitializerInfo(invoke);
if (instanceInitializerInfo.mayHaveOtherSideEffectsThanInstanceFieldAssignments()) {
killAllNonFinalActiveFields();
}
@@ -419,7 +419,7 @@
// that we are conservative.
activeState.removeNonFinalInstanceFields(field);
} else if (instruction.isStaticPut()) {
- if (field.holder != code.method().holder()) {
+ if (field.holder != code.method().getHolderType()) {
// Accessing a static field on a different object could cause <clinit> to run which
// could modify any static field on any other object.
activeState.clearNonFinalStaticFields();
@@ -427,7 +427,7 @@
activeState.removeNonFinalStaticField(field);
}
} else if (instruction.isStaticGet()) {
- if (field.holder != code.method().holder()) {
+ if (field.holder != code.method().getHolderType()) {
// Accessing a static field on a different object could cause <clinit> to run which
// could modify any static field on any other object.
activeState.clearNonFinalStaticFields();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
index c11c8bf..48002cc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
@@ -28,9 +28,9 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
@@ -54,7 +54,7 @@
private final Map<DexMethod, ArgumentInfoCollection> removedArgumentsInfoPerMethod;
UninstantiatedTypeOptimizationGraphLens(
- BiMap<DexMethod, DexMethod> methodMap,
+ BidirectionalOneToOneHashMap<DexMethod, DexMethod> methodMap,
Map<DexMethod, ArgumentInfoCollection> removedArgumentsInfoPerMethod,
AppView<?> appView) {
super(
@@ -62,7 +62,7 @@
methodMap,
ImmutableMap.of(),
null,
- methodMap.inverse(),
+ methodMap.getInverseOneToOneMap(),
appView.graphLens(),
appView.dexItemFactory());
this.appView = appView;
@@ -129,7 +129,8 @@
}
Map<Wrapper<DexMethod>, Set<DexType>> changedVirtualMethods = new HashMap<>();
- BiMap<DexMethod, DexMethod> methodMapping = HashBiMap.create();
+ BidirectionalOneToOneHashMap<DexMethod, DexMethod> methodMapping =
+ new BidirectionalOneToOneHashMap<>();
Map<DexMethod, ArgumentInfoCollection> removedArgumentsInfoPerMethod = new IdentityHashMap<>();
TopDownClassHierarchyTraversal.forProgramClasses(appView)
@@ -139,7 +140,7 @@
processClass(
clazz,
changedVirtualMethods,
- methodMapping,
+ methodMapping.getForwardBacking(),
methodPoolCollection,
removedArgumentsInfoPerMethod));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
index 6270034..16fb558 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
@@ -26,9 +26,9 @@
import com.android.tools.r8.utils.SymbolGenerationUtils.MixedCasing;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Streams;
@@ -48,7 +48,8 @@
private final AppView<AppInfoWithLiveness> appView;
private final MethodPoolCollection methodPoolCollection;
- private final BiMap<DexMethod, DexMethod> methodMapping = HashBiMap.create();
+ private final BidirectionalOneToOneHashMap<DexMethod, DexMethod> methodMapping =
+ new BidirectionalOneToOneHashMap<>();
private final Map<DexMethod, ArgumentInfoCollection> removedArguments = new IdentityHashMap<>();
public static class UnusedArgumentsGraphLens extends NestedGraphLens {
@@ -60,7 +61,7 @@
Map<DexMethod, DexMethod> methodMap,
Map<DexField, DexField> fieldMap,
BiMap<DexField, DexField> originalFieldSignatures,
- BiMap<DexMethod, DexMethod> originalMethodSignatures,
+ BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures,
GraphLens previousLens,
DexItemFactory dexItemFactory,
Map<DexMethod, ArgumentInfoCollection> removedArguments) {
@@ -111,7 +112,7 @@
methodMapping,
ImmutableMap.of(),
ImmutableBiMap.of(),
- methodMapping.inverse(),
+ methodMapping.getInverseOneToOneMap(),
appView.graphLens(),
appView.dexItemFactory(),
removedArguments);
@@ -153,7 +154,8 @@
// Constructors must be named `<init>`.
return null;
}
- newSignature = appView.dexItemFactory().createMethod(method.holder(), newProto, newName);
+ newSignature =
+ appView.dexItemFactory().createMethod(method.getHolderType(), newProto, newName);
count++;
} while (!isMethodSignatureAvailable(newSignature));
return newSignature;
@@ -193,7 +195,8 @@
// Constructors must be named `<init>`.
return null;
}
- newSignature = appView.dexItemFactory().createMethod(method.holder(), newProto, newName);
+ newSignature =
+ appView.dexItemFactory().createMethod(method.getHolderType(), newProto, newName);
count++;
} while (methodPool.hasSeen(equivalence.wrap(newSignature)));
return newSignature;
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 550a0e1..e080808 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
@@ -826,7 +826,7 @@
// Check that the `eligibleInstance` does not escape via the constructor.
InstanceInitializerInfo instanceInitializerInfo =
- singleTarget.getDefinition().getOptimizationInfo().getInstanceInitializerInfo();
+ singleTarget.getDefinition().getOptimizationInfo().getInstanceInitializerInfo(invoke);
if (instanceInitializerInfo.receiverMayEscapeOutsideConstructorChain()) {
return null;
}
@@ -856,7 +856,11 @@
NopWhyAreYouNotInliningReporter.getInstance())) {
return null;
}
- parent = encodedParentMethod.getOptimizationInfo().getInstanceInitializerInfo().getParent();
+ parent =
+ encodedParentMethod
+ .getOptimizationInfo()
+ .getContextInsensitiveInstanceInitializerInfo()
+ .getParent();
}
return new InliningInfo(singleTarget, eligibleClass.type);
@@ -1317,7 +1321,7 @@
return false;
}
InstanceInitializerInfo initializerInfo =
- definition.getOptimizationInfo().getInstanceInitializerInfo();
+ definition.getOptimizationInfo().getContextInsensitiveInstanceInitializerInfo();
return initializerInfo.receiverNeverEscapesOutsideConstructorChain();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index b46805b..2d46582 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -604,7 +604,7 @@
constraint = Constraint.NEVER;
return;
}
- DexType resolvedHolder = target.holder();
+ DexType resolvedHolder = target.getHolderType();
if (initialResolutionHolder == null) {
constraint = Constraint.NEVER;
return;
@@ -698,7 +698,7 @@
hasInstanceInitializer = true;
if (directMethod
.getOptimizationInfo()
- .getInstanceInitializerInfo()
+ .getContextInsensitiveInstanceInitializerInfo()
.mayHaveOtherSideEffectsThanInstanceFieldAssignments()) {
markEnumAsUnboxable(Reason.INVALID_INIT, enumClass);
break;
@@ -714,6 +714,7 @@
}
if (enumClass.classInitializationMayHaveSideEffects(appView)) {
+ enumClass.classInitializationMayHaveSideEffects(appView);
markEnumAsUnboxable(Reason.INVALID_CLINIT, enumClass);
}
});
@@ -739,7 +740,8 @@
DexClass dexClass = singleTarget.getHolder();
if (dexClass.isProgramClass()) {
if (dexClass.isEnum() && singleTarget.getDefinition().isInstanceInitializer()) {
- if (code.method().holder() == dexClass.type && code.method().isClassInitializer()) {
+ if (code.method().getHolderType() == dexClass.type
+ && code.method().isClassInitializer()) {
// The enum instance initializer is allowed to be called only from the enum clinit.
return Reason.ELIGIBLE;
} else {
@@ -807,7 +809,8 @@
return Reason.ELIGIBLE;
} else if (singleTargetReference == factory.enumMembers.constructor) {
// Enum constructor call is allowed only if called from an enum initializer.
- if (code.method().isInstanceInitializer() && code.method().holder() == enumClass.type) {
+ if (code.method().isInstanceInitializer()
+ && code.method().getHolderType() == enumClass.type) {
return Reason.ELIGIBLE;
}
}
@@ -823,7 +826,8 @@
if (field == null) {
return Reason.INVALID_FIELD_PUT;
}
- DexProgramClass dexClass = appView.programDefinitionFor(field.holder(), code.context());
+ DexProgramClass dexClass =
+ appView.programDefinitionFor(field.getHolderType(), code.context());
if (dexClass == null) {
return Reason.INVALID_FIELD_PUT;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
index d99bf3e..b6660fc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.graph.RewrittenPrototypeDescription;
import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableMap;
@@ -30,7 +31,7 @@
Map<DexMethod, DexMethod> methodMap,
Map<DexField, DexField> fieldMap,
BiMap<DexField, DexField> originalFieldSignatures,
- BiMap<DexMethod, DexMethod> originalMethodSignatures,
+ BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures,
GraphLens previousLens,
DexItemFactory dexItemFactory,
Map<DexMethod, RewrittenPrototypeDescription> prototypeChangesPerMethod,
@@ -76,7 +77,8 @@
protected final Map<DexType, DexType> typeMap = new IdentityHashMap<>();
protected final BiMap<DexField, DexField> originalFieldSignatures = HashBiMap.create();
- protected final BiMap<DexMethod, DexMethod> originalMethodSignatures = HashBiMap.create();
+ protected final BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures =
+ new BidirectionalOneToOneHashMap<>();
private Map<DexMethod, RewrittenPrototypeDescription> prototypeChangesPerMethod =
new IdentityHashMap<>();
@@ -148,7 +150,7 @@
}
return new EnumUnboxingLens(
typeMap,
- originalMethodSignatures.inverse(),
+ originalMethodSignatures.getInverseOneToOneMap(),
originalFieldSignatures.inverse(),
originalFieldSignatures,
originalMethodSignatures,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index bfb520d..c456bd0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -579,7 +579,8 @@
// We compute encodedMembers by types.
for (T encodedMember : encodedMembers) {
List<T> members =
- encodedMembersMap.computeIfAbsent(encodedMember.holder(), ignored -> new ArrayList<>());
+ encodedMembersMap.computeIfAbsent(
+ encodedMember.getHolderType(), ignored -> new ArrayList<>());
members.add(encodedMember);
}
// We make the order deterministic.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index 1dd8e0c..92ce5a1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -107,7 +107,7 @@
+ method.name.toString());
DexProto proto = encodedMethod.isStatic() ? method.proto : factory.prependHolderToProto(method);
DexMethod newMethod = factory.createMethod(newHolder, fixupProto(proto), newMethodName);
- assert appView.definitionFor(encodedMethod.holder()).lookupMethod(newMethod) == null;
+ assert appView.definitionFor(encodedMethod.getHolderType()).lookupMethod(newMethod) == null;
lensBuilder.move(method, newMethod, encodedMethod.isStatic(), true);
encodedMethod.accessFlags.promoteToPublic();
encodedMethod.accessFlags.promoteToStatic();
@@ -145,7 +145,7 @@
}
private DexMethod ensureUniqueMethod(DexEncodedMethod encodedMethod, DexMethod newMethod) {
- DexClass holder = appView.definitionFor(encodedMethod.holder());
+ DexClass holder = appView.definitionFor(encodedMethod.getHolderType());
assert holder != null;
if (newMethod.isInstanceInitializer(appView.dexItemFactory())) {
newMethod =
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
index f1c2bdd..033a233 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
@@ -228,16 +228,9 @@
continue;
}
- Int2IntMap ordinalToTargetMap = new Int2IntArrayMap(switchInsn.numberOfKeys());
- for (int i = 0; i < switchInsn.numberOfKeys(); i++) {
- assert switchInsn.targetBlockIndices()[i] != switchInsn.getFallthroughBlockIndex();
- DexField field = info.indexMap.get(switchInsn.getKey(i));
- EnumValueInfo valueInfo = info.valueInfoMap.getEnumValueInfo(field);
- if (valueInfo != null) {
- ordinalToTargetMap.put(valueInfo.ordinal, switchInsn.targetBlockIndices()[i]);
- } else {
- // The switch map refers to a field on the enum that does not exist in this compilation.
- }
+ Int2IntMap ordinalToTargetMap = computeOrdinalToTargetMap(switchInsn, info);
+ if (ordinalToTargetMap == null) {
+ continue;
}
int fallthroughBlockIndex = switchInsn.getFallthroughBlockIndex();
@@ -322,6 +315,24 @@
}
}
+ private Int2IntMap computeOrdinalToTargetMap(IntSwitch switchInsn, EnumSwitchInfo info) {
+ Int2IntMap ordinalToTargetMap = new Int2IntArrayMap(switchInsn.numberOfKeys());
+ for (int i = 0; i < switchInsn.numberOfKeys(); i++) {
+ assert switchInsn.targetBlockIndices()[i] != switchInsn.getFallthroughBlockIndex();
+ DexField field = info.indexMap.get(switchInsn.getKey(i));
+ EnumValueInfo valueInfo = info.valueInfoMap.getEnumValueInfo(field);
+ if (valueInfo != null) {
+ if (appView.appInfo().isPinned(field)) {
+ return null;
+ }
+ ordinalToTargetMap.put(valueInfo.ordinal, switchInsn.targetBlockIndices()[i]);
+ } else {
+ // The switch map refers to a field on the enum that does not exist in this compilation.
+ }
+ }
+ return ordinalToTargetMap;
+ }
+
private static final class EnumSwitchInfo {
final DexType enumClass;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
index 3d20843..33b7242 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
@@ -80,7 +80,8 @@
int size = method.method.getArity() + argOffset;
TypeElement[] staticTypes = new TypeElement[size];
if (!method.isStatic()) {
- staticTypes[0] = TypeElement.fromDexType(method.holder(), definitelyNotNull(), appView);
+ staticTypes[0] =
+ TypeElement.fromDexType(method.getHolderType(), definitelyNotNull(), appView);
}
for (int i = 0; i < method.method.getArity(); i++) {
staticTypes[i + argOffset] =
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
index 0e3e339..b2b10ae 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
+import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
@@ -82,7 +83,12 @@
}
@Override
- public InstanceInitializerInfo getInstanceInitializerInfo() {
+ public InstanceInitializerInfo getContextInsensitiveInstanceInitializerInfo() {
+ return DefaultInstanceInitializerInfo.getInstance();
+ }
+
+ @Override
+ public InstanceInitializerInfo getInstanceInitializerInfo(InvokeDirect invoke) {
return DefaultInstanceInitializerInfo.getInstance();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
index f919a1e..cf7c4ee 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
@@ -68,7 +69,9 @@
public abstract Set<DexType> getInitializedClassesOnNormalExit();
- public abstract InstanceInitializerInfo getInstanceInitializerInfo();
+ public abstract InstanceInitializerInfo getContextInsensitiveInstanceInitializerInfo();
+
+ public abstract InstanceInitializerInfo getInstanceInitializerInfo(InvokeDirect invoke);
public abstract boolean isInitializerEnablingJavaVmAssertions();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index be88541..09c4a5d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -23,6 +23,7 @@
import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_GET;
import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_OF;
import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_PUT;
+import static com.android.tools.r8.ir.code.Opcodes.INT_SWITCH;
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_NEW_ARRAY;
@@ -38,6 +39,7 @@
import static com.android.tools.r8.ir.code.Opcodes.SHL;
import static com.android.tools.r8.ir.code.Opcodes.SHR;
import static com.android.tools.r8.ir.code.Opcodes.STATIC_GET;
+import static com.android.tools.r8.ir.code.Opcodes.STRING_SWITCH;
import static com.android.tools.r8.ir.code.Opcodes.SUB;
import static com.android.tools.r8.ir.code.Opcodes.THROW;
import static com.android.tools.r8.ir.code.Opcodes.USHR;
@@ -91,8 +93,8 @@
import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsageBuilder;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeAnalyzer;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
-import com.android.tools.r8.ir.optimize.info.initializer.DefaultInstanceInitializerInfo;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
+import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
import com.android.tools.r8.ir.optimize.info.initializer.NonTrivialInstanceInitializerInfo;
import com.android.tools.r8.ir.optimize.typechecks.CheckCastAndInstanceOfMethodSpecialization;
import com.android.tools.r8.kotlin.Kotlin;
@@ -435,11 +437,8 @@
NonTrivialInstanceInitializerInfo.Builder builder =
NonTrivialInstanceInitializerInfo.builder(instanceFieldInitializationInfos);
InstanceInitializerInfo instanceInitializerInfo = analyzeInstanceInitializer(code, builder);
- feedback.setInstanceInitializerInfo(
- method,
- instanceInitializerInfo != null
- ? instanceInitializerInfo
- : DefaultInstanceInitializerInfo.getInstance());
+ feedback.setInstanceInitializerInfoCollection(
+ method, InstanceInitializerInfoCollection.of(instanceInitializerInfo));
}
// This method defines trivial instance initializer as follows:
@@ -484,6 +483,8 @@
break;
case IF:
+ case INT_SWITCH:
+ case STRING_SWITCH:
builder.setInstanceFieldInitializationMayDependOnEnvironment();
break;
@@ -573,7 +574,7 @@
return null;
}
if (singleTarget.isInstanceInitializer() && invoke.getReceiver() == receiver) {
- if (builder.hasParent()) {
+ if (builder.hasParent() && builder.getParent() != singleTarget.getReference()) {
return null;
}
// java.lang.Enum.<init>() and java.lang.Object.<init>() are considered trivial.
@@ -582,7 +583,8 @@
builder.setParent(invokedMethod);
break;
}
- builder.merge(singleTarget.getOptimizationInfo().getInstanceInitializerInfo());
+ builder.merge(
+ singleTarget.getOptimizationInfo().getInstanceInitializerInfo(invoke));
for (int i = 1; i < invoke.arguments().size(); i++) {
Value argument =
invoke.arguments().get(i).getAliasedValue(aliasesThroughAssumeAndCheckCasts);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
index 07b7ce1..50cb793 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
@@ -14,7 +14,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
-import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
+import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.AppInfoWithLivenessModifier;
import com.android.tools.r8.utils.IteratorUtils;
@@ -254,10 +254,11 @@
}
@Override
- public synchronized void setInstanceInitializerInfo(
- DexEncodedMethod method, InstanceInitializerInfo instanceInitializerInfo) {
+ public synchronized void setInstanceInitializerInfoCollection(
+ DexEncodedMethod method,
+ InstanceInitializerInfoCollection instanceInitializerInfoCollection) {
getMethodOptimizationInfoForUpdating(method)
- .setInstanceInitializerInfo(instanceInitializerInfo);
+ .setInstanceInitializerInfoCollection(instanceInitializerInfoCollection);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
index 6a28331..12fda15 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
@@ -14,7 +14,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
-import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
+import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.BitSet;
import java.util.Set;
@@ -115,8 +115,9 @@
DexEncodedMethod method, ClassInlinerEligibilityInfo eligibility) {}
@Override
- public void setInstanceInitializerInfo(
- DexEncodedMethod method, InstanceInitializerInfo instanceInitializerInfo) {}
+ public void setInstanceInitializerInfoCollection(
+ DexEncodedMethod method,
+ InstanceInitializerInfoCollection instanceInitializerInfoCollection) {}
@Override
public void setInitializerEnablingJavaVmAssertions(DexEncodedMethod method) {}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
index 9a3bb51..5840ea6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -14,7 +14,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
-import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
+import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.BitSet;
import java.util.Set;
@@ -165,9 +165,12 @@
}
@Override
- public void setInstanceInitializerInfo(
- DexEncodedMethod method, InstanceInitializerInfo instanceInitializerInfo) {
- method.getMutableOptimizationInfo().setInstanceInitializerInfo(instanceInitializerInfo);
+ public void setInstanceInitializerInfoCollection(
+ DexEncodedMethod method,
+ InstanceInitializerInfoCollection instanceInitializerInfoCollection) {
+ method
+ .getMutableOptimizationInfo()
+ .setInstanceInitializerInfoCollection(instanceInitializerInfoCollection);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
index 6d9fc9f..877c72d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
@@ -12,11 +12,12 @@
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
+import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
-import com.android.tools.r8.ir.optimize.info.initializer.DefaultInstanceInitializerInfo;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
+import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.BooleanUtils;
import java.util.BitSet;
@@ -39,8 +40,8 @@
private BridgeInfo bridgeInfo = null;
private ClassInlinerEligibilityInfo classInlinerEligibility =
DefaultMethodOptimizationInfo.UNKNOWN_CLASS_INLINER_ELIGIBILITY;
- private InstanceInitializerInfo instanceInitializerInfo =
- DefaultInstanceInitializerInfo.getInstance();
+ private InstanceInitializerInfoCollection instanceInitializerInfoCollection =
+ InstanceInitializerInfoCollection.empty();
private ParameterUsagesInfo parametersUsages =
DefaultMethodOptimizationInfo.UNKNOWN_PARAMETER_USAGE_INFO;
// Stores information about nullability hint per parameter. If set, that means, the method
@@ -138,7 +139,7 @@
inlining = template.inlining;
bridgeInfo = template.bridgeInfo;
classInlinerEligibility = template.classInlinerEligibility;
- instanceInitializerInfo = template.instanceInitializerInfo;
+ instanceInitializerInfoCollection = template.instanceInitializerInfoCollection;
parametersUsages = template.parametersUsages;
nonNullParamOrThrow = template.nonNullParamOrThrow;
nonNullParamOnNormalExits = template.nonNullParamOnNormalExits;
@@ -172,9 +173,8 @@
public UpdatableMethodOptimizationInfo fixupInstanceInitializerInfo(
AppView<AppInfoWithLiveness> appView, GraphLens lens) {
- if (instanceInitializerInfo != null) {
- instanceInitializerInfo = instanceInitializerInfo.rewrittenWithLens(appView, lens);
- }
+ instanceInitializerInfoCollection =
+ instanceInitializerInfoCollection.rewrittenWithLens(appView, lens);
return this;
}
@@ -248,8 +248,13 @@
}
@Override
- public InstanceInitializerInfo getInstanceInitializerInfo() {
- return instanceInitializerInfo;
+ public InstanceInitializerInfo getContextInsensitiveInstanceInitializerInfo() {
+ return instanceInitializerInfoCollection.getContextInsensitive();
+ }
+
+ @Override
+ public InstanceInitializerInfo getInstanceInitializerInfo(InvokeDirect invoke) {
+ return instanceInitializerInfoCollection.get(invoke);
}
@Override
@@ -371,8 +376,9 @@
this.classInlinerEligibility = eligibility;
}
- void setInstanceInitializerInfo(InstanceInitializerInfo instanceInitializerInfo) {
- this.instanceInitializerInfo = instanceInitializerInfo;
+ void setInstanceInitializerInfoCollection(
+ InstanceInitializerInfoCollection instanceInitializerInfoCollection) {
+ this.instanceInitializerInfoCollection = instanceInitializerInfoCollection;
}
void setInitializerEnablingJavaAssertions() {
@@ -520,7 +526,7 @@
// classInlinerEligibility: chances are the method is not an instance method anymore.
classInlinerEligibility = DefaultMethodOptimizationInfo.UNKNOWN_CLASS_INLINER_ELIGIBILITY;
// initializerInfo: the computed initializer info may become invalid.
- instanceInitializerInfo = null;
+ instanceInitializerInfoCollection = InstanceInitializerInfoCollection.empty();
// initializerEnablingJavaAssertions: `this` could trigger <clinit> of the previous holder.
setFlag(
INITIALIZER_ENABLING_JAVA_ASSERTIONS_FLAG,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/AlwaysTrueInstanceInitializerInfoContext.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/AlwaysTrueInstanceInitializerInfoContext.java
new file mode 100644
index 0000000..12ef51a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/AlwaysTrueInstanceInitializerInfoContext.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.info.initializer;
+
+import com.android.tools.r8.ir.code.InvokeMethod;
+
+public class AlwaysTrueInstanceInitializerInfoContext extends InstanceInitializerInfoContext {
+
+ private static final AlwaysTrueInstanceInitializerInfoContext INSTANCE =
+ new AlwaysTrueInstanceInitializerInfoContext();
+
+ private AlwaysTrueInstanceInitializerInfoContext() {}
+
+ public static AlwaysTrueInstanceInitializerInfoContext getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public boolean isAlwaysTrue() {
+ return true;
+ }
+
+ @Override
+ public boolean isSatisfiedBy(InvokeMethod invoke) {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextInsensitiveInstanceInitializerInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextInsensitiveInstanceInitializerInfoCollection.java
new file mode 100644
index 0000000..6cf070d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextInsensitiveInstanceInitializerInfoCollection.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.info.initializer;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+
+public class ContextInsensitiveInstanceInitializerInfoCollection
+ extends InstanceInitializerInfoCollection {
+
+ private final NonTrivialInstanceInitializerInfo info;
+
+ ContextInsensitiveInstanceInitializerInfoCollection(NonTrivialInstanceInitializerInfo info) {
+ this.info = info;
+ }
+
+ @Override
+ public NonTrivialInstanceInitializerInfo getContextInsensitive() {
+ return info;
+ }
+
+ @Override
+ public NonTrivialInstanceInitializerInfo get(InvokeDirect invoke) {
+ return info;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
+
+ @Override
+ public ContextInsensitiveInstanceInitializerInfoCollection rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+ NonTrivialInstanceInitializerInfo rewrittenInfo = info.rewrittenWithLens(appView, lens);
+ if (rewrittenInfo != info) {
+ return new ContextInsensitiveInstanceInitializerInfoCollection(rewrittenInfo);
+ }
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextSensitiveInstanceInitializerInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextSensitiveInstanceInitializerInfoCollection.java
new file mode 100644
index 0000000..3347587
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextSensitiveInstanceInitializerInfoCollection.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.info.initializer;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map.Entry;
+
+public class ContextSensitiveInstanceInitializerInfoCollection
+ extends InstanceInitializerInfoCollection {
+
+ private final ImmutableMap<InstanceInitializerInfoContext, NonTrivialInstanceInitializerInfo>
+ infos;
+
+ protected ContextSensitiveInstanceInitializerInfoCollection(
+ ImmutableMap<InstanceInitializerInfoContext, NonTrivialInstanceInitializerInfo> infos) {
+ assert !infos.isEmpty();
+ this.infos = infos;
+ }
+
+ @Override
+ public InstanceInitializerInfo getContextInsensitive() {
+ NonTrivialInstanceInitializerInfo result =
+ infos.get(AlwaysTrueInstanceInitializerInfoContext.getInstance());
+ return result != null ? result : DefaultInstanceInitializerInfo.getInstance();
+ }
+
+ @Override
+ public InstanceInitializerInfo get(InvokeDirect invoke) {
+ assert infos.keySet().stream().filter(context -> context.isSatisfiedBy(invoke)).count() <= 1;
+ for (Entry<InstanceInitializerInfoContext, NonTrivialInstanceInitializerInfo> entry :
+ infos.entrySet()) {
+ if (entry.getKey().isSatisfiedBy(invoke)) {
+ return entry.getValue();
+ }
+ }
+ return DefaultInstanceInitializerInfo.getInstance();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
+
+ @Override
+ public InstanceInitializerInfoCollection rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+ Builder builder = builder();
+ infos.forEach((context, info) -> builder.put(context, info.rewrittenWithLens(appView, lens)));
+ return builder.build();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java
index c9a7f32..af59b7c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java
@@ -25,6 +25,11 @@
}
@Override
+ public boolean isDefaultInstanceInitializerInfo() {
+ return true;
+ }
+
+ @Override
public DexMethod getParent() {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/EmptyInstanceInitializerInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/EmptyInstanceInitializerInfoCollection.java
new file mode 100644
index 0000000..d7d3560
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/EmptyInstanceInitializerInfoCollection.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.info.initializer;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+
+public class EmptyInstanceInitializerInfoCollection extends InstanceInitializerInfoCollection {
+
+ private static final EmptyInstanceInitializerInfoCollection EMPTY =
+ new EmptyInstanceInitializerInfoCollection();
+
+ private EmptyInstanceInitializerInfoCollection() {}
+
+ public static EmptyInstanceInitializerInfoCollection getInstance() {
+ return EMPTY;
+ }
+
+ @Override
+ public DefaultInstanceInitializerInfo getContextInsensitive() {
+ return DefaultInstanceInitializerInfo.getInstance();
+ }
+
+ @Override
+ public DefaultInstanceInitializerInfo get(InvokeDirect invoke) {
+ return DefaultInstanceInitializerInfo.getInstance();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return true;
+ }
+
+ @Override
+ public EmptyInstanceInitializerInfoCollection rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java
index 1e36f40..3865bc1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java
@@ -13,6 +13,18 @@
public abstract class InstanceInitializerInfo {
+ public boolean isDefaultInstanceInitializerInfo() {
+ return false;
+ }
+
+ public boolean isNonTrivialInstanceInitializerInfo() {
+ return false;
+ }
+
+ public NonTrivialInstanceInitializerInfo asNonTrivialInstanceInitializerInfo() {
+ return null;
+ }
+
public abstract DexMethod getParent();
public abstract InstanceFieldInitializationInfoCollection fieldInitializationInfos();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfoCollection.java
new file mode 100644
index 0000000..ce53a6a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfoCollection.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.info.initializer;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.MapUtils;
+import com.google.common.collect.ImmutableMap;
+
+public abstract class InstanceInitializerInfoCollection {
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static InstanceInitializerInfoCollection empty() {
+ return EmptyInstanceInitializerInfoCollection.getInstance();
+ }
+
+ public static InstanceInitializerInfoCollection of(InstanceInitializerInfo info) {
+ if (info != null && info.isNonTrivialInstanceInitializerInfo()) {
+ return new ContextInsensitiveInstanceInitializerInfoCollection(
+ info.asNonTrivialInstanceInitializerInfo());
+ }
+ return empty();
+ }
+
+ public abstract InstanceInitializerInfo getContextInsensitive();
+
+ public abstract InstanceInitializerInfo get(InvokeDirect invoke);
+
+ public abstract boolean isEmpty();
+
+ public abstract InstanceInitializerInfoCollection rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens lens);
+
+ public static class Builder {
+
+ private final ImmutableMap.Builder<
+ InstanceInitializerInfoContext, NonTrivialInstanceInitializerInfo>
+ infosBuilder = ImmutableMap.builder();
+
+ private Builder() {}
+
+ public Builder put(InstanceInitializerInfoContext context, InstanceInitializerInfo info) {
+ if (info.isNonTrivialInstanceInitializerInfo()) {
+ infosBuilder.put(context, info.asNonTrivialInstanceInitializerInfo());
+ }
+ return this;
+ }
+
+ public InstanceInitializerInfoCollection build() {
+ ImmutableMap<InstanceInitializerInfoContext, NonTrivialInstanceInitializerInfo> infos =
+ infosBuilder.build();
+ if (infos.isEmpty()) {
+ return empty();
+ }
+ if (infos.size() == 1 && MapUtils.firstKey(infos).isAlwaysTrue()) {
+ return new ContextInsensitiveInstanceInitializerInfoCollection(MapUtils.firstValue(infos));
+ }
+ return new ContextSensitiveInstanceInitializerInfoCollection(infos);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfoContext.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfoContext.java
new file mode 100644
index 0000000..8c28004
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfoContext.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.info.initializer;
+
+import com.android.tools.r8.ir.code.InvokeMethod;
+
+public abstract class InstanceInitializerInfoContext {
+
+ public boolean isAlwaysTrue() {
+ return false;
+ }
+
+ public abstract boolean isSatisfiedBy(InvokeMethod invoke);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
index ee08c65..40b3edc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
@@ -38,6 +38,16 @@
this.parent = parent;
}
+ @Override
+ public boolean isNonTrivialInstanceInitializerInfo() {
+ return true;
+ }
+
+ @Override
+ public NonTrivialInstanceInitializerInfo asNonTrivialInstanceInitializerInfo() {
+ return this;
+ }
+
private static boolean verifyNoUnknownBits(int data) {
int knownBits =
INSTANCE_FIELD_INITIALIZATION_INDEPENDENT_OF_ENVIRONMENT
@@ -189,8 +199,12 @@
return parent != null;
}
+ public DexMethod getParent() {
+ return parent;
+ }
+
public Builder setParent(DexMethod parent) {
- assert !hasParent();
+ assert !hasParent() || getParent() == parent;
this.parent = parent;
return this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
index d0412b7..9b41f18 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
@@ -20,6 +20,7 @@
import com.android.tools.r8.ir.optimize.info.LibraryOptimizationInfoInitializerFeedback;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoFactory;
+import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
import com.android.tools.r8.ir.optimize.info.initializer.NonTrivialInstanceInitializerInfo;
import com.google.common.collect.Sets;
import java.util.BitSet;
@@ -67,11 +68,12 @@
.recordInitializationInfo(
enumMembers.ordinalField, factory.createArgumentInitializationInfo(2))
.build();
- feedback.setInstanceInitializerInfo(
+ feedback.setInstanceInitializerInfoCollection(
enumConstructor,
- NonTrivialInstanceInitializerInfo.builder(fieldInitializationInfos)
- .setParent(dexItemFactory.objectMembers.constructor)
- .build());
+ InstanceInitializerInfoCollection.of(
+ NonTrivialInstanceInitializerInfo.builder(fieldInitializationInfos)
+ .setParent(dexItemFactory.objectMembers.constructor)
+ .build()));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/peepholes/BasicBlockMuncher.java b/src/main/java/com/android/tools/r8/ir/optimize/peepholes/BasicBlockMuncher.java
index fc48072..b196de9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/peepholes/BasicBlockMuncher.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/peepholes/BasicBlockMuncher.java
@@ -5,13 +5,14 @@
package com.android.tools.r8.ir.optimize.peepholes;
import static com.android.tools.r8.utils.InternalOptions.TestingOptions.NO_LIMIT;
+import static com.android.tools.r8.utils.PredicateUtils.not;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.LinearFlowInstructionListIterator;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.IteratorUtils;
import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.ListIterator;
@@ -45,7 +46,7 @@
int iterations = 0;
while (blocksIterator.hasPrevious()) {
BasicBlock currentBlock = blocksIterator.previous();
- InstructionListIterator it =
+ LinearFlowInstructionListIterator it =
new LinearFlowInstructionListIterator(
code, currentBlock, currentBlock.getInstructions().size());
boolean matched = false;
@@ -74,6 +75,11 @@
new LinearFlowInstructionListIterator(
code, currentBlock, currentBlock.getInstructions().size());
} else {
+ // Move the iterator to the first block we have not seen.
+ if (IteratorUtils.previousUntilUnsafe(blocksIterator, not(it::hasVisitedBlock)) != null) {
+ // Ensure that we visit the first not visited block.
+ blocksIterator.next();
+ }
break;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
index 0231d43..d5ef586 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
@@ -84,7 +84,7 @@
}
DexType hostType() {
- return singletonField.holder();
+ return singletonField.getHolderType();
}
DexProgramClass hostClass() {
@@ -495,7 +495,7 @@
if (ListUtils.lastIndexMatching(values, v -> v.getAliasedValue() == candidateValue) != 0
|| methodInvoked == null
- || methodInvoked.holder() != info.candidate.type) {
+ || methodInvoked.getHolderType() != info.candidate.type) {
return false;
}
@@ -663,7 +663,7 @@
: resolutionResult.isVirtualTarget() ? resolutionResult.getSingleTarget() : null;
if (ListUtils.lastIndexMatching(invoke.inValues(), isAliasedValue) == 0
&& methodInvoked != null
- && methodInvoked.holder() == candidateInfo.candidate.type) {
+ && methodInvoked.getHolderType() == candidateInfo.candidate.type) {
return true;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerGraphLens.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerGraphLens.java
index 6eeeb46..fbc4c12 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerGraphLens.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableMap;
@@ -17,13 +18,13 @@
ClassStaticizerGraphLens(
AppView<?> appView,
BiMap<DexField, DexField> fieldMapping,
- BiMap<DexMethod, DexMethod> methodMapping) {
+ BidirectionalOneToOneHashMap<DexMethod, DexMethod> methodMapping) {
super(
ImmutableMap.of(),
methodMapping,
fieldMapping,
fieldMapping.inverse(),
- methodMapping.inverse(),
+ methodMapping.getInverseOneToOneMap(),
appView.graphLens(),
appView.dexItemFactory());
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
index f776d44..2313a5a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -45,6 +45,7 @@
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.TraversalContinuation;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
@@ -737,7 +738,8 @@
}
private ProgramMethodSet staticizeMethodSymbols() {
- BiMap<DexMethod, DexMethod> methodMapping = HashBiMap.create();
+ BidirectionalOneToOneHashMap<DexMethod, DexMethod> methodMapping =
+ new BidirectionalOneToOneHashMap<>();
BiMap<DexField, DexField> fieldMapping = HashBiMap.create();
ProgramMethodSet staticizedMethods = ProgramMethodSet.create();
@@ -801,7 +803,7 @@
DexProgramClass candidateClass,
DexType hostType,
DexProgramClass hostClass,
- BiMap<DexMethod, DexMethod> methodMapping,
+ BidirectionalOneToOneHashMap<DexMethod, DexMethod> methodMapping,
BiMap<DexField, DexField> fieldMapping) {
candidateToHostMapping.put(candidateClass.type, hostType);
@@ -856,7 +858,7 @@
// has just been migrated to the host class.
staticizedMethods.createAndAdd(hostClass, newMethod);
}
- DexMethod originalMethod = methodMapping.inverse().get(method.method);
+ DexMethod originalMethod = methodMapping.getRepresentativeKey(method.method);
if (originalMethod == null) {
methodMapping.put(method.method, newMethod.method);
} else {
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 97ef2dc..58c9f2d 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -361,6 +361,13 @@
LensCodeRewriterUtils rewriter,
ClassWriter writer,
ImmutableMap<DexString, DexValue> defaults) {
+ NamingLens namingLens = this.namingLens;
+
+ // For "pass through" classes which has already been library desugared use the identity lens.
+ if (appView.isAlreadyLibraryDesugared(method.getHolder())) {
+ namingLens = NamingLens.getIdentityLens();
+ }
+
DexEncodedMethod definition = method.getDefinition();
int access = definition.getAccessFlags().getAsCfAccessFlags();
if (definition.isDeprecated()) {
@@ -383,7 +390,7 @@
writeAnnotations(visitor::visitAnnotation, definition.annotations().annotations);
writeParameterAnnotations(visitor, definition.parameterAnnotationsList);
if (!definition.shouldNotHaveCode()) {
- writeCode(method, classFileVersion, rewriter, visitor);
+ writeCode(method, classFileVersion, namingLens, rewriter, visitor);
}
visitor.visitEnd();
}
@@ -520,6 +527,7 @@
private void writeCode(
ProgramMethod method,
CfVersion classFileVersion,
+ NamingLens namingLens,
LensCodeRewriterUtils rewriter,
MethodVisitor visitor) {
CfCode code = method.getDefinition().getCode().asCfCode();
diff --git a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
index 86dac13..a8bd96c 100644
--- a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
@@ -104,7 +104,7 @@
// Used for iterating the sub trees that has this node as root.
final Set<DexType> children = new HashSet<>();
// Collection of the frontier reservation types and the interface type itself.
- final Set<DexType> reservationTypes = new HashSet<>();
+ private final Set<DexType> reservationTypes = new HashSet<>();
InterfaceReservationState(DexClass iface) {
this.iface = iface;
@@ -139,6 +139,10 @@
return isReserved == null ? null : method.getName();
}
+ void addReservationType(DexType type) {
+ this.reservationTypes.add(type);
+ }
+
void reserveName(DexString reservedName, DexEncodedMethod method) {
forAll(
s -> {
@@ -162,7 +166,7 @@
}
return null;
});
- return result == null ? true : result;
+ return result == null || result;
}
void addRenaming(DexString newName, DexEncodedMethod method) {
@@ -304,7 +308,7 @@
}
return null;
});
- return result == null ? true : result;
+ return result == null || result;
}
void addRenaming(DexString newName, MethodNameMinifier.State minifierState) {
@@ -398,7 +402,7 @@
assert iface.isInterface();
minifierState.allocateReservationStateAndReserve(iface.type, iface.type);
InterfaceReservationState iFaceState = new InterfaceReservationState(iface);
- iFaceState.reservationTypes.add(iface.type);
+ iFaceState.addReservationType(iface.type);
interfaceStateMap.put(iface.type, iFaceState);
}
}
@@ -644,8 +648,12 @@
InterfaceReservationState iState = interfaceStateMap.get(directlyImplemented);
if (iState != null) {
DexType frontierType = minifierState.getFrontier(clazz.type);
- assert minifierState.getReservationState(frontierType) != null;
- iState.reservationTypes.add(frontierType);
+ iState.addReservationType(frontierType);
+ // The reservation state should already be added, but if a class is extending
+ // an interface, we will not visit the class during the sub-type traversel
+ if (minifierState.getReservationState(clazz.type) == null) {
+ minifierState.allocateReservationStateAndReserve(clazz.type, frontierType);
+ }
}
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index 9893fb7..4bc638a 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -26,6 +26,7 @@
import com.android.tools.r8.utils.SymbolGenerationUtils;
import com.android.tools.r8.utils.SymbolGenerationUtils.MixedCasing;
import com.android.tools.r8.utils.Timing;
+import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -47,7 +48,7 @@
assert appView.options().isMinifying();
SubtypingInfo subtypingInfo = appView.appInfo().computeSubtypingInfo();
timing.begin("ComputeInterfaces");
- Set<DexClass> interfaces = new TreeSet<>((a, b) -> a.type.compareTo(b.type));
+ Set<DexClass> interfaces = new TreeSet<>(Comparator.comparing(a -> a.type));
interfaces.addAll(appView.appInfo().computeReachableInterfaces());
timing.end();
timing.begin("MinifyClasses");
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index 136595a..04a24b6 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -191,7 +191,7 @@
if (target == null || target.method == method) {
return;
}
- DexClass targetClass = appView.definitionFor(target.holder());
+ DexClass targetClass = appView.definitionFor(target.getHolderType());
DexMethod targetMethod = target.method;
if (originalClass.isProgramClass()) {
// In Java bytecode, it is only possible to target interface methods that are in one of
@@ -284,7 +284,7 @@
}
private boolean mayNeedBridgeForVisibility(ProgramMethod context, DexEncodedMethod method) {
- DexType holderType = method.holder();
+ DexType holderType = method.getHolderType();
DexClass holder = appView.definitionFor(holderType);
if (holder == null) {
return false;
diff --git a/src/main/java/com/android/tools/r8/optimize/PublicizerLens.java b/src/main/java/com/android/tools/r8/optimize/PublicizerLens.java
index 52e7b3d..bd8db04 100644
--- a/src/main/java/com/android/tools/r8/optimize/PublicizerLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/PublicizerLens.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.utils.collections.BidirectionalManyToManyRepresentativeMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import java.util.Set;
@@ -25,7 +26,7 @@
ImmutableMap.of(),
ImmutableMap.of(),
null,
- null,
+ BidirectionalManyToManyRepresentativeMap.empty(),
appView.graphLens(),
appView.dexItemFactory());
this.appView = appView;
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingLens.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingLens.java
index d3ceea5..b871193 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingLens.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingLens.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
@@ -20,11 +21,11 @@
private RepackagingLens(
AppView<AppInfoWithLiveness> appView,
BiMap<DexField, DexField> originalFieldSignatures,
- BiMap<DexMethod, DexMethod> originalMethodSignatures,
+ BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures,
BiMap<DexType, DexType> originalTypes) {
super(
originalTypes.inverse(),
- originalMethodSignatures.inverse(),
+ originalMethodSignatures.getInverseBacking(),
originalFieldSignatures.inverse(),
originalFieldSignatures,
originalMethodSignatures,
@@ -48,7 +49,8 @@
protected final BiMap<DexType, DexType> originalTypes = HashBiMap.create();
protected final BiMap<DexField, DexField> originalFieldSignatures = HashBiMap.create();
- protected final BiMap<DexMethod, DexMethod> originalMethodSignatures = HashBiMap.create();
+ protected final BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures =
+ new BidirectionalOneToOneHashMap<>();
public void recordMove(DexField from, DexField to) {
originalFieldSignatures.put(to, from);
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 bdda13b..7687ad4 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -819,7 +819,7 @@
if (fieldAccessInfo == null || !fieldAccessInfo.isWritten()) {
return false;
}
- DexType holder = field.holder();
+ DexType holder = field.getHolderType();
return fieldAccessInfo.isWrittenOnlyInMethodSatisfying(
method ->
method.getDefinition().isInstanceInitializer() && method.getHolderType() == holder);
@@ -829,7 +829,7 @@
assert checkIfObsolete();
assert isFieldWritten(field) : "Expected field `" + field.toSourceString() + "` to be written";
DexEncodedMethod staticInitializer =
- definitionFor(field.holder()).asProgramClass().getClassInitializer();
+ definitionFor(field.getHolderType()).asProgramClass().getClassInitializer();
return staticInitializer != null && isFieldOnlyWrittenInMethod(field, staticInitializer);
}
@@ -849,7 +849,7 @@
}
private boolean isLibraryOrClasspathField(DexEncodedField field) {
- DexClass holder = definitionFor(field.holder());
+ DexClass holder = definitionFor(field.getHolderType());
return holder == null || holder.isLibraryClass() || holder.isClasspathClass();
}
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index ed31df5..f6b5aa2 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -649,6 +649,33 @@
}
}
+ private void warnIfClassExtendsInterfaceOrImplementsClass(DexProgramClass clazz) {
+ if (clazz.superType != null) {
+ DexClass superClass = definitionFor(clazz.superType);
+ if (superClass != null && superClass.isInterface()) {
+ options.reporter.warning(
+ new StringDiagnostic(
+ "Class "
+ + clazz.toSourceString()
+ + " extends "
+ + superClass.toSourceString()
+ + " which is an interface"));
+ }
+ }
+ for (DexType iface : clazz.interfaces.values) {
+ DexClass ifaceClass = definitionFor(iface);
+ if (ifaceClass != null && !ifaceClass.isInterface()) {
+ options.reporter.warning(
+ new StringDiagnostic(
+ "Class "
+ + clazz.toSourceString()
+ + " implements "
+ + ifaceClass.toSourceString()
+ + " which is not an interface"));
+ }
+ }
+ }
+
private void enqueueRootItems(Map<DexReference, Set<ProguardKeepRuleBase>> items) {
items.entrySet().forEach(this::enqueueRootItem);
}
@@ -759,13 +786,13 @@
}
} else if (item.isDexEncodedField()) {
DexEncodedField field = item.asDexEncodedField();
- DexProgramClass holder = getProgramClassOrNull(field.holder());
+ DexProgramClass holder = getProgramClassOrNull(field.getHolderType());
if (holder != null) {
enqueueRootField(new ProgramField(holder, field), rules, precondition);
}
} else if (item.isDexEncodedMethod()) {
DexEncodedMethod method = item.asDexEncodedMethod();
- DexProgramClass holder = getProgramClassOrNull(method.holder());
+ DexProgramClass holder = getProgramClassOrNull(method.getHolderType());
if (holder != null) {
enqueueRootMethod(new ProgramMethod(holder, method), rules, precondition);
}
@@ -1459,6 +1486,10 @@
// Must mark the field as targeted even if it does not exist.
markFieldAsTargeted(fieldReference, currentMethod);
noClassMerging.add(fieldReference.getHolderType());
+
+ // Record field reference for generated extension registry shrinking.
+ appView.withGeneratedExtensionRegistryShrinker(
+ shrinker -> shrinker.handleFailedOrUnknownFieldResolution(fieldReference, currentMethod));
return;
}
@@ -1477,17 +1508,15 @@
Log.verbose(getClass(), "Register Sget `%s`.", fieldReference);
}
- if (appView.options().protoShrinking().enableGeneratedExtensionRegistryShrinking) {
- // If it is a dead proto extension field, don't trace onwards.
- boolean skipTracing =
- appView.withGeneratedExtensionRegistryShrinker(
- shrinker ->
- shrinker.isDeadProtoExtensionField(field, fieldAccessInfoCollection, keepInfo),
- false);
- if (skipTracing) {
- addDeadProtoTypeCandidate(field.getHolder());
- return;
- }
+ // If it is a dead proto extension field, don't trace onwards.
+ boolean skipTracing =
+ appView.withGeneratedExtensionRegistryShrinker(
+ shrinker ->
+ shrinker.isDeadProtoExtensionField(field, fieldAccessInfoCollection, keepInfo),
+ false);
+ if (skipTracing) {
+ addDeadProtoTypeCandidate(field.getHolder());
+ return;
}
if (field.getReference() != fieldReference) {
@@ -1679,6 +1708,9 @@
markTypeAsLive(holder.superType, reason);
}
+ // Warn if the class extends an interface or implements a class
+ warnIfClassExtendsInterfaceOrImplementsClass(holder);
+
// If this is an interface that has just become live, then report previously seen but unreported
// implemented-by edges.
transitionUnusedInterfaceToLive(holder);
@@ -2385,7 +2417,7 @@
&& appView.rewritePrefix.hasRewrittenTypeInSignature(method.method.proto, appView)) {
DexMethod methodToResolve =
DesugaredLibraryAPIConverter.methodWithVivifiedTypeInSignature(
- method.method, method.holder(), appView);
+ method.method, method.getHolderType(), appView);
assert methodToResolve != method.method;
markLibraryOrClasspathOverrideLive(
instantiation,
@@ -2787,7 +2819,7 @@
failedResolutionTargets.add(symbolicMethod);
failedResolution.forEachFailureDependency(
method -> {
- DexProgramClass clazz = getProgramClassOrNull(method.holder());
+ DexProgramClass clazz = getProgramClassOrNull(method.getHolderType());
if (clazz != null) {
failedResolutionTargets.add(method.method);
markMethodAsTargeted(new ProgramMethod(clazz, method), reason);
@@ -2839,7 +2871,7 @@
return;
}
- DexProgramClass clazz = getProgramClassOrNull(target.holder());
+ DexProgramClass clazz = getProgramClassOrNull(target.getHolderType());
if (clazz == null) {
return;
}
@@ -3672,7 +3704,7 @@
} else {
DexEncodedMethod implementation = definition.getDefaultInterfaceMethodImplementation();
if (implementation != null) {
- DexProgramClass companion = getProgramClassOrNull(implementation.holder());
+ DexProgramClass companion = getProgramClassOrNull(implementation.getHolderType());
markTypeAsLive(companion, graphReporter.reportCompanionClass(holder, companion));
markVirtualMethodAsLive(
new ProgramMethod(companion, implementation),
diff --git a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
index b154081..d78c808 100644
--- a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
+++ b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
@@ -281,7 +281,7 @@
public KeepReasonWitness reportCompanionMethod(
DexEncodedMethod definition, DexEncodedMethod implementation) {
- assert InterfaceMethodRewriter.isCompanionClassType(implementation.holder());
+ assert InterfaceMethodRewriter.isCompanionClassType(implementation.getHolderType());
if (keptGraphConsumer == null) {
return KeepReasonWitness.INSTANCE;
}
@@ -356,7 +356,8 @@
if (skipReporting(reason)) {
return KeepReasonWitness.INSTANCE;
}
- if (reason.edgeKind() == EdgeKind.IsLibraryMethod && isNonProgramClass(method.holder())) {
+ if (reason.edgeKind() == EdgeKind.IsLibraryMethod
+ && isNonProgramClass(method.getHolderType())) {
// Don't report edges to actual library methods.
// TODO(b/120959039): This should be dead code once no library classes are ever enqueued.
return KeepReasonWitness.INSTANCE;
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
index 9ccbf5e..85a7b88 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
@@ -306,13 +306,13 @@
@Override
public KeepMethodInfo getMethodInfo(DexEncodedMethod method, DexProgramClass holder) {
- assert method.holder() == holder.type;
+ assert method.getHolderType() == holder.type;
return keepMethodInfo.getOrDefault(method.method, KeepMethodInfo.bottom());
}
@Override
public KeepFieldInfo getFieldInfo(DexEncodedField field, DexProgramClass holder) {
- assert field.holder() == holder.type;
+ assert field.getHolderType() == holder.type;
return keepFieldInfo.getOrDefault(field.field, KeepFieldInfo.bottom());
}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepReason.java b/src/main/java/com/android/tools/r8/shaking/KeepReason.java
index 31f7611..53b944f 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepReason.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepReason.java
@@ -160,7 +160,7 @@
private InvokedFrom(DexProgramClass holder, DexEncodedMethod method) {
super(method);
- assert holder.type == method.holder();
+ assert holder.type == method.getHolderType();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/shaking/LibraryMethodOverrideAnalysis.java b/src/main/java/com/android/tools/r8/shaking/LibraryMethodOverrideAnalysis.java
index 2de5c70..d63c685 100644
--- a/src/main/java/com/android/tools/r8/shaking/LibraryMethodOverrideAnalysis.java
+++ b/src/main/java/com/android/tools/r8/shaking/LibraryMethodOverrideAnalysis.java
@@ -229,7 +229,7 @@
}
InstanceInitializerInfo initializerInfo =
- singleTarget.getDefinition().getOptimizationInfo().getInstanceInitializerInfo();
+ singleTarget.getDefinition().getOptimizationInfo().getInstanceInitializerInfo(invoke);
return initializerInfo.receiverNeverEscapesOutsideConstructorChain();
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
index 590ede0..42b56bd 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
@@ -57,7 +57,7 @@
public static ProguardKeepRule buildMethodKeepRule(DexClass clazz, DexEncodedMethod method) {
// TODO(b/122295241): These generated rules should be linked into the graph, eg, the method
// using identified reflection should be the source keeping the target alive.
- assert clazz.type == method.holder();
+ assert clazz.type == method.getHolderType();
ProguardKeepRule.Builder builder = ProguardKeepRule.builder();
builder.setOrigin(proguardCompatOrigin);
builder.setType(ProguardKeepRuleType.KEEP_CLASS_MEMBERS);
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 1d476bf..be431d3 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -403,7 +403,7 @@
DexEncodedMethod target =
appView.appInfo().unsafeResolveMethodDueToDexFormat(referenceInSubType).getSingleTarget();
// But, the resolution should not be landed on the current type we are visiting.
- if (target == null || target.holder() == type) {
+ if (target == null || target.getHolderType() == type) {
continue;
}
ProguardMemberRule ruleInSubType = assumeRulePool.get(target.method);
@@ -626,7 +626,7 @@
private boolean canInsertForwardingMethod(DexClass holder, DexEncodedMethod target) {
return appView.options().isGeneratingDex()
- || ArrayUtils.contains(holder.interfaces.values, target.holder());
+ || ArrayUtils.contains(holder.interfaces.values, target.getHolderType());
}
private void markMatchingOverriddenMethods(
@@ -1109,7 +1109,7 @@
if (options.isInterfaceMethodDesugaringEnabled()
&& encodedMethod.hasCode()
&& (encodedMethod.isPrivateMethod() || encodedMethod.isStaticMember())) {
- DexClass holder = appView.definitionFor(encodedMethod.holder());
+ DexClass holder = appView.definitionFor(encodedMethod.getHolderType());
if (holder != null && holder.isInterface()) {
if (rule.isSpecific()) {
options.reporter.warning(
@@ -1176,7 +1176,7 @@
} else if (context instanceof ProguardAssumeNoSideEffectRule) {
if (item.isDexEncodedMember()) {
DexEncodedMember<?, ?> member = item.asDexEncodedMember();
- if (member.holder() == appView.dexItemFactory().objectType) {
+ if (member.getHolderType() == appView.dexItemFactory().objectType) {
assert member.isDexEncodedMethod();
reportAssumeNoSideEffectsWarningForJavaLangClassMethod(
member.asDexEncodedMethod(), (ProguardAssumeNoSideEffectRule) context);
@@ -1957,7 +1957,8 @@
DexClass holder = appInfo.definitionForHolder(reference);
DexEncodedField field = reference.lookupOnClass(holder);
if (field != null
- && (field.isStatic() || isKeptDirectlyOrIndirectly(field.holder(), appInfo))) {
+ && (field.isStatic()
+ || isKeptDirectlyOrIndirectly(field.getHolderType(), appInfo))) {
assert appInfo.isFieldRead(field)
: "Expected kept field `" + field.toSourceString() + "` to be read";
assert appInfo.isFieldWritten(field)
@@ -1974,7 +1975,8 @@
: "Expected kept method `" + reference.toSourceString() + "` to be targeted";
DexEncodedMethod method =
appInfo.definitionForHolder(reference).lookupMethod(reference);
- if (!method.isAbstract() && isKeptDirectlyOrIndirectly(method.holder(), appInfo)) {
+ if (!method.isAbstract()
+ && isKeptDirectlyOrIndirectly(method.getHolderType(), appInfo)) {
assert appInfo.isLiveMethod(reference)
: "Expected non-abstract kept method `"
+ reference.toSourceString()
diff --git a/src/main/java/com/android/tools/r8/shaking/RuntimeTypeCheckInfo.java b/src/main/java/com/android/tools/r8/shaking/RuntimeTypeCheckInfo.java
index ddfea43..fb3868a 100644
--- a/src/main/java/com/android/tools/r8/shaking/RuntimeTypeCheckInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/RuntimeTypeCheckInfo.java
@@ -49,17 +49,24 @@
@Override
public void traceCheckCast(DexType type, ProgramMethod context) {
- checkCastTypes.add(type.toBaseType(factory));
+ add(type, checkCastTypes);
}
@Override
public void traceInstanceOf(DexType type, ProgramMethod context) {
- instanceOfTypes.add(type.toBaseType(factory));
+ add(type, instanceOfTypes);
}
@Override
public void traceExceptionGuard(DexType guard, ProgramMethod context) {
- exceptionGuardTypes.add(guard);
+ add(guard, exceptionGuardTypes);
+ }
+
+ private void add(DexType type, Set<DexType> set) {
+ DexType baseType = type.toBaseType(factory);
+ if (baseType.isClassType()) {
+ set.add(baseType);
+ }
}
public void attach(Enqueuer enqueuer) {
diff --git a/src/main/java/com/android/tools/r8/shaking/ScopedDexMethodSet.java b/src/main/java/com/android/tools/r8/shaking/ScopedDexMethodSet.java
index 4675107..d3b7e21 100644
--- a/src/main/java/com/android/tools/r8/shaking/ScopedDexMethodSet.java
+++ b/src/main/java/com/android/tools/r8/shaking/ScopedDexMethodSet.java
@@ -63,8 +63,8 @@
}
if (method.accessFlags.isMoreVisibleThan(
existing.accessFlags,
- method.holder().getPackageName(),
- existing.holder().getPackageName())) {
+ method.getHolderType().getPackageName(),
+ existing.getHolderType().getPackageName())) {
items.put(wrapped, method);
return AddMethodIfMoreVisibleResult.ADDED_MORE_VISIBLE;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
index 97e980c..58e85ff 100644
--- a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.android.tools.r8.utils.SingletonEquivalence;
import com.android.tools.r8.utils.TraversalContinuation;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
import com.google.common.base.Equivalence;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.BiMap;
@@ -199,7 +200,8 @@
private final Map<MergeKey, Representative> representatives = new HashMap<>();
private final BiMap<DexField, DexField> fieldMapping = HashBiMap.create();
- private final BiMap<DexMethod, DexMethod> methodMapping = HashBiMap.create();
+ private final BidirectionalOneToOneHashMap<DexMethod, DexMethod> methodMapping =
+ new BidirectionalOneToOneHashMap<>();
private int numberOfMergedClasses = 0;
@@ -234,7 +236,8 @@
private NestedGraphLens buildGraphLens() {
if (!fieldMapping.isEmpty() || !methodMapping.isEmpty()) {
BiMap<DexField, DexField> originalFieldSignatures = fieldMapping.inverse();
- BiMap<DexMethod, DexMethod> originalMethodSignatures = methodMapping.inverse();
+ BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures =
+ methodMapping.getInverseOneToOneMap();
return new NestedGraphLens(
ImmutableMap.of(),
methodMapping,
@@ -497,7 +500,7 @@
newMethods.add(sourceMethodAfterMove);
DexMethod originalMethod =
- methodMapping.inverse().getOrDefault(sourceMethod.method, sourceMethod.method);
+ methodMapping.getRepresentativeKeyOrDefault(sourceMethod.method, sourceMethod.method);
methodMapping.forcePut(originalMethod, sourceMethodAfterMove.method);
existingMethods.add(equivalence.wrap(sourceMethodAfterMove.method));
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index c98e345..434b5d2 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -762,7 +762,7 @@
// Conservatively find all possible targets for this method.
LookupResultSuccess lookupResult =
appInfo
- .resolveMethodOnInterface(method.holder(), method.method)
+ .resolveMethodOnInterface(method.getHolderType(), method.method)
.lookupVirtualDispatchTargets(target, appInfo)
.asLookupResultSuccess();
assert lookupResult != null;
@@ -1360,7 +1360,7 @@
private DexEncodedMethod renameConstructor(
DexEncodedMethod method, Predicate<DexMethod> availableMethodSignatures) {
assert method.isInstanceInitializer();
- DexType oldHolder = method.holder();
+ DexType oldHolder = method.getHolderType();
DexMethod newSignature;
int count = 1;
@@ -1396,7 +1396,7 @@
// renamed already.
assert !method.accessFlags.isConstructor() || strategy == Rename.NEVER;
DexString oldName = method.method.name;
- DexType oldHolder = method.holder();
+ DexType oldHolder = method.getHolderType();
DexMethod newSignature;
switch (strategy) {
@@ -1431,7 +1431,7 @@
private DexEncodedField renameFieldIfNeeded(
DexEncodedField field, Predicate<DexField> availableFieldSignatures) {
DexString oldName = field.field.name;
- DexType oldHolder = field.holder();
+ DexType oldHolder = field.getHolderType();
DexField newSignature =
application.dexItemFactory.createField(target.type, field.field.type, oldName);
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
index 2bee133..f634f48 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.utils.IterableUtils;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableSet;
@@ -74,7 +75,7 @@
Map<DexType, Map<DexMethod, GraphLensLookupResultProvider>>
contextualVirtualToDirectMethodMaps,
BiMap<DexField, DexField> originalFieldSignatures,
- BiMap<DexMethod, DexMethod> originalMethodSignatures,
+ BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures,
Map<DexMethod, DexMethod> originalMethodSignaturesForBridges,
GraphLens previousLens) {
super(
@@ -171,7 +172,8 @@
private final Map<DexType, Map<DexMethod, GraphLensLookupResultProvider>>
contextualVirtualToDirectMethodMaps = new IdentityHashMap<>();
- private final BiMap<DexMethod, DexMethod> originalMethodSignatures = HashBiMap.create();
+ private final BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures =
+ new BidirectionalOneToOneHashMap<>();
private final Map<DexMethod, DexMethod> originalMethodSignaturesForBridges =
new IdentityHashMap<>();
@@ -215,11 +217,12 @@
context);
}
}
- for (Map.Entry<DexMethod, DexMethod> entry : builder.originalMethodSignatures.entrySet()) {
- newBuilder.recordMove(
- entry.getValue(),
- builder.getMethodSignatureAfterClassMerging(entry.getKey(), mergedClasses));
- }
+ builder.originalMethodSignatures.forEach(
+ (renamedMethodSignature, originalMethodSignature) ->
+ newBuilder.recordMove(
+ originalMethodSignature,
+ builder.getMethodSignatureAfterClassMerging(
+ renamedMethodSignature, mergedClasses)));
for (Map.Entry<DexMethod, DexMethod> entry :
builder.originalMethodSignaturesForBridges.entrySet()) {
newBuilder.recordCreationOfBridgeMethod(
diff --git a/src/main/java/com/android/tools/r8/tracereferences/MissingDefinitionsDiagnostic.java b/src/main/java/com/android/tools/r8/tracereferences/MissingDefinitionsDiagnostic.java
index 8fd9edb..12a2462 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/MissingDefinitionsDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/MissingDefinitionsDiagnostic.java
@@ -7,10 +7,9 @@
import com.android.tools.r8.Keep;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.Position;
-import com.android.tools.r8.tracereferences.Tracer.TracedClassImpl;
-import com.android.tools.r8.tracereferences.Tracer.TracedFieldImpl;
-import com.android.tools.r8.tracereferences.Tracer.TracedMethodImpl;
-import com.android.tools.r8.tracereferences.Tracer.TracedReferenceBase;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -18,14 +17,14 @@
@Keep
public class MissingDefinitionsDiagnostic implements Diagnostic {
- private final Set<TracedClassImpl> missingClasses;
- private final Set<TracedFieldImpl> missingFields;
- private final Set<TracedMethodImpl> missingMethods;
+ private final Set<ClassReference> missingClasses;
+ private final Set<FieldReference> missingFields;
+ private final Set<MethodReference> missingMethods;
MissingDefinitionsDiagnostic(
- Set<TracedClassImpl> missingClasses,
- Set<TracedFieldImpl> missingFields,
- Set<TracedMethodImpl> missingMethods) {
+ Set<ClassReference> missingClasses,
+ Set<FieldReference> missingFields,
+ Set<MethodReference> missingMethods) {
this.missingClasses = missingClasses;
this.missingFields = missingFields;
this.missingMethods = missingMethods;
@@ -41,15 +40,25 @@
return Position.UNKNOWN;
}
- private <T extends TracedReferenceBase<?, ?>> void appendSorted(
- StringBuilder builder, Set<T> missing) {
+ private <T> void appendSorted(StringBuilder builder, Set<T> missing) {
missing.stream()
- .map(element -> element.getReference())
.map(Object::toString)
.sorted()
.forEach(item -> builder.append(" ").append(item).append(System.lineSeparator()));
}
+ public Set<ClassReference> getMissingClasses() {
+ return missingClasses;
+ }
+
+ public Set<FieldReference> getMissingFields() {
+ return missingFields;
+ }
+
+ public Set<MethodReference> getMissingMethods() {
+ return missingMethods;
+ }
+
@Override
public String getDiagnosticMessage() {
StringBuilder builder = new StringBuilder("Tracereferences found ");
diff --git a/src/main/java/com/android/tools/r8/tracereferences/PrintUsesFormatter.java b/src/main/java/com/android/tools/r8/tracereferences/PrintUsesFormatter.java
deleted file mode 100644
index a29fdb5..0000000
--- a/src/main/java/com/android/tools/r8/tracereferences/PrintUsesFormatter.java
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.tracereferences;
-
-import com.android.tools.r8.references.MethodReference;
-import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedClass;
-import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedField;
-import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedMethod;
-import java.util.List;
-
-class PrintUsesFormatter extends Formatter {
-
- @Override
- protected void printConstructorName(MethodReference method) {
- if (method.getMethodName().equals("<clinit>")) {
- append("<clinit>");
- } else {
- String holderName = method.getHolderClass().getTypeName();
- String constructorName = holderName.substring(holderName.lastIndexOf('.') + 1);
- append(constructorName);
- }
- }
-
- @Override
- protected void printMethod(TracedMethod method) {
- append(method.getReference().getHolderClass().getTypeName() + ": ");
- printNameAndReturn(method.getReference());
- printArguments(method.getReference());
- appendLine("");
- }
-
- @Override
- protected void printPackageNames(List<String> packageNames) {
- // No need to print package names for text output.
- }
-
- @Override
- protected void printTypeHeader(TracedClass type) {
- appendLine(type.getReference().getTypeName());
- }
-
- @Override
- protected void printTypeFooter() {}
-
- @Override
- protected void printField(TracedField field) {
- appendLine(
- field.getReference().getHolderClass().getTypeName()
- + ": "
- + field.getReference().getFieldType().getTypeName()
- + " "
- + field.getReference().getFieldName());
- }
-}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommandParser.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommandParser.java
index 3a3faeb..96956b0 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommandParser.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommandParser.java
@@ -16,7 +16,6 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import java.io.IOException;
-import java.io.PrintStream;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -37,8 +36,6 @@
"Usage: tracereferences <command> [<options>] [@<argfile>]",
" Where <command> is one of:",
" --check # Run emitting only diagnostics messages.",
- " --print-usage # Traced references will be output in the print-usage",
- " # format.",
" --keep-rules [<keep-rules-options>]",
" # Traced references will be output in the keep-rules",
" # format.",
@@ -92,7 +89,6 @@
private enum Command {
CHECK,
- PRINTUSAGE,
KEEP_RULES;
}
@@ -137,9 +133,6 @@
} else if (arg.equals("--check")) {
checkCommandNotSet(command, builder, origin);
command = Command.CHECK;
- } else if (arg.equals("--print-usage")) {
- checkCommandNotSet(command, builder, origin);
- command = Command.PRINTUSAGE;
} else if (arg.equals("--keep-rules")) {
checkCommandNotSet(command, builder, origin);
command = Command.KEEP_RULES;
@@ -174,15 +167,13 @@
if (command == null) {
builder.error(
new StringDiagnostic(
- "Missing command, specify one of 'check', '--print-usage' or '--keep-rules'",
- origin));
+ "Missing command, specify one of 'check' or '--keep-rules'", origin));
return builder;
}
if (command == Command.CHECK && output != null) {
builder.error(
- new StringDiagnostic(
- "Using '--output' requires command '--print-usage' or '--keep-rules'", origin));
+ new StringDiagnostic("Using '--output' requires command '--keep-rules'", origin));
return builder;
}
@@ -207,24 +198,6 @@
: new WriterConsumer(null, new PrintWriter(System.out)))
.build());
break;
- case PRINTUSAGE:
- final Path finalOutput = output;
- builder.setConsumer(
- new TraceReferencesPrintUsage() {
- @Override
- public void finished(DiagnosticsHandler handler) {
- PrintStream out = System.out;
- if (finalOutput != null) {
- try {
- out = new PrintStream(Files.newOutputStream(finalOutput));
- } catch (IOException e) {
- handler.error(new ExceptionDiagnostic(e));
- }
- }
- out.print(get());
- }
- });
- break;
default:
throw new Unreachable();
}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesPrintUsage.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesPrintUsage.java
deleted file mode 100644
index 80e8096..0000000
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesPrintUsage.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.tracereferences;
-
-import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.references.PackageReference;
-
-class TraceReferencesPrintUsage implements TraceReferencesConsumer {
-
- private final TraceReferencesResult.Builder traceReferencesResultBuilder =
- TraceReferencesResult.builder();
- private boolean finishedCalled = false;
-
- @Override
- public void acceptType(TracedClass type, DiagnosticsHandler handler) {
- assert !finishedCalled;
- traceReferencesResultBuilder.acceptType(type, handler);
- }
-
- @Override
- public void acceptField(TracedField field, DiagnosticsHandler handler) {
- assert !finishedCalled;
- traceReferencesResultBuilder.acceptField(field, handler);
- }
-
- @Override
- public void acceptMethod(TracedMethod method, DiagnosticsHandler handler) {
- assert !finishedCalled;
- traceReferencesResultBuilder.acceptMethod(method, handler);
- }
-
- @Override
- public void acceptPackage(PackageReference pkg, DiagnosticsHandler handler) {
- assert !finishedCalled;
- traceReferencesResultBuilder.acceptPackage(pkg, handler);
- }
-
- @Override
- public void finished(DiagnosticsHandler handler) {
- assert !finishedCalled;
- finishedCalled = true;
- }
-
- public String get() {
- Formatter formatter = new PrintUsesFormatter();
- formatter.format(traceReferencesResultBuilder.build());
- return formatter.get();
- }
-}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
index 8b571a8..ddd3f78 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
@@ -259,9 +259,9 @@
private final TraceReferencesConsumer consumer;
private DexProgramClass context;
private final DiagnosticsHandler diagnostics;
- private final Set<TracedClassImpl> missingClasses = new HashSet<>();
- private final Set<TracedFieldImpl> missingFields = new HashSet<>();
- private final Set<TracedMethodImpl> missingMethods = new HashSet<>();
+ private final Set<ClassReference> missingClasses = new HashSet<>();
+ private final Set<FieldReference> missingFields = new HashSet<>();
+ private final Set<MethodReference> missingMethods = new HashSet<>();
UseCollector(
DexItemFactory factory, TraceReferencesConsumer consumer, DiagnosticsHandler diagnostics) {
@@ -299,7 +299,7 @@
private void addField(DexField field) {
addType(field.type);
DexEncodedField baseField = appInfo.resolveField(field).getResolvedField();
- if (baseField != null && baseField.holder() != field.holder) {
+ if (baseField != null && baseField.getHolderType() != field.holder) {
field = baseField.field;
}
addType(field.holder);
@@ -310,7 +310,7 @@
if (!tracedField.isMissingDefinition()
&& baseField.accessFlags.isVisibilityDependingOnPackage()) {
consumer.acceptPackage(
- Reference.packageFromString(baseField.holder().getPackageName()), diagnostics);
+ Reference.packageFromString(baseField.getHolderType().getPackageName()), diagnostics);
}
}
}
@@ -330,7 +330,8 @@
if (!tracedMethod.isMissingDefinition()
&& definition.accessFlags.isVisibilityDependingOnPackage()) {
consumer.acceptPackage(
- Reference.packageFromString(definition.holder().getPackageName()), diagnostics);
+ Reference.packageFromString(definition.getHolderType().getPackageName()),
+ diagnostics);
}
}
}
@@ -347,10 +348,10 @@
collectMissing(tracedMethod, missingMethods);
}
- private <T extends TracedReferenceBase<?, ?>> void collectMissing(
- T tracedReference, Set<T> missingCollection) {
+ private <R, T extends TracedReferenceBase<R, ?>> void collectMissing(
+ T tracedReference, Set<R> missingCollection) {
if (tracedReference.isMissingDefinition()) {
- missingCollection.add(tracedReference);
+ missingCollection.add(tracedReference.getReference());
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index 4478209..21c3447 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils;
+import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
import static com.android.tools.r8.utils.FileUtils.isAarFile;
import static com.android.tools.r8.utils.FileUtils.isArchive;
import static com.android.tools.r8.utils.FileUtils.isClassFile;
@@ -504,7 +505,9 @@
mainDexList.add(mainDexListResource.getString());
}
}
- mainDexList.addAll(getMainDexClasses());
+ for (String mainDexClass : getMainDexClasses()) {
+ mainDexList.add(mainDexClass.replace(".", "/") + CLASS_EXTENSION);
+ }
String join = StringUtils.join(mainDexList, "\n");
writeToZipStream(out, dumpMainDexListResourceFileName, join.getBytes(), ZipEntry.DEFLATED);
}
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 9116bb5..f4cdbaa 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -238,6 +238,9 @@
System.getProperty("com.android.tools.r8.fieldBitAccessAnalysis") != null;
public boolean enableStaticClassMerging = true;
public boolean enableHorizontalClassMerging = true;
+ public boolean enableHorizontalClassMergingConstructorMerging = true;
+ public int horizontalClassMergingMaxGroupSize = 30;
+ public int horizontalClassMergingSyntheticArgumentCount = 3;
public boolean enableHorizontalClassMergingOfKotlinLambdas = true;
public boolean enableVerticalClassMerging = true;
public boolean enableArgumentRemoval = true;
@@ -1183,8 +1186,11 @@
// b/172508621
public boolean sortMethodsOnCfOutput =
System.getProperty("com.android.tools.r8.sortMethodsOnCfWriting") != null;
- public boolean allowDesugaredInput =
- System.getProperty("com.android.tools.r8.allowDesugaredInput") != null;
+ // Desugaring is not fully idempotent. With this option turned on all desugared input is
+ // allowed, and if it is detected that the desugared input cannot be reprocessed, that input
+ // will be passed-through without the problematic rewritings applied.
+ public boolean allowAllDesugaredInput =
+ System.getProperty("com.android.tools.r8.allowAllDesugaredInput") != null;
}
public static class CallSiteOptimizationOptions {
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 57cf52b..aee34f6 100644
--- a/src/main/java/com/android/tools/r8/utils/IterableUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
@@ -9,6 +9,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -65,11 +66,32 @@
return Iterables.concat(singleton(t), iterable);
}
+ public static <T> T flatten(T init, BiFunction<T, T, T> combine, Iterable<? extends T> iterable) {
+ T v = init;
+ for (T t : iterable) {
+ v = combine.apply(v, t);
+ }
+ return v;
+ }
+
+ public static int sumInt(Iterable<Integer> iterable) {
+ return flatten(0, Integer::sum, iterable);
+ }
+
+ public static <F> int sumInt(Iterable<F> iterable, Function<? super F, Integer> fn) {
+ Iterable<Integer> integers = Iterables.transform(iterable, i -> fn.apply(i));
+ return sumInt(integers);
+ }
+
public static <T, U> Iterable<U> flatMap(
Iterable<T> iterable, Function<? super T, Iterable<U>> map) {
return Iterables.concat(Iterables.transform(iterable, val -> map.apply(val)));
}
+ public static <T> Iterable<T> empty() {
+ return Collections.emptyList();
+ }
+
public static <T> Iterable<T> emptyIf(Iterable<T> iterable, boolean condition) {
if (condition) {
return Collections.emptySet();
diff --git a/src/main/java/com/android/tools/r8/utils/IteratorUtils.java b/src/main/java/com/android/tools/r8/utils/IteratorUtils.java
index b12de07..35f950a 100644
--- a/src/main/java/com/android/tools/r8/utils/IteratorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/IteratorUtils.java
@@ -87,6 +87,27 @@
throw new Unreachable();
}
+ public static <T> T previousUntilUnsafe(ListIterator<T> iterator, Predicate<T> predicate) {
+ while (iterator.hasPrevious()) {
+ T previous = iterator.previous();
+ if (predicate.test(previous)) {
+ return previous;
+ }
+ }
+ return null;
+ }
+
+ public static <T> T removeFirst(Iterator<T> iterator, Predicate<T> predicate) {
+ while (iterator.hasNext()) {
+ T item = iterator.next();
+ if (predicate.test(item)) {
+ iterator.remove();
+ return item;
+ }
+ }
+ return null;
+ }
+
public static <T> void removeIf(Iterator<T> iterator, Predicate<T> predicate) {
while (iterator.hasNext()) {
T item = iterator.next();
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index 51a3778..b1099e1 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -187,7 +187,7 @@
if (parsedData != null || parsedKotlinSourceDebugExtensions.containsKey(holder)) {
return parsedData;
}
- DexClass clazz = appView.definitionFor(currentMethod.holder());
+ DexClass clazz = appView.definitionFor(currentMethod.getHolderType());
DexValueString dexValueString = appView.getSourceDebugExtensionForType(clazz);
if (dexValueString != null) {
parsedData = KotlinSourceDebugExtensionParser.parse(dexValueString.value.toString());
@@ -475,7 +475,7 @@
continue;
}
// We use the same name for interface names even if it has different types.
- DexProgramClass clazz = appView.definitionForProgramType(method.holder());
+ DexProgramClass clazz = appView.definitionForProgramType(method.getHolderType());
DexClassAndMethod lookupResult =
appView.appInfo().lookupMaximallySpecificMethod(clazz, method.method);
if (lookupResult == null) {
@@ -808,7 +808,7 @@
}
method.setCode(
new CfCode(
- method.holder(),
+ method.getHolderType(),
oldCode.getMaxStack(),
oldCode.getMaxLocals(),
newInstructions,
diff --git a/src/main/java/com/android/tools/r8/utils/MapUtils.java b/src/main/java/com/android/tools/r8/utils/MapUtils.java
index c899a75..ac79586 100644
--- a/src/main/java/com/android/tools/r8/utils/MapUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MapUtils.java
@@ -12,6 +12,14 @@
public class MapUtils {
+ public static <K, V> K firstKey(Map<K, V> map) {
+ return map.keySet().iterator().next();
+ }
+
+ public static <K, V> V firstValue(Map<K, V> map) {
+ return map.values().iterator().next();
+ }
+
public static <K, V> Map<K, V> map(
Map<K, V> map,
IntFunction<Map<K, V>> factory,
diff --git a/src/main/java/com/android/tools/r8/utils/OptionalBool.java b/src/main/java/com/android/tools/r8/utils/OptionalBool.java
index abb048d..4443355 100644
--- a/src/main/java/com/android/tools/r8/utils/OptionalBool.java
+++ b/src/main/java/com/android/tools/r8/utils/OptionalBool.java
@@ -15,6 +15,11 @@
}
@Override
+ public int ordinal() {
+ return 1;
+ }
+
+ @Override
public String toString() {
return "true";
}
@@ -29,6 +34,11 @@
}
@Override
+ public int ordinal() {
+ return 0;
+ }
+
+ @Override
public String toString() {
return "false";
}
@@ -43,6 +53,11 @@
}
@Override
+ public int ordinal() {
+ return 2;
+ }
+
+ @Override
public String toString() {
return "unknown";
}
@@ -73,6 +88,8 @@
return System.identityHashCode(this);
}
+ public abstract int ordinal();
+
// Force all subtypes to implement toString().
@Override
public abstract String toString();
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToManyRepresentativeMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToManyRepresentativeMap.java
new file mode 100644
index 0000000..a4878dc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToManyRepresentativeMap.java
@@ -0,0 +1,94 @@
+// 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 java.util.Map;
+
+public abstract class BidirectionalManyToManyRepresentativeMap<K, V> {
+
+ public static <K, V> BidirectionalManyToManyRepresentativeMap<K, V> empty() {
+ return new EmptyBidirectionalManyToManyRepresentativeMap<>();
+ }
+
+ public abstract boolean containsKey(K key);
+
+ public abstract boolean containsValue(V value);
+
+ public abstract Map<K, V> getForwardBacking();
+
+ public abstract Map<V, K> getInverseBacking();
+
+ public final Inverse getInverseManyToManyMap() {
+ return new Inverse();
+ }
+
+ public abstract K getRepresentativeKey(V value);
+
+ public final K getRepresentativeKeyOrDefault(V value, K defaultValue) {
+ K representativeKey = getRepresentativeKey(value);
+ return representativeKey != null ? representativeKey : defaultValue;
+ }
+
+ public abstract V getRepresentativeValue(K key);
+
+ public final V getRepresentativeValueOrDefault(K key, V defaultValue) {
+ V representativeValue = getRepresentativeValue(key);
+ return representativeValue != null ? representativeValue : defaultValue;
+ }
+
+ public abstract Iterable<K> getKeys(V value);
+
+ public abstract Iterable<V> getValues(K key);
+
+ public abstract boolean isEmpty();
+
+ public class Inverse extends BidirectionalManyToManyRepresentativeMap<V, K> {
+
+ @Override
+ public boolean containsKey(V key) {
+ return BidirectionalManyToManyRepresentativeMap.this.containsValue(key);
+ }
+
+ @Override
+ public boolean containsValue(K value) {
+ return BidirectionalManyToManyRepresentativeMap.this.containsKey(value);
+ }
+
+ @Override
+ public Map<V, K> getForwardBacking() {
+ return BidirectionalManyToManyRepresentativeMap.this.getInverseBacking();
+ }
+
+ @Override
+ public Map<K, V> getInverseBacking() {
+ return BidirectionalManyToManyRepresentativeMap.this.getForwardBacking();
+ }
+
+ @Override
+ public V getRepresentativeKey(K value) {
+ return BidirectionalManyToManyRepresentativeMap.this.getRepresentativeValue(value);
+ }
+
+ @Override
+ public K getRepresentativeValue(V key) {
+ return BidirectionalManyToManyRepresentativeMap.this.getRepresentativeKey(key);
+ }
+
+ @Override
+ public Iterable<V> getKeys(K value) {
+ return BidirectionalManyToManyRepresentativeMap.this.getValues(value);
+ }
+
+ @Override
+ public Iterable<K> getValues(V key) {
+ return BidirectionalManyToManyRepresentativeMap.this.getKeys(key);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return BidirectionalManyToManyRepresentativeMap.this.isEmpty();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneHashMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneHashMap.java
new file mode 100644
index 0000000..d618b52
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneHashMap.java
@@ -0,0 +1,134 @@
+// 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.utils.IterableUtils;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+public class BidirectionalOneToOneHashMap<K, V>
+ extends BidirectionalManyToManyRepresentativeMap<K, V> implements Map<K, V> {
+
+ private final BiMap<K, V> backing;
+
+ public BidirectionalOneToOneHashMap() {
+ this(HashBiMap.create());
+ }
+
+ public BidirectionalOneToOneHashMap(BiMap<K, V> backing) {
+ this.backing = backing;
+ }
+
+ @Override
+ public void clear() {
+ backing.clear();
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ return backing.containsKey(key);
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ return backing.containsValue(value);
+ }
+
+ @Override
+ public Set<Entry<K, V>> entrySet() {
+ return backing.entrySet();
+ }
+
+ public V forcePut(K key, V value) {
+ return backing.forcePut(key, value);
+ }
+
+ @Override
+ public V get(Object key) {
+ return backing.get(key);
+ }
+
+ @Override
+ public BiMap<K, V> getForwardBacking() {
+ return backing;
+ }
+
+ @Override
+ public BiMap<V, K> getInverseBacking() {
+ return backing.inverse();
+ }
+
+ public BidirectionalOneToOneHashMap<V, K> getInverseOneToOneMap() {
+ return new BidirectionalOneToOneHashMap<>(backing.inverse());
+ }
+
+ @Override
+ public K getRepresentativeKey(V value) {
+ return backing.inverse().get(value);
+ }
+
+ @Override
+ public V getRepresentativeValue(K key) {
+ return backing.get(key);
+ }
+
+ @Override
+ public Iterable<K> getKeys(V value) {
+ if (containsValue(value)) {
+ return IterableUtils.singleton(getRepresentativeKey(value));
+ }
+ return IterableUtils.empty();
+ }
+
+ @Override
+ public Iterable<V> getValues(K key) {
+ if (containsKey(key)) {
+ return IterableUtils.singleton(getRepresentativeValue(key));
+ }
+ return IterableUtils.empty();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return backing.isEmpty();
+ }
+
+ @Override
+ public Set<K> keySet() {
+ return backing.keySet();
+ }
+
+ @Override
+ public V put(K key, V value) {
+ return backing.put(key, value);
+ }
+
+ public void putAll(BidirectionalOneToOneHashMap<K, V> map) {
+ putAll(map.backing);
+ }
+
+ @Override
+ public void putAll(Map<? extends K, ? extends V> map) {
+ backing.putAll(map);
+ }
+
+ @Override
+ public V remove(Object key) {
+ return backing.remove(key);
+ }
+
+ @Override
+ public int size() {
+ return backing.size();
+ }
+
+ @Override
+ public Collection<V> values() {
+ return backing.values();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/EmptyBidirectionalManyToManyRepresentativeMap.java b/src/main/java/com/android/tools/r8/utils/collections/EmptyBidirectionalManyToManyRepresentativeMap.java
new file mode 100644
index 0000000..dbcb753
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/collections/EmptyBidirectionalManyToManyRepresentativeMap.java
@@ -0,0 +1,58 @@
+// 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.utils.IterableUtils;
+import java.util.Collections;
+import java.util.Map;
+
+public class EmptyBidirectionalManyToManyRepresentativeMap<K, V>
+ extends BidirectionalManyToManyRepresentativeMap<K, V> {
+
+ @Override
+ public boolean containsKey(K key) {
+ return false;
+ }
+
+ @Override
+ public boolean containsValue(V value) {
+ return false;
+ }
+
+ @Override
+ public Map<K, V> getForwardBacking() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Map<V, K> getInverseBacking() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public K getRepresentativeKey(V value) {
+ return null;
+ }
+
+ @Override
+ public V getRepresentativeValue(K key) {
+ return null;
+ }
+
+ @Override
+ public Iterable<K> getKeys(V value) {
+ return IterableUtils.empty();
+ }
+
+ @Override
+ public Iterable<V> getValues(K key) {
+ return IterableUtils.empty();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitor.java b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitor.java
index 832d172..32221a0 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitor.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitor.java
@@ -5,10 +5,14 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.utils.structural.StructuralItem.CompareToAccept;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.Comparator;
+import java.util.Iterator;
/** Base class for a visitor implementing compareTo on a structural item. */
public abstract class CompareToVisitor {
@@ -17,13 +21,29 @@
public abstract void visitInt(int value1, int value2);
- public abstract void visitDexString(
- DexString string1, DexString string2, Comparator<DexString> comparator);
+ public abstract void visitLong(long value1, long value2);
+
+ public abstract void visitFloat(float value1, float value2);
+
+ public abstract void visitDouble(double value1, double value2);
+
+ /** Base for visiting an enumeration of items. */
+ public abstract <S> void visitItemIterator(
+ Iterator<S> it1, Iterator<S> it2, CompareToAccept<S> compareToAccept);
+
+ public final <S extends StructuralItem<S>> void visitItemArray(S[] items1, S[] items2) {
+ visitItemCollection(Arrays.asList(items1), Arrays.asList(items2));
+ }
+
+ public final <S extends StructuralItem<S>> void visitItemCollection(
+ Collection<S> items1, Collection<S> items2) {
+ visitItemIterator(items1.iterator(), items2.iterator(), S::acceptCompareTo);
+ }
+
+ public abstract void visitDexString(DexString string1, DexString string2);
public abstract void visitDexType(DexType type1, DexType type2);
- public abstract void visitDexTypeList(DexTypeList types1, DexTypeList types2);
-
public void visitDexField(DexField field1, DexField field2) {
visit(field1, field2, field1.getStructuralAccept());
}
@@ -32,6 +52,8 @@
visit(method1, method2, method1.getStructuralAccept());
}
+ public abstract void visitDexReference(DexReference reference1, DexReference reference2);
+
public abstract <S> void visit(S item1, S item2, StructuralAccept<S> accept);
@Deprecated
diff --git a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorBase.java b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorBase.java
index b7d016a..1f517d0 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorBase.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorBase.java
@@ -3,14 +3,17 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils.structural;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.utils.structural.StructuralItem.CompareToAccept;
import com.android.tools.r8.utils.structural.StructuralItem.HashingAccept;
import java.util.Comparator;
+import java.util.Iterator;
import java.util.function.Function;
import java.util.function.Predicate;
+import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
+import java.util.function.ToLongFunction;
/** Base class to share most visiting methods */
public abstract class CompareToVisitorBase extends CompareToVisitor {
@@ -44,23 +47,57 @@
}
@Override
- public final void visitDexString(
- DexString string1, DexString string2, Comparator<DexString> comparator) {
+ public void visitLong(long value1, long value2) {
if (stillEqual()) {
- setOrder(comparator.compare(string1, string2));
+ setOrder(Long.compare(value1, value2));
}
}
@Override
- public final void visitDexTypeList(DexTypeList types1, DexTypeList types2) {
- // Comparison is lexicographic with comparisons between items prior to the length of the lists.
+ public void visitFloat(float value1, float value2) {
if (stillEqual()) {
- int length = Math.min(types1.size(), types2.size());
- for (int i = 0; i < length && stillEqual(); i++) {
- visitDexType(types1.values[i], types2.values[i]);
- }
+ setOrder(Float.compare(value1, value2));
+ }
+ }
+
+ @Override
+ public void visitDouble(double value1, double value2) {
+ if (stillEqual()) {
+ setOrder(Double.compare(value1, value2));
+ }
+ }
+
+ @Override
+ public <S> void visitItemIterator(
+ Iterator<S> it1, Iterator<S> it2, CompareToAccept<S> compareToAccept) {
+ while (stillEqual() && it1.hasNext() && it2.hasNext()) {
+ compareToAccept.acceptCompareTo(it1.next(), it2.next(), this);
+ }
+ if (stillEqual()) {
+ visitBool(it1.hasNext(), it2.hasNext());
+ }
+ }
+
+ @Override
+ public void visitDexString(DexString string1, DexString string2) {
+ if (stillEqual()) {
+ setOrder(string1.compareTo(string2));
+ }
+ }
+
+ @Override
+ public void visitDexReference(DexReference reference1, DexReference reference2) {
+ if (stillEqual()) {
+ visitInt(reference1.referenceTypeOrder(), reference2.referenceTypeOrder());
if (stillEqual()) {
- visitInt(types1.size(), types2.size());
+ assert reference1.getClass() == reference2.getClass();
+ if (reference1.isDexType()) {
+ visitDexType(reference1.asDexType(), reference2.asDexType());
+ } else if (reference1.isDexField()) {
+ visitDexField(reference1.asDexField(), reference2.asDexField());
+ } else {
+ visitDexMethod(reference1.asDexMethod(), reference2.asDexMethod());
+ }
}
}
}
@@ -75,7 +112,7 @@
@Override
public final <S> void visit(S item1, S item2, StructuralAccept<S> accept) {
if (stillEqual()) {
- accept.accept(new ItemSpecification<>(item1, item2, this));
+ accept.apply(new ItemSpecification<>(item1, item2, this));
}
}
@@ -101,13 +138,49 @@
@Override
public ItemSpecification<T> withBool(Predicate<T> getter) {
- parent.visitBool(getter.test(item1), getter.test(item2));
+ if (parent.stillEqual()) {
+ parent.visitBool(getter.test(item1), getter.test(item2));
+ }
return this;
}
@Override
public ItemSpecification<T> withInt(ToIntFunction<T> getter) {
- parent.visitInt(getter.applyAsInt(item1), getter.applyAsInt(item2));
+ if (parent.stillEqual()) {
+ parent.visitInt(getter.applyAsInt(item1), getter.applyAsInt(item2));
+ }
+ return this;
+ }
+
+ @Override
+ public ItemSpecification<T> withLong(ToLongFunction<T> getter) {
+ if (parent.stillEqual()) {
+ parent.visitLong(getter.applyAsLong(item1), getter.applyAsLong(item2));
+ }
+ return this;
+ }
+
+ @Override
+ public ItemSpecification<T> withDouble(ToDoubleFunction<T> getter) {
+ if (parent.stillEqual()) {
+ parent.visitDouble(getter.applyAsDouble(item1), getter.applyAsDouble(item2));
+ }
+ return this;
+ }
+
+ @Override
+ public ItemSpecification<T> withIntArray(Function<T, int[]> getter) {
+ if (parent.stillEqual()) {
+ int[] is1 = getter.apply(item1);
+ int[] is2 = getter.apply(item2);
+ int minLength = Math.min(is1.length, is2.length);
+ for (int i = 0; i < minLength && parent.stillEqual(); i++) {
+ parent.visitInt(is1[i], is2[i]);
+ }
+ if (parent.stillEqual()) {
+ parent.visitInt(is1.length, is2.length);
+ }
+ }
return this;
}
@@ -121,12 +194,21 @@
boolean test1 = predicate.test(item1);
boolean test2 = predicate.test(item2);
if (test1 && test2) {
- compare.accept(getter.apply(item1), getter.apply(item2), parent);
+ compare.acceptCompareTo(getter.apply(item1), getter.apply(item2), parent);
} else {
parent.visitBool(test1, test2);
}
}
return this;
}
+
+ @Override
+ protected <S> ItemSpecification<T> withItemIterator(
+ Function<T, Iterator<S>> getter, CompareToAccept<S> compare, HashingAccept<S> hasher) {
+ if (parent.stillEqual()) {
+ parent.visitItemIterator(getter.apply(item1), getter.apply(item2), compare);
+ }
+ return this;
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithNamingLens.java b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithNamingLens.java
index a16575d..5295766 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithNamingLens.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithNamingLens.java
@@ -18,7 +18,7 @@
public static <T> int run(
T item1, T item2, NamingLens namingLens, CompareToAccept<T> compareToAccept) {
CompareToVisitorWithNamingLens state = new CompareToVisitorWithNamingLens(namingLens);
- compareToAccept.accept(item1, item2, state);
+ compareToAccept.acceptCompareTo(item1, item2, state);
return state.getOrder();
}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithStringTable.java b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithStringTable.java
new file mode 100644
index 0000000..dfd9fa0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithStringTable.java
@@ -0,0 +1,35 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils.structural;
+
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.NamingLensComparable;
+import com.android.tools.r8.naming.NamingLens;
+import java.util.function.ToIntFunction;
+
+public class CompareToVisitorWithStringTable extends CompareToVisitorWithNamingLens {
+
+ public static <T extends NamingLensComparable<T>> int run(
+ T item1, T item2, NamingLens namingLens, ToIntFunction<DexString> stringTable) {
+ CompareToVisitorWithNamingLens state =
+ new CompareToVisitorWithStringTable(namingLens, stringTable);
+ item1.acceptCompareTo(item2, state);
+ return state.getOrder();
+ }
+
+ private final ToIntFunction<DexString> stringTable;
+
+ public CompareToVisitorWithStringTable(
+ NamingLens namingLens, ToIntFunction<DexString> stringTable) {
+ super(namingLens);
+ this.stringTable = stringTable;
+ }
+
+ @Override
+ public void visitDexString(DexString string1, DexString string2) {
+ if (stillEqual()) {
+ visitInt(stringTable.applyAsInt(string1), stringTable.applyAsInt(string2));
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithTypeEquivalence.java b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithTypeEquivalence.java
index f77ab53..62b1942 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithTypeEquivalence.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithTypeEquivalence.java
@@ -15,7 +15,7 @@
public static <T> int run(
T item1, T item2, RepresentativeMap map, CompareToAccept<T> compareToAccept) {
CompareToVisitorWithTypeEquivalence state = new CompareToVisitorWithTypeEquivalence(map);
- compareToAccept.accept(item1, item2, state);
+ compareToAccept.acceptCompareTo(item1, item2, state);
return state.getOrder();
}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithTypeTable.java b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithTypeTable.java
new file mode 100644
index 0000000..94fb1f9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithTypeTable.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils.structural;
+
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.NamingLensComparable;
+import com.android.tools.r8.naming.NamingLens;
+import java.util.function.ToIntFunction;
+
+public class CompareToVisitorWithTypeTable extends CompareToVisitorWithStringTable {
+
+ public static <T extends NamingLensComparable<T>> int run(
+ T item1,
+ T item2,
+ NamingLens namingLens,
+ ToIntFunction<DexString> stringTable,
+ ToIntFunction<DexType> typeTable) {
+ CompareToVisitorWithNamingLens state =
+ new CompareToVisitorWithTypeTable(namingLens, stringTable, typeTable);
+ item1.acceptCompareTo(item2, state);
+ return state.getOrder();
+ }
+
+ private final ToIntFunction<DexType> typeTable;
+
+ public CompareToVisitorWithTypeTable(
+ NamingLens namingLens,
+ ToIntFunction<DexString> stringTable,
+ ToIntFunction<DexType> typeTable) {
+ super(namingLens, stringTable);
+ this.typeTable = typeTable;
+ }
+
+ @Override
+ public void visitDexType(DexType type1, DexType type2) {
+ if (stillEqual()) {
+ visitInt(typeTable.applyAsInt(type1), typeTable.applyAsInt(type2));
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/HashCodeVisitor.java b/src/main/java/com/android/tools/r8/utils/structural/HashCodeVisitor.java
index ab75fb4..1b5b400 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/HashCodeVisitor.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/HashCodeVisitor.java
@@ -5,9 +5,13 @@
import com.android.tools.r8.utils.structural.StructuralItem.CompareToAccept;
import com.android.tools.r8.utils.structural.StructuralItem.HashingAccept;
+import java.util.Arrays;
+import java.util.Iterator;
import java.util.function.Function;
import java.util.function.Predicate;
+import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
+import java.util.function.ToLongFunction;
/**
* Simple hash code implementation.
@@ -21,7 +25,7 @@
public static <T> int run(T item, StructuralAccept<T> visit) {
HashCodeVisitor<T> visitor = new HashCodeVisitor<>(item);
- visit.accept(visitor);
+ visit.apply(visitor);
return visitor.hashCode;
}
@@ -56,6 +60,21 @@
}
@Override
+ public HashCodeVisitor<T> withLong(ToLongFunction<T> getter) {
+ return amend(Long.hashCode(getter.applyAsLong(item)));
+ }
+
+ @Override
+ public HashCodeVisitor<T> withDouble(ToDoubleFunction<T> getter) {
+ return amend(Double.hashCode(getter.applyAsDouble(item)));
+ }
+
+ @Override
+ public HashCodeVisitor<T> withIntArray(Function<T, int[]> getter) {
+ return amend(Arrays.hashCode(getter.apply(item)));
+ }
+
+ @Override
protected <S> HashCodeVisitor<T> withConditionalCustomItem(
Predicate<T> predicate,
Function<T, S> getter,
@@ -64,9 +83,19 @@
if (predicate.test(item)) {
return amend(getter.apply(item).hashCode());
} else {
- // Use the value 1 for the failing-predicate case such that a different hash is obtained for
+ // Use the value 1 for the failing-predicate case such that a different hash is obtained for,
// eg, {null, null} and {null}.
return amend(1);
}
}
+
+ @Override
+ protected <S> HashCodeVisitor<T> withItemIterator(
+ Function<T, Iterator<S>> getter, CompareToAccept<S> compare, HashingAccept<S> hasher) {
+ Iterator<S> it = getter.apply(item);
+ while (it.hasNext()) {
+ amend(it.next().hashCode());
+ }
+ return this;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/HashingVisitor.java b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitor.java
index bfd5e0d..ed0f61e 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/HashingVisitor.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitor.java
@@ -3,10 +3,16 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils.structural;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.utils.structural.StructuralItem.HashingAccept;
import com.google.common.hash.Hasher;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
import java.util.function.BiConsumer;
public abstract class HashingVisitor {
@@ -15,11 +21,38 @@
public abstract void visitInt(int value);
+ public abstract void visitFloat(float value);
+
+ public abstract void visitLong(long value);
+
+ public abstract void visitDouble(double value);
+
+ /** Base for visiting an enumeration of items. */
+ protected abstract <S> void visitItemIterator(Iterator<S> it, HashingAccept<S> hashingAccept);
+
+ public final <S extends StructuralItem<S>> void visitItemArray(S[] items) {
+ visitItemCollection(Arrays.asList(items));
+ }
+
+ public final <S extends StructuralItem<S>> void visitItemCollection(Collection<S> items) {
+ visitItemIterator(items.iterator(), S::acceptHashing);
+ }
+
public abstract void visitDexString(DexString string);
public abstract void visitDexType(DexType type);
- public abstract void visitDexTypeList(DexTypeList types);
+ public void visitDexField(DexField field) {
+ visit(field, field.getStructuralAccept());
+ }
+
+ public void visitDexMethod(DexMethod method) {
+ visit(method, method.getStructuralAccept());
+ }
+
+ public void visitDexReference(DexReference reference) {
+ reference.accept(this::visitDexType, this::visitDexField, this::visitDexMethod);
+ }
public abstract <S> void visit(S item, StructuralAccept<S> accept);
diff --git a/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java
index 2036b1b..63989f5 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java
@@ -5,14 +5,16 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.utils.structural.StructuralItem.CompareToAccept;
import com.android.tools.r8.utils.structural.StructuralItem.HashingAccept;
import com.google.common.hash.Hasher;
+import java.util.Iterator;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
+import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
+import java.util.function.ToLongFunction;
/** Visitor for hashing a structural item under some assumed type equivalence. */
public class HashingVisitorWithTypeEquivalence extends HashingVisitor {
@@ -24,7 +26,7 @@
public static <T> void run(
T item, Hasher hasher, RepresentativeMap map, HashingAccept<T> hashingAccept) {
- hashingAccept.accept(item, new HashingVisitorWithTypeEquivalence(hasher, map));
+ hashingAccept.acceptHashing(item, new HashingVisitorWithTypeEquivalence(hasher, map));
}
private final Hasher hash;
@@ -46,6 +48,21 @@
}
@Override
+ public void visitFloat(float value) {
+ hash.putFloat(value);
+ }
+
+ @Override
+ public void visitLong(long value) {
+ hash.putLong(value);
+ }
+
+ @Override
+ public void visitDouble(double value) {
+ hash.putDouble(value);
+ }
+
+ @Override
public void visitDexString(DexString string) {
visitInt(string.hashCode());
}
@@ -56,13 +73,15 @@
}
@Override
- public void visitDexTypeList(DexTypeList types) {
- types.forEach(this::visitDexType);
+ public <S> void visit(S item, StructuralAccept<S> accept) {
+ accept.apply(new ItemSpecification<>(item, this));
}
@Override
- public <S> void visit(S item, StructuralAccept<S> accept) {
- accept.accept(new ItemSpecification<>(item, this));
+ protected <S> void visitItemIterator(Iterator<S> it, HashingAccept<S> hashingAccept) {
+ while (it.hasNext()) {
+ hashingAccept.acceptHashing(it.next(), this);
+ }
}
@Override
@@ -100,6 +119,27 @@
}
@Override
+ public ItemSpecification<T> withLong(ToLongFunction<T> getter) {
+ parent.visitLong(getter.applyAsLong(item));
+ return this;
+ }
+
+ @Override
+ public ItemSpecification<T> withDouble(ToDoubleFunction<T> getter) {
+ parent.visitDouble(getter.applyAsDouble(item));
+ return this;
+ }
+
+ @Override
+ public ItemSpecification<T> withIntArray(Function<T, int[]> getter) {
+ int[] ints = getter.apply(item);
+ for (int i = 0; i < ints.length; i++) {
+ parent.visitInt(ints[i]);
+ }
+ return this;
+ }
+
+ @Override
protected <S> ItemSpecification<T> withConditionalCustomItem(
Predicate<T> predicate,
Function<T, S> getter,
@@ -109,9 +149,16 @@
// Always hash the predicate result to distinguish, eg, {null, null} and {null}.
parent.visitBool(test);
if (test) {
- hasher.accept(getter.apply(item), parent);
+ hasher.acceptHashing(getter.apply(item), parent);
}
return this;
}
+
+ @Override
+ protected <S> ItemSpecification<T> withItemIterator(
+ Function<T, Iterator<S>> getter, CompareToAccept<S> compare, HashingAccept<S> hasher) {
+ parent.visitItemIterator(getter.apply(item), hasher);
+ return this;
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/StructuralAccept.java b/src/main/java/com/android/tools/r8/utils/structural/StructuralAccept.java
index 1c6d25c..ede348f 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/StructuralAccept.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/StructuralAccept.java
@@ -3,7 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils.structural;
+/** Mapping of a specification over an item. */
+// TODO(b/171867022): Rename this to StructuralMapping to avoid confusion with the Acceptor and
+// accept classes.
@FunctionalInterface
public interface StructuralAccept<T> {
- void accept(StructuralSpecification<T, ?> visitor);
+ void apply(StructuralSpecification<T, ?> spec);
}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/StructuralAcceptor.java b/src/main/java/com/android/tools/r8/utils/structural/StructuralAcceptor.java
new file mode 100644
index 0000000..4a06e40
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/structural/StructuralAcceptor.java
@@ -0,0 +1,9 @@
+// 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.structural;
+
+import com.android.tools.r8.utils.structural.StructuralItem.CompareToAccept;
+import com.android.tools.r8.utils.structural.StructuralItem.HashingAccept;
+
+public interface StructuralAcceptor<T> extends CompareToAccept<T>, HashingAccept<T> {}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/StructuralItem.java b/src/main/java/com/android/tools/r8/utils/structural/StructuralItem.java
index f077c25..92542f2 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/StructuralItem.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/StructuralItem.java
@@ -17,7 +17,7 @@
@FunctionalInterface
interface CompareToAccept<T> {
- void accept(T item1, T item2, CompareToVisitor visitor);
+ void acceptCompareTo(T item1, T item2, CompareToVisitor visitor);
}
/**
@@ -51,7 +51,7 @@
@FunctionalInterface
interface HashingAccept<T> {
- void accept(T item, HashingVisitor visitor);
+ void acceptHashing(T item, HashingVisitor visitor);
}
/**
diff --git a/src/main/java/com/android/tools/r8/utils/structural/StructuralSpecification.java b/src/main/java/com/android/tools/r8/utils/structural/StructuralSpecification.java
index bb8f7c7..ff74111 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/StructuralSpecification.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/StructuralSpecification.java
@@ -5,19 +5,24 @@
import com.android.tools.r8.utils.structural.StructuralItem.CompareToAccept;
import com.android.tools.r8.utils.structural.StructuralItem.HashingAccept;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
import java.util.function.Function;
import java.util.function.Predicate;
+import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
+import java.util.function.ToLongFunction;
public abstract class StructuralSpecification<T, V extends StructuralSpecification<T, V>> {
/**
- * Basic specification for visiting an item.
+ * Base for accessing and visiting a sub-part on an item.
*
- * <p>This specified the getter for the item as well as all of the methods that are required for
- * visiting. Those coincide with the requirements of Specified.
+ * <p>This specifies the getter for the sub-part as well as all of the methods that are required
+ * for visiting. The required methods coincide with the requirements of StructuralItem.
*
- * <p>It is preferable to use withStructuralItem.
+ * <p>It is preferable to use withItem and make the item itself implement StructuralItem.
*/
@Deprecated
public final <S> V withCustomItem(
@@ -25,12 +30,26 @@
return withConditionalCustomItem(t -> true, getter, compare, hasher);
}
+ public final <S> V withCustomItem(Function<T, S> getter, StructuralAcceptor<S> acceptor) {
+ return withCustomItem(getter, acceptor, acceptor);
+ }
+
+ /** Base implementation for visiting an item. */
protected abstract <S> V withConditionalCustomItem(
Predicate<T> predicate,
Function<T, S> getter,
CompareToAccept<S> compare,
HashingAccept<S> hasher);
+ /** Base implementation for visiting an enumeration of items. */
+ protected abstract <S> V withItemIterator(
+ Function<T, Iterator<S>> getter, CompareToAccept<S> compare, HashingAccept<S> hasher);
+
+ public final <S> V withCustomItemCollection(
+ Function<T, Collection<S>> getter, StructuralAcceptor<S> acceptor) {
+ return withItemIterator(getter.andThen(Collection::iterator), acceptor, acceptor);
+ }
+
/**
* Specification for a "specified" item.
*
@@ -41,7 +60,7 @@
return withConditionalItem(t -> true, getter);
}
- final <S extends StructuralItem<S>> V withNullableItem(Function<T, S> getter) {
+ public final <S extends StructuralItem<S>> V withNullableItem(Function<T, S> getter) {
return withConditionalItem(s -> getter.apply(s) != null, getter);
}
@@ -50,6 +69,17 @@
return withConditionalCustomItem(predicate, getter, S::acceptCompareTo, S::acceptHashing);
}
+ public final <S extends StructuralItem<S>> V withItemCollection(
+ Function<T, Collection<S>> getter) {
+ return withItemIterator(
+ getter.andThen(Collection::iterator), S::acceptCompareTo, S::acceptHashing);
+ }
+
+ public final <S extends StructuralItem<S>> V withItemArray(Function<T, S[]> getter) {
+ return withItemIterator(
+ getter.andThen(a -> Arrays.asList(a).iterator()), S::acceptCompareTo, S::acceptHashing);
+ }
+
/**
* Helper to declare an assert on the item.
*
@@ -62,4 +92,10 @@
public abstract V withBool(Predicate<T> getter);
public abstract V withInt(ToIntFunction<T> getter);
+
+ public abstract V withLong(ToLongFunction<T> getter);
+
+ public abstract V withDouble(ToDoubleFunction<T> getter);
+
+ public abstract V withIntArray(Function<T, int[]> getter);
}
diff --git a/src/test/java/com/android/tools/r8/DiagnosticsChecker.java b/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
index 09e32da..bda7fe1 100644
--- a/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
+++ b/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
@@ -48,7 +48,7 @@
void run(DiagnosticsHandler handler) throws CompilationFailedException;
}
- private static void checkContains(String snippet, List<Diagnostic> diagnostics) {
+ public static void checkContains(String snippet, List<Diagnostic> diagnostics) {
List<String> messages = ListUtils.map(diagnostics, Diagnostic::getDiagnosticMessage);
System.out.println("Expecting match for '" + snippet + "'");
System.out.println("StdErr:\n" + messages);
@@ -60,6 +60,10 @@
diagnostics.stream().anyMatch(d -> d.getDiagnosticMessage().contains(snippet)));
}
+ public static void checkContains(Collection<String> snippets, List<Diagnostic> diagnostics) {
+ snippets.forEach(snippet -> checkContains(snippet, diagnostics));
+ }
+
public void checkErrorsContains(String snippet) {
checkContains(snippet, errors);
}
@@ -84,7 +88,20 @@
runner.run(handler);
fail("Failure expected");
} catch (CompilationFailedException e) {
- snippets.forEach(snippet -> checkContains(snippet, handler.errors));
+ checkContains(snippets, handler.errors);
+ throw e;
+ }
+ }
+
+ public static void checkErrorDiagnostics(
+ Consumer<DiagnosticsChecker> checker, FailingRunner runner)
+ throws CompilationFailedException {
+ DiagnosticsChecker handler = new DiagnosticsChecker();
+ try {
+ runner.run(handler);
+ fail("Failure expected");
+ } catch (CompilationFailedException e) {
+ checker.accept(handler);
throw e;
}
}
diff --git a/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java b/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
index b6fe80d..10b8afe 100644
--- a/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
@@ -53,6 +53,12 @@
// External JDK to use to run R8
private final TestRuntime runtime;
+ // Enable/disable compiling with assertions
+ private boolean enableAssertions = true;
+
+ // Allow test proguard options
+ private boolean allowTestProguardOptions = false;
+
private boolean addR8ExternalDeps = false;
private List<String> jvmFlags = new ArrayList<>();
@@ -79,6 +85,16 @@
return self();
}
+ public ExternalR8TestBuilder enableAssertions(boolean enable) {
+ enableAssertions = enable;
+ return self();
+ }
+
+ public ExternalR8TestBuilder allowTestProguardOptions(boolean allow) {
+ allowTestProguardOptions = allow;
+ return self();
+ }
+
@Override
ExternalR8TestCompileResult internalCompile(
Builder builder, Consumer<InternalOptions> optionsConsumer, Supplier<AndroidApp> app)
@@ -101,9 +117,16 @@
command.addAll(jvmFlags);
+ if (enableAssertions) {
+ command.add("-ea");
+ }
+
+ if (allowTestProguardOptions) {
+ command.add("-Dcom.android.tools.r8.allowTestProguardOptions=true");
+ }
+
Collections.addAll(
command,
- "-ea",
"-cp",
classPath,
R8.class.getTypeName(),
diff --git a/src/test/java/com/android/tools/r8/ExternalR8TestCompileResult.java b/src/test/java/com/android/tools/r8/ExternalR8TestCompileResult.java
index ea6b2f7..a7c0baf 100644
--- a/src/test/java/com/android/tools/r8/ExternalR8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/ExternalR8TestCompileResult.java
@@ -5,7 +5,6 @@
package com.android.tools.r8;
import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.io.IOException;
@@ -41,14 +40,6 @@
return proguardMap;
}
- public String stdout() {
- return processResult.stdout;
- }
-
- public String stderr() {
- return processResult.stdout;
- }
-
@Override
public ExternalR8TestCompileResult self() {
return this;
@@ -61,12 +52,12 @@
@Override
public String getStdout() {
- throw new Unimplemented("Unexpected attempt to access stdout from external R8");
+ return processResult.stdout;
}
@Override
public String getStderr() {
- throw new Unimplemented("Unexpected attempt to access stderr from external R8");
+ return processResult.stderr;
}
@Override
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 058c2e5..34d573a 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -484,6 +484,10 @@
return self();
}
+ public T addNoHorizontalClassMergingRule(String clazz) {
+ return addKeepRules("-nohorizontalclassmerging class " + clazz);
+ }
+
public T enableNoHorizontalClassMergingAnnotations() {
if (!enableNoHorizontalClassMergingAnnotations) {
enableNoHorizontalClassMergingAnnotations = true;
diff --git a/src/test/java/com/android/tools/r8/TestParametersBuilder.java b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
index 7b630c3..f93d6d6 100644
--- a/src/test/java/com/android/tools/r8/TestParametersBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
@@ -165,9 +165,13 @@
return withApiFilter(api -> true);
}
- public TestParametersBuilder withAllApiLevelsAlsoForCf() {
+ public TestParametersBuilder enableApiLevelsForCf() {
enableApiLevelsForCf = true;
- return withAllApiLevels();
+ return this;
+ }
+
+ public TestParametersBuilder withAllApiLevelsAlsoForCf() {
+ return withAllApiLevels().enableApiLevelsForCf();
}
public TestParametersBuilder withApiLevel(AndroidApiLevel api) {
@@ -204,13 +208,8 @@
if (!enableApiLevels) {
return Stream.of(new TestParameters(runtime));
}
- if (!runtime.isDex()) {
- if (!enableApiLevelsForCf) {
- return Stream.of(new TestParameters(runtime));
- }
- return Stream.of(
- new TestParameters(runtime, AndroidApiLevel.B),
- new TestParameters(runtime, AndroidApiLevel.LATEST));
+ if (runtime.isCf() && !enableApiLevelsForCf) {
+ return Stream.of(new TestParameters(runtime));
}
List<AndroidApiLevel> sortedApiLevels =
AndroidApiLevel.getAndroidApiLevelsSorted().stream()
@@ -219,7 +218,7 @@
if (sortedApiLevels.isEmpty()) {
return Stream.of();
}
- AndroidApiLevel vmLevel = runtime.asDex().getMinApiLevel();
+ AndroidApiLevel vmLevel = runtime.maxSupportedApiLevel();
AndroidApiLevel lowestApplicable = sortedApiLevels.get(0);
if (vmLevel.getLevel() < lowestApplicable.getLevel()) {
return Stream.of();
diff --git a/src/test/java/com/android/tools/r8/TestRuntime.java b/src/test/java/com/android/tools/r8/TestRuntime.java
index 99f9a71..db1ca7f 100644
--- a/src/test/java/com/android/tools/r8/TestRuntime.java
+++ b/src/test/java/com/android/tools/r8/TestRuntime.java
@@ -175,6 +175,12 @@
}
@Override
+ public AndroidApiLevel maxSupportedApiLevel() {
+ // The "none" runtime trivally supports all api levels as nothing is run.
+ return AndroidApiLevel.LATEST;
+ }
+
+ @Override
public String toString() {
return NAME;
}
@@ -224,6 +230,11 @@
}
@Override
+ public AndroidApiLevel maxSupportedApiLevel() {
+ return getMinApiLevel();
+ }
+
+ @Override
public boolean equals(Object other) {
if (!(other instanceof DexRuntime)) {
return false;
@@ -286,6 +297,12 @@
}
@Override
+ public AndroidApiLevel maxSupportedApiLevel() {
+ // TODO: define the mapping from android API levels back to JDKs.
+ return AndroidApiLevel.LATEST;
+ }
+
+ @Override
public String toString() {
return vm.toString();
}
@@ -339,6 +356,8 @@
throw new Unreachable("Unexpected runtime without backend: " + this);
}
+ public abstract AndroidApiLevel maxSupportedApiLevel();
+
@Override
public abstract boolean equals(Object other);
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java
index 8624277..8cd7860 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java
@@ -165,6 +165,7 @@
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
.enableMemberValuePropagationAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.noMinification()
.addKeepRules(
"-checkdiscard class " + Base.class.getCanonicalName() + "{",
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/privateinstance/Sub1.java b/src/test/java/com/android/tools/r8/accessrelaxation/privateinstance/Sub1.java
index deb2559..c832fe7 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/privateinstance/Sub1.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/privateinstance/Sub1.java
@@ -5,8 +5,10 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
@NeverClassInline
+@NoHorizontalClassMerging
public class Sub1 extends Base implements Itf1 {
@Override
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/privateinstance/Sub2.java b/src/test/java/com/android/tools/r8/accessrelaxation/privateinstance/Sub2.java
index 15cc549..ffdeec6 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/privateinstance/Sub2.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/privateinstance/Sub2.java
@@ -5,8 +5,10 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
@NeverClassInline
+@NoHorizontalClassMerging
public class Sub2 extends Base implements Itf2 {
@Override
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
index 070d6c8..f2660ef 100644
--- a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
@@ -257,8 +257,8 @@
.setMode(mode)
.compile();
// Check that the process outputs (exit code, stdout, stderr) are the same.
- assertEquals(result.stdout(), runR8R8.stdout());
- assertEquals(result.stderr(), runR8R8.stderr());
+ assertEquals(result.getStdout(), runR8R8.getStdout());
+ assertEquals(result.getStderr(), runR8R8.getStderr());
// Check that the output jars are the same.
assertProgramsEqual(result.outputJar(), runR8R8.outputJar());
}
diff --git a/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java b/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
index 164259f..3ae4175 100644
--- a/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
+++ b/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
@@ -102,7 +102,8 @@
if (method.isInitializer()) {
continue;
}
- String methodName = method.holder().getName() + "_" + method.method.name.toString();
+ String methodName =
+ method.getHolderType().getName() + "_" + method.method.name.toString();
codePrinter.visitMethod(methodName, method.getCode().asCfCode());
}
});
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentInterfacesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentInterfacesTest.java
index d1333a3..3c9d066 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentInterfacesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentInterfacesTest.java
@@ -5,13 +5,13 @@
package com.android.tools.r8.classmerging.horizontal;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import org.junit.Test;
public class ClassesWithDifferentInterfacesTest extends HorizontalClassMergingTestBase {
@@ -31,8 +31,8 @@
.enableNeverClassInliningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
- .addHorizontallyMergedClassesInspector(
- HorizontallyMergedClassesInspector::assertNoClassesMerged)
+ .addHorizontallyMergedClassesInspectorIf(
+ enableHorizontalClassMerging, inspector -> inspector.assertMergedInto(Z.class, Y.class))
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("bar", "foo y", "bar")
.inspect(
@@ -40,7 +40,8 @@
assertThat(codeInspector.clazz(I.class), isPresent());
assertThat(codeInspector.clazz(X.class), isPresent());
assertThat(codeInspector.clazz(Y.class), isPresent());
- assertThat(codeInspector.clazz(Z.class), isPresent());
+ assertThat(
+ codeInspector.clazz(Z.class), notIf(isPresent(), enableHorizontalClassMerging));
});
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java
index 5c03d3b..2bab975 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java
@@ -30,14 +30,17 @@
.enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.addHorizontallyMergedClassesInspectorIf(
- enableHorizontalClassMerging, inspector -> inspector.assertMergedInto(Z.class, Y.class))
+ enableHorizontalClassMerging,
+ inspector ->
+ inspector.assertMergedInto(Y.class, X.class).assertMergedInto(Z.class, X.class))
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("bar", "foo y", "foo z")
.inspect(
codeInspector -> {
assertThat(codeInspector.clazz(I.class), isPresent());
assertThat(codeInspector.clazz(X.class), isPresent());
- assertThat(codeInspector.clazz(Y.class), isPresent());
+ assertThat(
+ codeInspector.clazz(Y.class), notIf(isPresent(), enableHorizontalClassMerging));
assertThat(
codeInspector.clazz(Z.class), notIf(isPresent(), enableHorizontalClassMerging));
});
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingAfterUnusedArgumentRemovalTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingAfterUnusedArgumentRemovalTest.java
new file mode 100644
index 0000000..32f7454
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingAfterUnusedArgumentRemovalTest.java
@@ -0,0 +1,78 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.classmerging.horizontal;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+
+public class ConstructorMergingAfterUnusedArgumentRemovalTest
+ extends HorizontalClassMergingTestBase {
+
+ public ConstructorMergingAfterUnusedArgumentRemovalTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .addHorizontallyMergedClassesInspectorIf(
+ enableHorizontalClassMerging,
+ inspector ->
+ inspector
+ .assertMergedInto(B.class, A.class)
+ .assertMergedInto(C.class, A.class)
+ .assertMergedInto(D.class, A.class))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(
+ "A.<init>(?, 43)", "B.<init>(44)", "C.<init>()", "D.<init>()");
+ }
+
+ @NeverClassInline
+ public static class A {
+
+ public A(int unused, int x) {
+ System.out.println("A.<init>(?, " + x + ")");
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+
+ public B(int x) {
+ System.out.println("B.<init>(" + x + ")");
+ }
+ }
+
+ @NeverClassInline
+ public static class C {
+ public C() {
+ System.out.println("C.<init>()");
+ }
+ }
+
+ @NeverClassInline
+ public static class D {
+ public D() {
+ System.out.println("D.<init>()");
+ }
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ new A(42, 43);
+ new B(44);
+ new C();
+ new D();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/ConflictWasDetectedTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/ConflictWasDetectedTest.java
new file mode 100644
index 0000000..8b24e99
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/ConflictWasDetectedTest.java
@@ -0,0 +1,98 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.classmerging.vertical;
+
+import com.android.tools.r8.KeepUnusedArguments;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ConflictWasDetectedTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public ConflictWasDetectedTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addVerticallyMergedClassesInspector(
+ VerticallyMergedClassesInspector::assertNoClassesMerged)
+ .enableInliningAnnotations()
+ // .enableNoHorizontalClassMergingAnnotations()
+ .enableUnusedArgumentAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class);
+ }
+
+ static class Main {
+
+ public static void main(String... args) {
+ ConflictingInterfaceImpl impl = new ConflictingInterfaceImpl();
+ callMethodOnIface(impl);
+
+ // Ensure that the instantiations are not dead code eliminated.
+ escape(impl);
+ }
+
+ private static void callMethodOnIface(ConflictingInterface iface) {
+ System.out.println(iface.method());
+ System.out.println(ClassWithConflictingMethod.conflict(null));
+ System.out.println(OtherClassWithConflictingMethod.conflict(null));
+ }
+
+ @NeverInline
+ private static void escape(Object o) {
+ if (System.currentTimeMillis() < 0) {
+ System.out.println(o);
+ }
+ }
+ }
+
+ public interface ConflictingInterface {
+
+ String method();
+ }
+
+ public static class ConflictingInterfaceImpl implements ConflictingInterface {
+
+ @Override
+ public String method() {
+ return "ConflictingInterfaceImpl::method";
+ }
+ }
+
+ public static class ClassWithConflictingMethod {
+
+ @KeepUnusedArguments
+ public static int conflict(ConflictingInterface item) {
+ return 123;
+ }
+ }
+
+ public static class OtherClassWithConflictingMethod {
+
+ @KeepUnusedArguments
+ public static int conflict(ConflictingInterfaceImpl item) {
+ return 321;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/LambdaRewritingTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/LambdaRewritingTest.java
new file mode 100644
index 0000000..4cb9707
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/LambdaRewritingTest.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.classmerging.vertical;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class LambdaRewritingTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public LambdaRewritingTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addVerticallyMergedClassesInspector(
+ inspector -> inspector.assertMergedIntoSubtype(Interface.class))
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class);
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ Interface obj = new InterfaceImpl();
+
+ // Leads to an invoke-custom instruction that mentions the type of `obj` since it is captured.
+ invoke(() -> obj.foo());
+
+ FunctionImpl functionImpl = new FunctionImpl();
+ if (System.currentTimeMillis() < 0) {
+ System.out.println(functionImpl);
+ }
+ }
+
+ @NeverInline
+ private static void invoke(Function f) {
+ f.accept();
+ }
+ }
+
+ // Cannot be merged as it has two subtypes: FunctionImpl and a lambda.
+ public interface Function {
+
+ void accept();
+ }
+
+ public static class FunctionImpl implements Function {
+
+ @Override
+ public void accept() {
+ System.out.println("In FunctionImpl.accept()");
+ }
+ }
+
+ // Will be merged into InterfaceImpl.
+ public interface Interface {
+
+ void foo();
+ }
+
+ public static class InterfaceImpl implements Interface {
+
+ @Override
+ public void foo() {
+ System.out.println("In InterfaceImpl.foo()");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
index d7d31c9..2fb36cd 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -305,13 +305,6 @@
}
@Test
- public void testConflictWasDetected() throws Throwable {
- runR8(EXAMPLE_KEEP, this::configure);
- assertThat(inspector.clazz("classmerging.ConflictingInterface"), isPresent());
- assertThat(inspector.clazz("classmerging.ConflictingInterfaceImpl"), isPresent());
- }
-
- @Test
public void testFieldCollision() throws Throwable {
String main = "classmerging.FieldCollisionTest";
Path[] programFiles =
@@ -334,34 +327,6 @@
}
@Test
- public void testLambdaRewriting() throws Throwable {
- String main = "classmerging.LambdaRewritingTest";
- Path[] programFiles =
- new Path[] {
- JAVA8_CF_DIR.resolve("LambdaRewritingTest.class"),
- JAVA8_CF_DIR.resolve("LambdaRewritingTest$Function.class"),
- JAVA8_CF_DIR.resolve("LambdaRewritingTest$FunctionImpl.class"),
- JAVA8_CF_DIR.resolve("LambdaRewritingTest$Interface.class"),
- JAVA8_CF_DIR.resolve("LambdaRewritingTest$InterfaceImpl.class")
- };
- Set<String> preservedClassNames =
- ImmutableSet.of(
- "classmerging.LambdaRewritingTest",
- "classmerging.LambdaRewritingTest$Function",
- "classmerging.LambdaRewritingTest$FunctionImpl",
- "classmerging.LambdaRewritingTest$InterfaceImpl");
- runTestOnInput(
- testForR8(parameters.getBackend())
- .addKeepRules(getProguardConfig(JAVA8_EXAMPLE_KEEP))
- .addOptionsModification(this::configure)
- .addOptionsModification(options -> options.enableClassInlining = false)
- .allowUnusedProguardConfigurationRules(),
- main,
- readProgramFiles(programFiles),
- name -> preservedClassNames.contains(name) || name.contains("$Lambda$"));
- }
-
- @Test
public void testMergeInterfaceWithoutInlining() throws Throwable {
String main = "classmerging.ConflictingInterfaceSignaturesTest";
Path[] programFiles =
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java b/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
index b4a4c48..d094c78 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
@@ -8,6 +8,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.TestBase;
@@ -49,7 +50,11 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withDexRuntimes().withApiLevel(AndroidApiLevel.J).build();
+ return getTestParameters()
+ .withAllRuntimes()
+ .withApiLevel(AndroidApiLevel.J)
+ .enableApiLevelsForCf()
+ .build();
}
public BackportDuplicationTest(TestParameters parameters) {
@@ -58,6 +63,8 @@
@Test
public void testR8() throws Exception {
+ // R8 does not support desugaring with class file output so this test is only valid for DEX.
+ assumeTrue(parameters.isDexRuntime());
runR8(false);
runR8(true);
}
@@ -87,6 +94,9 @@
@Test
public void testD8Merging() throws Exception {
+ assumeTrue(
+ "b/147485959: Merging does not happen for CF due to lack of synthetic annotations",
+ parameters.isDexRuntime());
boolean intermediate = true;
runD8Merging(intermediate);
}
@@ -100,7 +110,7 @@
private void runD8Merging(boolean intermediate) throws Exception {
// Compile part 1 of the input (maybe intermediate)
Path out1 =
- testForD8()
+ testForD8(parameters.getBackend())
.addProgramClasses(User1.class)
.addClasspathClasses(CLASSES)
.setMinApi(parameters.getApiLevel())
@@ -110,7 +120,7 @@
// Compile part 2 of the input (maybe intermediate)
Path out2 =
- testForD8()
+ testForD8(parameters.getBackend())
.addProgramClasses(User2.class)
.addClasspathClasses(CLASSES)
.setMinApi(parameters.getApiLevel())
@@ -126,7 +136,7 @@
// Merge parts as an intermediate artifact.
// This will not merge synthetics regardless of the setting of intermediate.
Path out3 = temp.newFolder().toPath().resolve("out3.zip");
- testForD8()
+ testForD8(parameters.getBackend())
.addProgramClasses(MiniAssert.class, TestClass.class)
.addProgramFiles(out1, out2)
.setMinApi(parameters.getApiLevel())
@@ -139,7 +149,7 @@
.inspect(inspector -> assertEquals(syntheticsInParts, getSyntheticMethods(inspector)));
// Finally do a non-intermediate merge.
- testForD8()
+ testForD8(parameters.getBackend())
.addProgramFiles(out3)
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), TestClass.class)
@@ -160,11 +170,13 @@
@Test
public void testD8FilePerClassFile() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
runD8FilePerMode(OutputMode.DexFilePerClassFile);
}
@Test
public void testD8FilePerClass() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
runD8FilePerMode(OutputMode.DexFilePerClass);
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmptyDesugaredLibrary.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmptyDesugaredLibrary.java
index 3abe853..5b706e8 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmptyDesugaredLibrary.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmptyDesugaredLibrary.java
@@ -13,6 +13,8 @@
import com.android.tools.r8.L8Command;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.AndroidApiLevel;
import java.nio.charset.StandardCharsets;
@@ -21,9 +23,22 @@
import java.util.Set;
import java.util.zip.ZipFile;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+@RunWith(Parameterized.class)
public class EmptyDesugaredLibrary extends DesugaredLibraryTestBase {
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public EmptyDesugaredLibrary(TestParameters parameters) {
+ parameters.assertNoneRuntime();
+ }
+
private L8Command.Builder prepareL8Builder(AndroidApiLevel minApiLevel) {
return L8Command.builder()
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/IteratorTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/IteratorTest.java
index 2a968a0..46cb2bd 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/IteratorTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/IteratorTest.java
@@ -112,7 +112,9 @@
diagnosticMessage(
containsString(
"Code has already been library desugared. "
- + "Interface Lj$/util/Iterator; is already implemented"))));
+ + "Interface Lj$/util/Iterator; is already implemented by "
+ + "Lcom/android/tools/r8/desugar/desugaredlibrary/"
+ + "IteratorTest$MyIterator;"))));
fail("Expected failure");
} catch (CompilationFailedException e) {
// Expected.
@@ -124,7 +126,7 @@
testForD8(Backend.CF)
.addOptionsModification(
options ->
- options.desugarSpecificOptions().allowDesugaredInput =
+ options.desugarSpecificOptions().allowAllDesugaredInput =
parameters.getApiLevel().isLessThan(apiLevelNotRequiringDesugaring))
.setMinApi(parameters.getApiLevel())
.addProgramFiles(firstJar)
@@ -141,9 +143,8 @@
assertEquals(
canUseDefaultAndStaticInterfaceMethods ? 0 : 1,
info.getInterfaces().stream().filter(name -> name.equals("j$/util/Iterator")).count());
- // TODO(b/171867367): This should only be 2.
assertEquals(
- canUseDefaultAndStaticInterfaceMethods ? 1 : 3,
+ canUseDefaultAndStaticInterfaceMethods ? 1 : 2,
info.getMethodNames().stream().filter(name -> name.equals("forEachRemaining")).count());
if (parameters.getRuntime().isDex()) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/CallBackConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/CallBackConversionTest.java
index 210216f..cb8c24f 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/CallBackConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/CallBackConversionTest.java
@@ -127,6 +127,8 @@
// Use D8 to desugar with Java classfile output.
Path secondJar =
testForD8(Backend.CF)
+ .addOptionsModification(
+ options -> options.desugarSpecificOptions().allowAllDesugaredInput = true)
.setMinApi(parameters.getApiLevel())
.addProgramFiles(firstJar)
.addLibraryClasses(CustomLibClass.class)
@@ -141,8 +143,7 @@
assertEquals(
Impl.class.getTypeName(),
DescriptorUtils.getJavaTypeFromBinaryName(info.getClassBinaryName()));
- // TODO(b/171867367): This should only be 2.
- assertEquals(3, info.getMethodNames().stream().filter(name -> name.equals("foo")).count());
+ assertEquals(2, info.getMethodNames().stream().filter(name -> name.equals("foo")).count());
// Convert to DEX without desugaring and run.
testForD8()
diff --git a/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java b/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
index 0fe19bc..7ea9110 100644
--- a/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
+++ b/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
@@ -3,6 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.dex;
+import static com.android.tools.r8.TestBase.getTestParameters;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication;
@@ -16,12 +20,26 @@
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.Timing;
import java.util.Collections;
import org.junit.Assert;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+@RunWith(Parameterized.class)
public class DebugByteCodeWriterTest {
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public DebugByteCodeWriterTest(TestParameters parameters) {
+ parameters.assertNoneRuntime();
+ }
+
private ObjectToOffsetMapping emptyObjectTObjectMapping() {
return new ObjectToOffsetMapping(
AppView.createForD8(
@@ -39,7 +57,8 @@
Collections.emptyList(),
Collections.emptyList(),
Collections.emptyList(),
- Collections.emptyList());
+ Collections.emptyList(),
+ Timing.empty());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/DumpInputsTest.java b/src/test/java/com/android/tools/r8/dump/DumpInputsTest.java
similarity index 93%
rename from src/test/java/com/android/tools/r8/DumpInputsTest.java
rename to src/test/java/com/android/tools/r8/dump/DumpInputsTest.java
index 84febed..008846d 100644
--- a/src/test/java/com/android/tools/r8/DumpInputsTest.java
+++ b/src/test/java/com/android/tools/r8/dump/DumpInputsTest.java
@@ -1,13 +1,17 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// 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;
+package com.android.tools.r8.dump;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.ZipUtils;
import java.io.IOException;
diff --git a/src/test/java/com/android/tools/r8/dump/DumpMainDexInputsTest.java b/src/test/java/com/android/tools/r8/dump/DumpMainDexInputsTest.java
new file mode 100644
index 0000000..ede7822
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/dump/DumpMainDexInputsTest.java
@@ -0,0 +1,135 @@
+// 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.dump;
+
+import static java.util.stream.Collectors.toList;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ZipUtils;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Stream;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class DumpMainDexInputsTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ public DumpMainDexInputsTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private Path newMainDexListPath() throws IOException {
+ Path mainDexPath = temp.newFile("main-dex-list.txt").toPath();
+ String mainDexList =
+ StringUtils.lines(toMainDexFormat(MainDexFile1.class), toMainDexFormat(MainDexFile2.class));
+ Files.write(mainDexPath, mainDexList.getBytes());
+ return mainDexPath;
+ }
+
+ private static String toMainDexFormat(Class<?> clazz) {
+ return clazz.getName().replace(".", "/") + FileUtils.CLASS_EXTENSION;
+ }
+
+ @Test
+ public void testD8MainDexList() throws Exception {
+ Assume.assumeTrue(
+ "pre-native-multidex only",
+ parameters.getApiLevel().isLessThanOrEqualTo(AndroidApiLevel.K));
+ Path dumpDir = temp.newFolder().toPath();
+ testForD8()
+ .setMinApi(parameters.getApiLevel())
+ .addInnerClasses(DumpMainDexInputsTest.class)
+ .addMainDexListFiles(Collections.singleton(newMainDexListPath()))
+ .addMainDexListClasses(MainDexClass1.class, MainDexClass2.class, TestClass.class)
+ .addLibraryFiles(ToolHelper.getJava8RuntimeJar())
+ .addOptionsModification(options -> options.dumpInputToDirectory = dumpDir.toString())
+ .compile()
+ .assertAllInfoMessagesMatch(containsString("Dumped compilation inputs to:"))
+ .assertAllWarningMessagesMatch(
+ containsString(
+ "Dumping main dex list resources may have side effects due to I/O on Paths."))
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello, world");
+ verifyDumpDir(dumpDir);
+ }
+
+ private void verifyDumpDir(Path dumpDir) throws IOException {
+ assertTrue(Files.isDirectory(dumpDir));
+ List<Path> paths = Files.walk(dumpDir, 1).collect(toList());
+ boolean hasVerified = false;
+ for (Path path : paths) {
+ if (!path.equals(dumpDir)) {
+ // The non-external run here results in assert code calling application read.
+ verifyDump(path);
+ hasVerified = true;
+ }
+ }
+ assertTrue(hasVerified);
+ }
+
+ private void verifyDump(Path dumpFile) throws IOException {
+ assertTrue(Files.exists(dumpFile));
+ Path unzipped = temp.newFolder().toPath();
+ ZipUtils.unzip(dumpFile.toString(), unzipped.toFile());
+ assertTrue(Files.exists(unzipped.resolve("r8-version")));
+ assertTrue(Files.exists(unzipped.resolve("program.jar")));
+ assertTrue(Files.exists(unzipped.resolve("library.jar")));
+ assertTrue(Files.exists(unzipped.resolve("main-dex-list.txt")));
+ String mainDex = new String(Files.readAllBytes(unzipped.resolve("main-dex-list.txt")));
+ List<String> mainDexLines =
+ Arrays.stream(mainDex.split("\n")).filter(s -> !s.isEmpty()).collect(toList());
+ assertEquals(5, mainDexLines.size());
+ List<String> expected =
+ Stream.of(
+ MainDexFile1.class,
+ MainDexFile2.class,
+ MainDexClass1.class,
+ MainDexClass2.class,
+ TestClass.class)
+ .map(DumpMainDexInputsTest::toMainDexFormat)
+ .collect(toList());
+ assertEquals(expected, mainDexLines);
+ }
+
+ static class TestClass {
+ public static void main(String[] args) {
+ System.out.println("Hello, world");
+ }
+ }
+
+ static class MainDexClass1 {}
+
+ static class MainDexClass2 {}
+
+ static class MainDexFile1 {}
+
+ static class MainDexFile2 {}
+
+ static class NonMainDex1 {}
+
+ static class NonMainDex2 {}
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
index 82f21d4..51b5323 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
@@ -101,7 +101,7 @@
.asLookupResultSuccess();
assertNotNull(lookupResult);
assertFalse(lookupResult.hasLambdaTargets());
- if (subtypingInfo.subtypes(method.holder()).stream()
+ if (subtypingInfo.subtypes(method.getHolderType()).stream()
.allMatch(t -> appInfo().definitionFor(t).isInterface())) {
Counter counter = new Counter();
lookupResult.forEach(
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
index cd743a9..d88e281 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
@@ -82,9 +82,13 @@
.addKeepMainRule("proto2.TestClass")
.addKeepRuleFiles(PROTOBUF_LITE_PROGUARD_RULES)
.addKeepRules(allGeneratedMessageLiteSubtypesAreInstantiatedRule())
+ // TODO(b/173340579): This rule should not be needed to allow shrinking of
+ // PartiallyUsed$Enum.
+ .addNoHorizontalClassMergingRule(PARTIALLY_USED + "$Enum$1")
.allowAccessModification(allowAccessModification)
.allowDiagnosticMessages()
.allowUnusedProguardConfigurationRules()
+ .enableProguardTestOptions()
.enableProtoShrinking()
.minification(enableMinification)
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
index cf52085..e0266bd 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.optimize.classinliner;
import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
@@ -88,9 +89,15 @@
.enableSideEffectAnnotations()
.addKeepMainRule(main)
.addKeepAttributes("LineNumberTable")
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector
+ .assertMergedInto(Iface1Impl.class, CycleReferenceBA.class)
+ .assertMergedInto(Iface2Impl.class, CycleReferenceBA.class))
.allowAccessModification()
.noMinification()
- .run(main)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), main)
.assertSuccessWithOutput(javaOutput);
CodeInspector inspector = result.inspector();
@@ -143,7 +150,7 @@
"com.android.tools.r8.ir.optimize.classinliner.trivial.CycleReferenceAB"),
collectTypes(inspector.clazz(CycleReferenceAB.class).uniqueMethodWithName("foo")));
- assertFalse(inspector.clazz(CycleReferenceBA.class).isPresent());
+ assertThat(inspector.clazz(CycleReferenceBA.class), isAbsent());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/ValuePropagationWithCatchHandlersTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/ValuePropagationWithCatchHandlersTest.java
new file mode 100644
index 0000000..32dbb5d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/ValuePropagationWithCatchHandlersTest.java
@@ -0,0 +1,69 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.membervaluepropagation;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ValuePropagationWithCatchHandlersTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return TestBase.getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public ValuePropagationWithCatchHandlersTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Woops!");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ try {
+ test();
+ } catch (ExceptionInInitializerError e) {
+ System.out.println("Woops!");
+ }
+ }
+
+ static synchronized void test() {
+ System.out.println(Greeter.getInstance());
+ }
+ }
+
+ static class Greeter {
+
+ static final Greeter INSTANCE = new Greeter();
+
+ static {
+ if (System.currentTimeMillis() > 0) {
+ throw new RuntimeException();
+ }
+ }
+
+ public static Greeter getInstance() {
+ return INSTANCE;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
index 8dbe868..3e28157 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
@@ -37,6 +37,7 @@
@NoHorizontalClassMerging
static class EffectivelyFinal {}
+ @NoHorizontalClassMerging
static class Reflection implements Callable<Class<?>> {
@ForceInline
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapInvalidOrdinalTest.java b/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapInvalidOrdinalTest.java
new file mode 100644
index 0000000..cbbd4f0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapInvalidOrdinalTest.java
@@ -0,0 +1,101 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.switches;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class SwitchMapInvalidOrdinalTest extends TestBase {
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ public SwitchMapInvalidOrdinalTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ testForD8()
+ .setMinApi(parameters.getApiLevel())
+ .addInnerClasses(SwitchMapInvalidOrdinalTest.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("a", "b", "0", "a");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addKeepMainRule(Main.class)
+ .addKeepRules(
+ "-keep class"
+ + "com.android.tools.r8.ir.optimize.switches.SwitchMapInvalidOrdinalTest$MyEnum {"
+ + " static <fields>; }")
+ .addInnerClasses(SwitchMapInvalidOrdinalTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ // When the code reaches the switch the first time, then the switch map int[] gets
+ // initialized based on the values in the enum at this point creating a mapping ordinal to
+ // switch map entry. Here D and X have 3 as ordinal.
+ .assertSuccessWithOutputLines("a", "b", "0", "a");
+ }
+
+ @NeverClassInline
+ enum MyEnum {
+ A,
+ B,
+ C,
+ D;
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ try {
+ // Use reflection to instantiate a new enum instance, and set it to A, using getFields
+ // and not getField since A is minified.
+ Constructor<MyEnum> constructor =
+ (Constructor<MyEnum>) MyEnum.class.getDeclaredConstructors()[0];
+ constructor.setAccessible(true);
+ MyEnum x = constructor.newInstance("X", 3);
+ Field f = MyEnum.class.getFields()[0];
+
+ // On Android, setAccessible allows to set a final field.
+ f.setAccessible(true);
+
+ f.set(null, x);
+ } catch (Exception e) {
+ System.out.println("Unexpected: " + e);
+ }
+ print(MyEnum.A);
+ print(MyEnum.B);
+ print(MyEnum.C);
+ print(MyEnum.D);
+ }
+
+ private static void print(MyEnum e) {
+ switch (e) {
+ case A:
+ System.out.println("a");
+ break;
+ case B:
+ System.out.println("b");
+ break;
+ default:
+ System.out.println("0");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/b173184123/ClassExtendsInterfaceNamingTest.java b/src/test/java/com/android/tools/r8/naming/b173184123/ClassExtendsInterfaceNamingTest.java
new file mode 100644
index 0000000..fccc7b2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/b173184123/ClassExtendsInterfaceNamingTest.java
@@ -0,0 +1,97 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming.b173184123;
+
+import static org.hamcrest.CoreMatchers.containsString;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
+import java.nio.file.Path;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+// This is a reproduction of b/173184123.
+@RunWith(Parameterized.class)
+public class ClassExtendsInterfaceNamingTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public ClassExtendsInterfaceNamingTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ Path classFiles = temp.newFile("classes.jar").toPath();
+ ZipBuilder.builder(classFiles)
+ .addFilesRelative(
+ ToolHelper.getClassPathForTests(),
+ ToolHelper.getClassFileForTestClass(Interface.class),
+ ToolHelper.getClassFileForTestClass(Main.class))
+ .addBytes(
+ DescriptorUtils.getPathFromJavaType(ConcreteClass.class),
+ transformer(ConcreteClass.class)
+ .setSuper(DescriptorUtils.javaTypeToDescriptor(Interface.class.getTypeName()))
+ .transform())
+ .build();
+ testForExternalR8(
+ parameters.getBackend(),
+ parameters.isCfRuntime() ? parameters.getRuntime() : TestRuntime.getCheckedInJdk11())
+ .addProgramFiles(classFiles)
+ .enableAssertions(false)
+ .useR8WithRelocatedDeps()
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .addKeepClassAndMembersRules(Interface.class)
+ .addKeepRules("-neverclassinline @com.android.tools.r8.NeverClassInline class *")
+ .addKeepRules("-neverinline class * { @**.NeverInline *; }")
+ .allowTestProguardOptions(true)
+ .compile()
+ .assertStderrThatMatches(
+ containsString(
+ "Class "
+ + ConcreteClass.class.getTypeName()
+ + " extends "
+ + Interface.class.getTypeName()
+ + " which is an interface"))
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
+ }
+
+ public interface Interface {
+ void foo();
+ }
+
+ @NeverClassInline
+ public static class ConcreteClass /* extends InterfaceSub */ implements Interface {
+
+ @Override
+ @NeverInline
+ public void foo() {
+ System.out.println("foo");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new ConcreteClass().foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageProtectedInSeparatePackageTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageProtectedInSeparatePackageTest.java
new file mode 100644
index 0000000..a455d0e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageProtectedInSeparatePackageTest.java
@@ -0,0 +1,81 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.repackage;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageProtectedInSeparatePackageTest extends RepackageTestBase {
+
+ private final String EXPECTED = "Base::foo";
+
+ public RepackageProtectedInSeparatePackageTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(Base.class, Sub.class, Main.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8WithRepackage() throws Exception {
+ R8TestRunResult runResult =
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Base.class, Sub.class, Main.class)
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepClassAndMembersRules(Base.class)
+ .apply(this::configureRepackaging)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .compile()
+ .inspect(
+ inspector -> {
+ // TODO(b/173584786): We should not repackage Sub when generating CF.
+ assertThat(Sub.class, isRepackaged(inspector));
+ })
+ .run(parameters.getRuntime(), Main.class);
+ if (parameters.isCfRuntime()) {
+ runResult.assertFailureWithErrorThatThrows(VerifyError.class);
+ } else {
+ runResult.assertSuccessWithOutputLines(EXPECTED);
+ }
+ }
+
+ public static class Base {
+ protected void foo() {
+ System.out.println("Base::foo");
+ }
+ }
+
+ @NeverClassInline
+ public static class Sub extends Base {
+
+ @NeverInline
+ public void callFoo(Base base) {
+ base.foo();
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new Sub().callFoo(new Sub());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateLibraryMethodTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateLibraryMethodTest.java
new file mode 100644
index 0000000..9eefd15
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateLibraryMethodTest.java
@@ -0,0 +1,78 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.repackage;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageWithPackagePrivateLibraryMethodTest extends RepackageTestBase {
+
+ private final String[] EXPECTED = new String[] {"Library::foo", "Program::bar"};
+
+ public RepackageWithPackagePrivateLibraryMethodTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addLibraryClasses(Library.class)
+ .addProgramClasses(Program.class, Main.class)
+ .addRunClasspathFiles(buildOnDexRuntime(parameters, Library.class))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ R8TestRunResult runResult =
+ testForR8(parameters.getBackend())
+ .addLibraryClasses(Library.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .addProgramClasses(Program.class, Main.class)
+ .apply(this::configureRepackaging)
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .addRunClasspathFiles(buildOnDexRuntime(parameters, Library.class))
+ .run(parameters.getRuntime(), Main.class);
+ if (parameters.isDexRuntime() && parameters.getDexRuntimeVersion() == Version.V8_1_0) {
+ runResult.assertSuccessWithOutputLines(EXPECTED);
+ } else {
+ runResult.assertFailureWithErrorThatThrows(IllegalAccessError.class);
+ }
+ }
+
+ public static class Library {
+
+ static void foo() {
+ System.out.println("Library::foo");
+ }
+ }
+
+ public static class Program {
+
+ @NeverInline
+ public static void bar() {
+ Library.foo();
+ System.out.println("Program::bar");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ Program.bar();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateLibraryOverrideTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateLibraryOverrideTest.java
new file mode 100644
index 0000000..7773fef
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateLibraryOverrideTest.java
@@ -0,0 +1,81 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.repackage;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageWithPackagePrivateLibraryOverrideTest extends RepackageTestBase {
+
+ private final String[] EXPECTED = new String[] {"Program::foo"};
+
+ public RepackageWithPackagePrivateLibraryOverrideTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addLibraryClasses(Library.class)
+ .addProgramClasses(Program.class, Main.class)
+ .addRunClasspathFiles(buildOnDexRuntime(parameters, Library.class))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ R8TestRunResult runResult =
+ testForR8(parameters.getBackend())
+ .addLibraryClasses(Library.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .addProgramClasses(Program.class, Main.class)
+ .apply(this::configureRepackaging)
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableNeverClassInliningAnnotations()
+ .compile()
+ .addRunClasspathFiles(buildOnDexRuntime(parameters, Library.class))
+ .run(parameters.getRuntime(), Main.class);
+ if (parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isDalvik()) {
+ runResult.assertSuccessWithOutputLines(EXPECTED);
+ } else {
+ runResult.assertSuccessWithOutputLines("Library::foo");
+ }
+ }
+
+ public static class Library {
+
+ void foo() {
+ System.out.println("Library::foo");
+ }
+
+ public void callFoo() {
+ foo();
+ }
+ }
+
+ @NeverClassInline
+ public static class Program extends Library {
+
+ @Override
+ void foo() {
+ System.out.println("Program::foo");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new Program().callFoo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateLibrarySuperTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateLibrarySuperTest.java
new file mode 100644
index 0000000..405c493
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateLibrarySuperTest.java
@@ -0,0 +1,70 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.repackage;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageWithPackagePrivateLibrarySuperTest extends RepackageTestBase {
+
+ private final String[] EXPECTED = new String[] {"Library::foo"};
+
+ public RepackageWithPackagePrivateLibrarySuperTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addLibraryClasses(Library.class)
+ .addProgramClasses(Program.class, Main.class)
+ .addRunClasspathFiles(buildOnDexRuntime(parameters, Library.class))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addLibraryClasses(Library.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .addProgramClasses(Program.class, Main.class)
+ .apply(this::configureRepackaging)
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableNeverClassInliningAnnotations()
+ .compile()
+ .addRunClasspathFiles(buildOnDexRuntime(parameters, Library.class))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ public static class Library {
+
+ void foo() {
+ System.out.println("Library::foo");
+ }
+ }
+
+ @NeverClassInline
+ public static class Program extends Library {
+
+ void callFoo() {
+ super.foo();
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new Program().callFoo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateLibraryTypeTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateLibraryTypeTest.java
new file mode 100644
index 0000000..1a35ded
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateLibraryTypeTest.java
@@ -0,0 +1,93 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.repackage;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageWithPackagePrivateLibraryTypeTest extends RepackageTestBase {
+
+ private final String[] EXPECTED =
+ new String[] {
+ "class com.android.tools.r8.repackage.RepackageWithPackagePrivateLibraryTypeTest$Library",
+ "ProgramSub::foo",
+ "ProgramSub2::foo"
+ };
+
+ public RepackageWithPackagePrivateLibraryTypeTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addLibraryClasses(Library.class, LibraryI.class)
+ .addProgramClasses(Program.class, ProgramSub.class, ProgramSub2.class, Main.class)
+ .addRunClasspathFiles(buildOnDexRuntime(parameters, Library.class, LibraryI.class))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addLibraryClasses(Library.class, LibraryI.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .addProgramClasses(Program.class, ProgramSub.class, ProgramSub2.class, Main.class)
+ .apply(this::configureRepackaging)
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .addRunClasspathFiles(buildOnDexRuntime(parameters, Library.class, LibraryI.class))
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatThrows(IllegalAccessError.class);
+ }
+
+ static class Library {}
+
+ interface LibraryI {}
+
+ public static class Program {
+
+ @NeverInline
+ public static void bar() {
+ System.out.println(Library.class.toString());
+ }
+ }
+
+ @NeverClassInline
+ public static class ProgramSub extends Library {
+
+ @NeverInline
+ public void foo() {
+ System.out.println("ProgramSub::foo");
+ }
+ }
+
+ @NeverClassInline
+ public static class ProgramSub2 implements LibraryI {
+
+ @NeverInline
+ public void foo() {
+ System.out.println("ProgramSub2::foo");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ Program.bar();
+ new ProgramSub().foo();
+ new ProgramSub2().foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleTargetExecutionTest.java b/src/test/java/com/android/tools/r8/resolution/SingleTargetExecutionTest.java
index bae2760..c6efef7 100644
--- a/src/test/java/com/android/tools/r8/resolution/SingleTargetExecutionTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/SingleTargetExecutionTest.java
@@ -99,6 +99,7 @@
.addProgramClasses(CLASSES)
.addProgramClassFileData(ASM_CLASSES)
.addKeepMainRule(Main.class)
+ .enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
index c119db0..a702874 100644
--- a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
@@ -188,7 +188,8 @@
Assert.assertNull(singleVirtualTarget);
} else {
Assert.assertNotNull(singleVirtualTarget);
- Assert.assertEquals(toType(singleTargetHolderOrNull, appInfo), singleVirtualTarget.holder());
+ Assert.assertEquals(
+ toType(singleTargetHolderOrNull, appInfo), singleVirtualTarget.getHolderType());
}
}
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
index 65e2ed5..6f14877 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
@@ -136,7 +136,7 @@
DexEncodedMethod targetSuper =
resolutionResult.lookupInvokeSuperTarget(callerClassDefinition, appInfo);
if (inSameNest) {
- assertEquals(definingClassDefinition.type, targetSpecial.holder());
+ assertEquals(definingClassDefinition.type, targetSpecial.getHolderType());
assertEquals(targetSpecial, targetSuper);
} else {
assertNull(targetSpecial);
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
index 25a5d9e..6c7e5f2 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
@@ -112,7 +112,7 @@
DexEncodedMethod targetSuper =
resolutionResult.lookupInvokeSuperTarget(callerClassDefinition, appInfo);
if (inSameNest) {
- assertEquals(definingClassDefinition.type, targetSpecial.holder());
+ assertEquals(definingClassDefinition.type, targetSpecial.getHolderType());
assertEquals(targetSpecial, targetSuper);
} else {
assertNull(targetSpecial);
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
index 4453f58..fe17f34 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
@@ -145,7 +145,7 @@
DexEncodedMethod targetSuper =
resolutionResult.lookupInvokeSuperTarget(callerClassDefinition, appInfo);
if (inSameNest && symbolicReferenceIsDefiningType) {
- assertEquals(definingClassDefinition.type, targetSpecial.holder());
+ assertEquals(definingClassDefinition.type, targetSpecial.getHolderType());
assertEquals(targetSpecial, targetSuper);
} else {
assertNull(targetSpecial);
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java
index aced457..9aa12b3 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java
@@ -110,7 +110,7 @@
// Verify that looking up the dispatch target returns the defining method.
DexEncodedMethod targetSpecial =
resolutionResult.lookupInvokeSpecialTarget(callerClassDefinition, appInfo);
- assertEquals(definingClassDefinition.type, targetSpecial.holder());
+ assertEquals(definingClassDefinition.type, targetSpecial.getHolderType());
DexEncodedMethod targetSuper =
resolutionResult.lookupInvokeSuperTarget(callerClassDefinition, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/AbstractAllTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/AbstractAllTest.java
index b031eca..44f969f 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/AbstractAllTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/AbstractAllTest.java
@@ -48,7 +48,7 @@
DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
// Currently R8 will resolve to L::f as that is the first in the topological search.
// Resolution may return any of the matches, so it is valid if this expectation changes.
- assertEquals(L.class.getTypeName(), resolutionTarget.holder().toSourceString());
+ assertEquals(L.class.getTypeName(), resolutionTarget.getHolderType().toSourceString());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java
index 429b8d9..990d146 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java
@@ -51,7 +51,7 @@
DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
- assertEquals(L.class.getTypeName(), resolutionTarget.holder().toSourceString());
+ assertEquals(L.class.getTypeName(), resolutionTarget.getHolderType().toSourceString());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java
index a1e3185..32d88b3 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java
@@ -51,7 +51,7 @@
DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
- assertEquals(R.class.getTypeName(), resolutionTarget.holder().toSourceString());
+ assertEquals(R.class.getTypeName(), resolutionTarget.getHolderType().toSourceString());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java
index 44c41eb..91b71ce 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java
@@ -54,7 +54,7 @@
DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
- assertEquals(L.class.getTypeName(), resolutionTarget.holder().toSourceString());
+ assertEquals(L.class.getTypeName(), resolutionTarget.getHolderType().toSourceString());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java
index 7683037..61f9725 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java
@@ -54,7 +54,7 @@
DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
- assertEquals(R.class.getTypeName(), resolutionTarget.holder().toSourceString());
+ assertEquals(R.class.getTypeName(), resolutionTarget.getHolderType().toSourceString());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java
index 6fa3216..5c8f813 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java
@@ -55,7 +55,7 @@
Set<String> holders = new HashSet<>();
resolutionResult
.asFailedResolution()
- .forEachFailureDependency(target -> holders.add(target.holder().toSourceString()));
+ .forEachFailureDependency(target -> holders.add(target.getHolderType().toSourceString()));
assertEquals(ImmutableSet.of(L.class.getTypeName(), R.class.getTypeName()), holders);
}
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java
index ccac0ed..74fb903 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java
@@ -45,7 +45,7 @@
DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
- assertEquals(L.class.getTypeName(), resolutionTarget.holder().toSourceString());
+ assertEquals(L.class.getTypeName(), resolutionTarget.getHolderType().toSourceString());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java
index 54a9b59..13f9340 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java
@@ -45,7 +45,7 @@
DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
- assertEquals(R.class.getTypeName(), resolutionTarget.holder().toSourceString());
+ assertEquals(R.class.getTypeName(), resolutionTarget.getHolderType().toSourceString());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java
index a311b5a..8bc64cf 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java
@@ -56,7 +56,7 @@
Set<String> holders = new HashSet<>();
resolutionResult
.asFailedResolution()
- .forEachFailureDependency(m -> holders.add(m.holder().toSourceString()));
+ .forEachFailureDependency(m -> holders.add(m.getHolderType().toSourceString()));
assertEquals(ImmutableSet.of(I.class.getTypeName(), J.class.getTypeName()), holders);
}
diff --git a/src/test/java/com/android/tools/r8/resolution/singletarget/one/SubSubClassOne.java b/src/test/java/com/android/tools/r8/resolution/singletarget/one/SubSubClassOne.java
index ff585af..c140fed 100644
--- a/src/test/java/com/android/tools/r8/resolution/singletarget/one/SubSubClassOne.java
+++ b/src/test/java/com/android/tools/r8/resolution/singletarget/one/SubSubClassOne.java
@@ -3,6 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.resolution.singletarget.one;
+import com.android.tools.r8.NoHorizontalClassMerging;
+
+@NoHorizontalClassMerging
public class SubSubClassOne extends AbstractSubClass implements IrrelevantInterfaceWithDefault {
// Avoid SubSubClassOne.class.getCanonicalName() as it may change during shrinking.
diff --git a/src/test/java/com/android/tools/r8/resolution/singletarget/one/SubSubClassTwo.java b/src/test/java/com/android/tools/r8/resolution/singletarget/one/SubSubClassTwo.java
index bd4c45c..1534f36 100644
--- a/src/test/java/com/android/tools/r8/resolution/singletarget/one/SubSubClassTwo.java
+++ b/src/test/java/com/android/tools/r8/resolution/singletarget/one/SubSubClassTwo.java
@@ -3,6 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.resolution.singletarget.one;
+import com.android.tools.r8.NoHorizontalClassMerging;
+
+@NoHorizontalClassMerging
public class SubSubClassTwo extends AbstractSubClass {
@Override
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedDifferentPackageLookupTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedDifferentPackageLookupTest.java
new file mode 100644
index 0000000..37b4dc8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedDifferentPackageLookupTest.java
@@ -0,0 +1,136 @@
+// 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.resolution.virtualtargets;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.LookupResult;
+import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ProtectedDifferentPackageLookupTest extends TestBase {
+
+ private static final String NEW_DESCRIPTOR_FOR_B = "Lanotherpackage/B;";
+ private static final String[] EXPECTED = new String[] {"A::foo", "A::foo"};
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public ProtectedDifferentPackageLookupTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testResolution() throws Exception {
+ assumeTrue(parameters.useRuntimeAsNoneRuntime());
+ AndroidApp.Builder builder = AndroidApp.builder();
+ builder.addProgramFiles(ToolHelper.getClassFileForTestClass(A.class));
+ builder.addClassProgramData(
+ ImmutableList.of(getBInAnotherPackage(), getMainWithCallToRelocatedB()));
+ AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(builder.build(), Main.class);
+ AppInfoWithLiveness appInfo = appView.appInfo();
+ DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
+ ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ DexProgramClass context =
+ appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+ // TODO(b/173363527): Should be an error.
+ assertTrue(lookupResult.isLookupResultSuccess());
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ TestRunResult<?> runResult =
+ testForRuntime(parameters)
+ .addProgramClasses(A.class)
+ .addProgramClassFileData(getBInAnotherPackage(), getMainWithCallToRelocatedB())
+ .run(parameters.getRuntime(), Main.class);
+ if (parameters.isDexRuntime()) {
+ // TODO(b/173363527): Figure out if this should be an error on DEX
+ runResult.assertSuccessWithOutputLines(EXPECTED);
+ } else {
+ runResult.assertFailureWithErrorThatThrows(VerifyError.class);
+ }
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(A.class)
+ .addProgramClassFileData(getBInAnotherPackage(), getMainWithCallToRelocatedB())
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ // TODO(b/173363527): Should be an error on CF.
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ private byte[] getBInAnotherPackage() throws Exception {
+ return transformer(B.class).setClassDescriptor(NEW_DESCRIPTOR_FOR_B).transform();
+ }
+
+ private byte[] getMainWithCallToRelocatedB() throws Exception {
+ return transformer(Main.class)
+ .replaceClassDescriptorInMethodInstructions(
+ DescriptorUtils.javaTypeToDescriptor(B.class.getTypeName()), NEW_DESCRIPTOR_FOR_B)
+ .transform();
+ }
+
+ public static class A {
+
+ protected void foo() {
+ System.out.println("A::foo");
+ }
+ }
+
+ // Will be moved to another package
+ public static class B extends A {
+
+ // Invoke-super is also legal but if we add a call to this we will not inline and we
+ // maintain the error.
+ // public void invokeSuper() {
+ // super.foo();
+ // }
+
+ public void invokeVirtual() {
+ foo(); // invoke-virtual
+ }
+
+ public void invokeOnMember(A a) {
+ // This one is illegal
+ a.foo();
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ B b = new B();
+ b.invokeVirtual();
+ b.invokeOnMember(new B());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedSamePackageLookupTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedSamePackageLookupTest.java
new file mode 100644
index 0000000..ea86d8a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedSamePackageLookupTest.java
@@ -0,0 +1,97 @@
+// 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.resolution.virtualtargets;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.LookupResult;
+import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ProtectedSamePackageLookupTest extends TestBase {
+
+ private static final String NEW_DESCRIPTOR_FOR_C = "Lanotherpackage/C;";
+ private static final String EXPECTED = "A::foo";
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public ProtectedSamePackageLookupTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testResolution() throws Exception {
+ assumeTrue(parameters.useRuntimeAsNoneRuntime());
+ AppView<AppInfoWithLiveness> appView =
+ computeAppViewWithLiveness(
+ buildClasses(A.class, C.class, Main.class).build(), PackagePrivateChainTest.Main.class);
+ AppInfoWithLiveness appInfo = appView.appInfo();
+ DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
+ ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ DexProgramClass context =
+ appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
+ LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
+ assertTrue(lookupResult.isLookupResultSuccess());
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ TestRunResult<?> runResult =
+ testForRuntime(parameters)
+ .addProgramClasses(A.class, C.class, Main.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(A.class, C.class, Main.class)
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ public static class A {
+
+ protected void foo() {
+ System.out.println("A::foo");
+ }
+ }
+
+ // Will be moved to another package
+ public static class C extends A {
+
+ public void runB(A b) {
+ b.foo();
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new C().runB(new A());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/EnumSideEffect.java b/src/test/java/com/android/tools/r8/rewrite/enums/EnumSideEffect.java
new file mode 100644
index 0000000..a0e59b3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/enums/EnumSideEffect.java
@@ -0,0 +1,102 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.rewrite.enums;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class EnumSideEffect extends TestBase {
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public EnumSideEffect(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testD8AndJava() throws Exception {
+ testForRuntime(parameters.getRuntime(), parameters.getApiLevel())
+ .addInnerClasses(EnumSideEffect.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("clinit", "init", "init", "a", "b", "a", "b");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .addInnerClasses(EnumSideEffect.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("clinit", "a", "b", "init", "init", "a", "b");
+ // TODO(b/173580704): should be the following result as in D8:
+ // .assertSuccessWithOutputLines("clinit", "init", "init", "a", "b", "a", "b");
+ }
+
+ @NeverClassInline
+ enum MyEnum1 {
+ A,
+ B;
+
+ static {
+ System.out.println("clinit");
+ }
+ }
+
+ @NeverClassInline
+ enum MyEnum2 {
+ A,
+ B;
+
+ MyEnum2() {
+ System.out.println("init");
+ }
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ switch1(MyEnum1.A);
+ switch1(MyEnum1.B);
+ switch2(MyEnum2.A);
+ switch2(MyEnum2.B);
+ }
+
+ @NeverInline
+ private static void switch1(MyEnum1 e) {
+ switch (e) {
+ case A:
+ System.out.println("a");
+ break;
+ case B:
+ System.out.println("b");
+ break;
+ }
+ }
+
+ @NeverInline
+ private static void switch2(MyEnum2 e) {
+ switch (e) {
+ case A:
+ System.out.println("a");
+ break;
+ case B:
+ System.out.println("b");
+ break;
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java
index d354df7..cbded10 100644
--- a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java
@@ -75,7 +75,7 @@
@Test(expected = CompilationFailedException.class)
public void unsupportedCommandCommandLine() throws Throwable {
DiagnosticsChecker.checkErrorsContains(
- "Missing command, specify one of 'check', '--print-usage' or '--keep-rules'",
+ "Missing command, specify one of 'check' or '--keep-rules'",
handler -> {
TraceReferences.run(
TraceReferencesCommand.parse(new String[] {"--xxx"}, Origin.unknown(), handler)
@@ -152,7 +152,7 @@
@Test(expected = CompilationFailedException.class)
public void multipleFormatsCommandLine() throws Throwable {
DiagnosticsChecker.checkErrorsContains(
- "Using '--output' requires command '--print-usage' or '--keep-rules'",
+ "Using '--output' requires command '--keep-rules'",
handler -> {
TraceReferences.run(
TraceReferencesCommand.parse(
@@ -176,9 +176,6 @@
}
private String formatName(OutputFormat format) {
- if (format == OutputFormat.PRINTUSAGE) {
- return "--print-usage";
- }
if (format == OutputFormat.KEEP_RULES) {
return "--keep-rules";
}
@@ -187,13 +184,7 @@
}
enum OutputFormat {
- /** Format used with the -printusage flag */
- PRINTUSAGE,
- /** Keep rules keeping each of the traced references */
KEEP_RULES,
- /**
- * Keep rules with <code>allowobfuscation</code> modifier keeping each of the traced references
- */
KEEP_RULES_WITH_ALLOWOBFUSCATION
}
@@ -229,12 +220,10 @@
DiagnosticsChecker diagnosticsChecker = new DiagnosticsChecker();
StringValueStringConsumer stringConsumer = new StringValueStringConsumer();
TraceReferencesConsumer consumer =
- format == OutputFormat.PRINTUSAGE
- ? new TraceReferencesPrintUsage()
- : TraceReferencesKeepRules.builder()
- .setAllowObfuscation(format == OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION)
- .setOutputConsumer(stringConsumer)
- .build();
+ TraceReferencesKeepRules.builder()
+ .setAllowObfuscation(format == OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION)
+ .setOutputConsumer(stringConsumer)
+ .build();
try {
TraceReferences.run(
TraceReferencesCommand.builder(diagnosticsChecker)
@@ -243,11 +232,7 @@
.addSourceFiles(sourceJar)
.setConsumer(consumer)
.build());
- if (consumer instanceof TraceReferencesPrintUsage) {
- assertEquals(expected, ((TraceReferencesPrintUsage) consumer).get());
- } else {
- assertEquals(expected, stringConsumer.get());
- }
+ assertEquals(expected, stringConsumer.get());
if (diagnosticsCheckerConsumer != null) {
diagnosticsCheckerConsumer.accept(diagnosticsChecker);
} else {
@@ -315,21 +300,6 @@
}
@Test
- public void test_printUses() throws Throwable {
- runAndCheckOutput(
- ImmutableList.of(Target.class),
- ImmutableList.of(Source.class),
- OutputFormat.PRINTUSAGE,
- StringUtils.lines(
- ImmutableList.of(
- "com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target",
- "com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target: void"
- + " method(int)",
- "com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target: int"
- + " field")));
- }
-
- @Test
public void test_keepRules() throws Throwable {
runAndCheckOutput(
ImmutableList.of(Target.class),
@@ -409,11 +379,12 @@
String expected =
StringUtils.lines(
ImmutableList.of(
- "com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target",
- "com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target: void"
- + " method(int)",
- "com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target: int"
- + " field"));
+ "-keep class"
+ + " com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target {",
+ " public static void method(int);",
+ " int field;",
+ "}",
+ "-keeppackagenames com.android.tools.r8.tracereferences"));
Path dir = temp.newFolder().toPath();
Path targetJar = zipWithTestClasses(dir.resolve("target.jar"), ImmutableList.of(Target.class));
Path sourceJar = zipWithTestClasses(dir.resolve("source.jar"), ImmutableList.of(Source.class));
@@ -430,7 +401,7 @@
targetJar.toString(),
"--source",
sourceJar.toString(),
- "--print-usage",
+ "--keep-rules",
},
Origin.unknown())
.build());
@@ -440,16 +411,20 @@
}
}
+ @Test
public void classFileInput() throws Throwable {
String expected =
StringUtils.lines(
ImmutableList.of(
- "com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target",
- "com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target: void"
- + " method(int)",
- "com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target: int"
- + " field"));
- TraceReferencesPrintUsage consumer = new TraceReferencesPrintUsage();
+ "-keep class"
+ + " com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target {",
+ " public static void method(int);",
+ " int field;",
+ "}",
+ "-keeppackagenames com.android.tools.r8.tracereferences"));
+ Path output = temp.newFile().toPath();
+ TraceReferencesKeepRules consumer =
+ TraceReferencesKeepRules.builder().setOutputPath(output).build();
TraceReferences.run(
TraceReferencesCommand.builder()
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
@@ -457,16 +432,21 @@
.addSourceFiles(ToolHelper.getClassFileForTestClass(Source.class))
.setConsumer(consumer)
.build());
- assertEquals(expected, consumer.get());
+ assertEquals(expected, FileUtils.readTextFile(output, Charsets.UTF_8));
- Path output = temp.newFile().toPath();
+ output = temp.newFile().toPath();
TraceReferences.run(
TraceReferencesCommand.parse(
new String[] {
- "--lib", ToolHelper.getAndroidJar(AndroidApiLevel.P).toString(),
- "--target", ToolHelper.getClassFileForTestClass(Target.class).toString(),
- "--source", ToolHelper.getClassFileForTestClass(Source.class).toString(),
- "--output", output.toString(),
+ "--lib",
+ ToolHelper.getAndroidJar(AndroidApiLevel.P).toString(),
+ "--target",
+ ToolHelper.getClassFileForTestClass(Target.class).toString(),
+ "--source",
+ ToolHelper.getClassFileForTestClass(Source.class).toString(),
+ "--output",
+ output.toString(),
+ "--keep-rules"
},
Origin.unknown())
.build());
@@ -491,24 +471,7 @@
}
@Test
- public void testNoReferences_printUses() throws Throwable {
- try {
- runAndCheckOutput(
- ImmutableList.of(OtherTarget.class),
- ImmutableList.of(Source.class),
- OutputFormat.PRINTUSAGE,
- StringUtils.lines(),
- this::checkTargetMissing);
- fail("Expected compilation to fail");
- } catch (CompilationFailedException e) {
- // Expected.
- }
- }
-
- @Test
public void testMissingReference_keepRules() throws Throwable {
- Field field = Target.class.getField("field");
- Method method = Target.class.getMethod("method", int.class);
try {
runAndCheckOutput(
ImmutableList.of(OtherTarget.class),
@@ -584,30 +547,6 @@
}
@Test
- public void testMissingDefinition_printUses() throws Throwable {
- Path dir = temp.newFolder().toPath();
- Path targetJar =
- ZipBuilder.builder(dir.resolve("target.jar"))
- .addBytes(
- DescriptorUtils.getPathFromJavaType(Target.class), getClassWithTargetRemoved())
- .build();
- Path sourceJar = zipWithTestClasses(dir.resolve("source.jar"), ImmutableList.of(Source.class));
- try {
- runAndCheckOutput(
- targetJar,
- sourceJar,
- OutputFormat.PRINTUSAGE,
- StringUtils.lines(
- ImmutableList.of(
- "com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target")),
- this::checkTargetPartlyMissing);
- fail("Expected compilation to fail");
- } catch (CompilationFailedException e) {
- // Expected.
- }
- }
-
- @Test
public void testMissingDefinition_keepRules() throws Throwable {
Path dir = temp.newFolder().toPath();
Path targetJar =
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest.java
index 551c8a1..cad4538 100644
--- a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest.java
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.tracereferences;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import com.android.tools.r8.CompilationFailedException;
@@ -12,13 +14,16 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import java.nio.file.Path;
+import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -60,25 +65,55 @@
.build();
String prefix = " Lcom/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest$";
+ List<String> snippets =
+ ImmutableList.of(
+ "Tracereferences found 3 classe(s), 2 field(s) and 4 method(s) without definition",
+ StringUtils.lines(
+ "Classe(s) without definition:",
+ prefix + "Target1;",
+ prefix + "Target2;",
+ prefix + "Target3;"),
+ StringUtils.lines(
+ "Field(s) without definition:",
+ prefix + "Target;missingField1:I",
+ prefix + "Target;missingField2:I"),
+ StringUtils.lines(
+ "Method(s) without definition:",
+ prefix + "Target1;<init>()V",
+ prefix + "Target2;<init>()V",
+ prefix + "Target3;<init>()V",
+ prefix + "Target;missingMethod()V"));
try {
- DiagnosticsChecker.checkErrorsContains(
- ImmutableList.of(
- "Tracereferences found 3 classe(s), 2 field(s) and 4 method(s) without definition",
- StringUtils.lines(
- "Classe(s) without definition:",
- prefix + "Target1;",
- prefix + "Target2;",
- prefix + "Target3;"),
- StringUtils.lines(
- "Field(s) without definition:",
- prefix + "Target;missingField1:I",
- prefix + "Target;missingField2:I"),
- StringUtils.lines(
- "Method(s) without definition:",
- prefix + "Target1;<init>()V",
- prefix + "Target2;<init>()V",
- prefix + "Target3;<init>()V",
- prefix + "Target;missingMethod()V")),
+ DiagnosticsChecker.checkErrorDiagnostics(
+ checker -> {
+ DiagnosticsChecker.checkContains(snippets, checker.errors);
+ try {
+ assertEquals(1, checker.errors.size());
+ assertTrue(checker.errors.get(0) instanceof MissingDefinitionsDiagnostic);
+ MissingDefinitionsDiagnostic diagnostic =
+ (MissingDefinitionsDiagnostic) checker.errors.get(0);
+ assertEquals(
+ diagnostic.getMissingClasses(),
+ ImmutableSet.of(
+ Reference.classFromClass(Target1.class),
+ Reference.classFromClass(Target2.class),
+ Reference.classFromClass(Target3.class)));
+ assertEquals(
+ diagnostic.getMissingFields(),
+ ImmutableSet.of(
+ Reference.fieldFromField(Target.class.getField("missingField1")),
+ Reference.fieldFromField(Target.class.getField("missingField2"))));
+ assertEquals(
+ diagnostic.getMissingMethods(),
+ ImmutableSet.of(
+ Reference.methodFromMethod(Target1.class.getDeclaredConstructor()),
+ Reference.methodFromMethod(Target2.class.getDeclaredConstructor()),
+ Reference.methodFromMethod(Target3.class.getDeclaredConstructor()),
+ Reference.methodFromMethod(Target.class.getMethod("missingMethod"))));
+ } catch (ReflectiveOperationException e) {
+ fail("Unexpected exception");
+ }
+ },
handler ->
TraceReferences.run(
TraceReferencesCommand.builder(handler)
@@ -125,16 +160,37 @@
.build();
String prefix = " Lcom/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest$";
+ List<String> snippets =
+ ImmutableList.of(
+ "Tracereferences found 2 field(s) and 1 method(s) without definition",
+ StringUtils.lines(
+ "Field(s) without definition:",
+ prefix + "Target;missingField1:I",
+ prefix + "Target;missingField2:I"),
+ StringUtils.lines("Method(s) without definition:", prefix + "Target;missingMethod()V"));
try {
- DiagnosticsChecker.checkErrorsContains(
- ImmutableList.of(
- "Tracereferences found 2 field(s) and 1 method(s) without definition",
- StringUtils.lines(
- "Field(s) without definition:",
- prefix + "Target;missingField1:I",
- prefix + "Target;missingField2:I"),
- StringUtils.lines(
- "Method(s) without definition:", prefix + "Target;missingMethod()V")),
+ DiagnosticsChecker.checkErrorDiagnostics(
+ checker -> {
+ DiagnosticsChecker.checkContains(snippets, checker.errors);
+ try {
+ assertEquals(1, checker.errors.size());
+ assertTrue(checker.errors.get(0) instanceof MissingDefinitionsDiagnostic);
+ MissingDefinitionsDiagnostic diagnostic =
+ (MissingDefinitionsDiagnostic) checker.errors.get(0);
+ assertEquals(diagnostic.getMissingClasses(), ImmutableSet.of());
+ assertEquals(
+ diagnostic.getMissingFields(),
+ ImmutableSet.of(
+ Reference.fieldFromField(Target.class.getField("missingField1")),
+ Reference.fieldFromField(Target.class.getField("missingField2"))));
+ assertEquals(
+ diagnostic.getMissingMethods(),
+ ImmutableSet.of(
+ Reference.methodFromMethod(Target.class.getMethod("missingMethod"))));
+ } catch (ReflectiveOperationException e) {
+ fail("Unexpected exception");
+ }
+ },
handler ->
TraceReferences.run(
TraceReferencesCommand.builder(handler)
@@ -175,12 +231,29 @@
.build();
String prefix = " Lcom/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest$";
+ List<String> snippets =
+ ImmutableList.of(
+ "Tracereferences found 1 method(s) without definition",
+ StringUtils.lines("Method(s) without definition:", prefix + "Target;missingMethod()V"));
try {
- DiagnosticsChecker.checkErrorsContains(
- ImmutableList.of(
- "Tracereferences found 1 method(s) without definition",
- StringUtils.lines(
- "Method(s) without definition:", prefix + "Target;missingMethod()V")),
+ DiagnosticsChecker.checkErrorDiagnostics(
+ checker -> {
+ DiagnosticsChecker.checkContains(snippets, checker.errors);
+ try {
+ assertEquals(1, checker.errors.size());
+ assertTrue(checker.errors.get(0) instanceof MissingDefinitionsDiagnostic);
+ MissingDefinitionsDiagnostic diagnostic =
+ (MissingDefinitionsDiagnostic) checker.errors.get(0);
+ assertEquals(diagnostic.getMissingClasses(), ImmutableSet.of());
+ assertEquals(diagnostic.getMissingFields(), ImmutableSet.of());
+ assertEquals(
+ diagnostic.getMissingMethods(),
+ ImmutableSet.of(
+ Reference.methodFromMethod(Target.class.getMethod("missingMethod"))));
+ } catch (ReflectiveOperationException e) {
+ fail("Unexpected exception");
+ }
+ },
handler ->
TraceReferences.run(
TraceReferencesCommand.builder(handler)
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index 764c1bb..7b61e06 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -292,6 +292,24 @@
});
}
+ public ClassFileTransformer setSuper(String superDescriptor) {
+ assert DescriptorUtils.isClassDescriptor(superDescriptor);
+ String newSuperName = getBinaryNameFromDescriptor(superDescriptor);
+ return addClassTransformer(
+ new ClassTransformer() {
+ @Override
+ public void visit(
+ int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] interfaces) {
+ super.visit(version, access, name, signature, newSuperName, interfaces);
+ }
+ });
+ }
+
public ClassFileTransformer setAccessFlags(Consumer<ClassAccessFlags> fn) {
return addClassTransformer(
new ClassTransformer() {
diff --git a/tools/git_sync_cl_chain.py b/tools/git_sync_cl_chain.py
index 66713fa..f3a0728 100755
--- a/tools/git_sync_cl_chain.py
+++ b/tools/git_sync_cl_chain.py
@@ -199,15 +199,18 @@
name = line[:name_end_index]
line = line[name_end_index:].lstrip()
- if ('[') not in line or ':' not in line:
- return Repo(name, is_current, None)
+ if '[' in line:
+ line = line[line.index('[')+1:]
- upstream_start_index = line.index('[')
- line = line[upstream_start_index+1:]
- upstream_end_index = line.index(':')
- upstream = line[:upstream_end_index]
+ if ':' in line:
+ upstream = line[:line.index(':')]
+ return Repo(name, is_current, upstream)
- return Repo(name, is_current, upstream)
+ if ']' in line:
+ upstream = line[:line.index(']')]
+ return Repo(name, is_current, upstream)
+
+ return Repo(name, is_current, None)
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))
diff --git a/tools/run_on_app_dump.py b/tools/run_on_app_dump.py
index 03687af..0ceb3b0b 100755
--- a/tools/run_on_app_dump.py
+++ b/tools/run_on_app_dump.py
@@ -319,6 +319,8 @@
'url': 'https://github.com/chrisbanes/tivi',
'revision': '8e2ddd8fe2d343264a66aa1ef8acbd4cc587e8ce',
'folder': 'tivi',
+ # TODO(b/173974110): Enable recompilation.
+ 'skip_recompilation': True,
}),
App({
'id': 'com.keylesspalace.tusky',
@@ -348,6 +350,8 @@
'url': 'https://github.com/android/compose-samples',
'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
'folder': 'android/compose-samples/crane',
+ # TODO(b/173176042): Fix recompilation
+ 'skip_recompilation': True,
}),
# TODO(b/173167253): Check if monkey testing works.
App({
@@ -398,6 +402,8 @@
'url': 'https://github.com/android/compose-samples',
'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
'folder': 'android/compose-samples/jetsnack',
+ # TODO(b/173176042): Fix recompilation
+ 'skip_recompilation': True,
}),
# TODO(b/173167253): Check if monkey testing works.
App({
@@ -409,6 +415,8 @@
'url': 'https://github.com/android/compose-samples',
'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
'folder': 'android/compose-samples/jetsurvey',
+ # TODO(b/173176042): Fix recompilation
+ 'skip_recompilation': True,
}),
# TODO(b/173167253): Check if monkey testing works.
App({
@@ -420,6 +428,8 @@
'url': 'https://github.com/android/compose-samples',
'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
'folder': 'android/compose-samples/owl',
+ # TODO(b/173176042): Fix recompilation
+ 'skip_recompilation': True,
}),
# TODO(b/173167253): Check if monkey testing works.
App({
@@ -431,6 +441,8 @@
'url': 'https://github.com/android/compose-samples',
'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
'folder': 'android/compose-samples/rally',
+ # TODO(b/173176042): Fix recompilation
+ 'skip_recompilation': True,
}),
]
@@ -790,6 +802,8 @@
print(' {}-#{}:'.format(shrinker, compilation_index))
dex_size = result.get('dex_size')
msg = ' dex size: {}'.format(dex_size)
+ if options.print_runtimeraw:
+ print(' run time raw: {} ms'.format(result.get('duration')))
if dex_size != proguard_dex_size and proguard_dex_size >= 0:
msg = '{} ({}, {})'.format(
msg, dex_size - proguard_dex_size,
@@ -887,6 +901,11 @@
help='Print the sizes of individual dex segments as ' +
'\'<BENCHMARKNAME>-<APP>-<segment>(CodeSize): '
'<bytes>\'')
+ result.add_option('--print-runtimeraw',
+ metavar='BENCHMARKNAME',
+ help='Print the line \'<BENCHMARKNAME>(RunTimeRaw):' +
+ ' <elapsed> ms\' at the end where <elapsed> is' +
+ ' the elapsed time in milliseconds.')
result.add_option('--quiet',
help='Disable verbose logging',
default=False,