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,