Merge commit '8b95700662d1f3c4d6df79d560ddcb436f550e9e' into dev-release
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index 4fc504a..2b6eb5f 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -149,6 +149,15 @@
}
}
builders {
+ name: "linux_horizontal"
+ mixins: "linux"
+ mixins: "normal"
+ recipe {
+ properties: "tool:r8"
+ properties: "horizontal_class_merging:True"
+ }
+ }
+ builders {
name: "linux_release"
mixins: "normal"
mixins: "linux"
diff --git a/infra/config/global/luci-milo.cfg b/infra/config/global/luci-milo.cfg
index faf51f3..90e3efc 100644
--- a/infra/config/global/luci-milo.cfg
+++ b/infra/config/global/luci-milo.cfg
@@ -16,6 +16,11 @@
short_name: "linux"
}
builders {
+ name: "buildbucket/luci.r8.ci/linux_horizontal"
+ category: "R8"
+ short_name: "horizontal"
+ }
+ 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-notify.cfg b/infra/config/global/luci-notify.cfg
index 5e37d53..e417700 100644
--- a/infra/config/global/luci-notify.cfg
+++ b/infra/config/global/luci-notify.cfg
@@ -29,6 +29,11 @@
repository: "https://r8.googlesource.com/r8"
}
builders {
+ name: "linux_horizontal"
+ bucket: "ci"
+ repository: "https://r8.googlesource.com/r8"
+ }
+ builders {
name: "linux_release"
bucket: "ci"
repository: "https://r8.googlesource.com/r8"
diff --git a/infra/config/global/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
index eb725d9..3c31451 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -27,6 +27,7 @@
}
triggers: "archive"
triggers: "linux"
+ triggers: "linux_horizontal"
triggers: "linux-android-4.0.4"
triggers: "linux-android-4.4.4"
triggers: "linux-android-5.1.1"
@@ -136,6 +137,19 @@
}
job {
+ id: "linux_horizontal"
+ acl_sets: "default"
+ triggering_policy: {
+ max_concurrent_invocations: 3
+ }
+ buildbucket {
+ server: "cr-buildbucket.appspot.com"
+ bucket: "luci.r8.ci"
+ builder: "linux_horizontal"
+ }
+}
+
+job {
id: "linux-android-4.0.4"
acl_sets: "default"
triggering_policy: {
diff --git a/src/main/java/com/android/tools/r8/PrintUses.java b/src/main/java/com/android/tools/r8/PrintUses.java
index a1d8b26..c0efa2c 100644
--- a/src/main/java/com/android/tools/r8/PrintUses.java
+++ b/src/main/java/com/android/tools/r8/PrintUses.java
@@ -34,6 +34,8 @@
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
@@ -299,7 +301,7 @@
}
public static void main(String... args) throws Exception {
- if (args.length != 3 && args.length != 4) {
+ if (args.length != 3 && args.length != 4 && args.length != 5) {
System.out.println(USAGE.replace("\n", System.lineSeparator()));
return;
}
@@ -324,11 +326,15 @@
builder.addLibraryFile(rtJar);
Path r8Jar = Paths.get(args[argumentIndex++]);
builder.addLibraryFile(r8Jar);
- Path sampleJar = Paths.get(args[argumentIndex]);
+ Path sampleJar = Paths.get(args[argumentIndex++]);
builder.addProgramFile(sampleJar);
+ PrintStream output = System.out;
+ if (argumentIndex < args.length) {
+ output = new PrintStream(Files.newOutputStream(Paths.get(args[argumentIndex])));
+ }
Set<String> descriptors = new HashSet<>(getDescriptors(r8Jar));
descriptors.removeAll(getDescriptors(sampleJar));
- Printer printer = printKeep ? new KeepPrinter() : new DefaultPrinter();
+ Printer printer = printKeep ? new KeepPrinter(output) : new DefaultPrinter(output);
PrintUses printUses = new PrintUses(descriptors, builder.build(), printer, allowObfuscation);
printUses.analyze();
printUses.print();
@@ -378,12 +384,18 @@
private abstract static class Printer {
+ PrintStream output;
+
+ Printer(PrintStream output) {
+ this.output = output;
+ }
+
void append(String string) {
- System.out.print(string);
+ output.print(string);
}
void appendLine(String string) {
- System.out.println(string);
+ output.println(string);
}
void printArguments(DexMethod method) {
@@ -472,6 +484,10 @@
private static class DefaultPrinter extends Printer {
+ DefaultPrinter(PrintStream output) {
+ super(output);
+ }
+
@Override
public void printConstructorName(DexEncodedMethod encodedMethod) {
if (encodedMethod.accessFlags.isStatic()) {
@@ -517,6 +533,10 @@
private static class KeepPrinter extends Printer {
+ KeepPrinter(PrintStream output) {
+ super(output);
+ }
+
@Override
public void printTypeHeader(DexClass dexClass, boolean allowObfuscation) {
append(allowObfuscation ? "-keep,allowobfuscation" : "-keep");
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 9963a47..c98bafd 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -74,6 +74,8 @@
import com.android.tools.r8.optimize.BridgeHoisting;
import com.android.tools.r8.optimize.ClassAndMemberPublicizer;
import com.android.tools.r8.optimize.MemberRebindingAnalysis;
+import com.android.tools.r8.optimize.MemberRebindingIdentityLens;
+import com.android.tools.r8.optimize.MemberRebindingIdentityLensFactory;
import com.android.tools.r8.optimize.VisibilityBridgeRemover;
import com.android.tools.r8.origin.CommandLineOrigin;
import com.android.tools.r8.repackaging.Repackaging;
@@ -532,21 +534,6 @@
}
timing.end();
}
- if (options.enableHorizontalClassMerging) {
- timing.begin("HorizontalClassMerger");
- HorizontalClassMerger merger =
- new HorizontalClassMerger(
- appViewWithLiveness, mainDexTracingResult, classMergingEnqueuerExtension);
- HorizontalClassMergerGraphLens lens = merger.run();
- if (lens != null) {
- appView.setHorizontallyMergedClasses(lens.getHorizontallyMergedClasses());
- appView.rewriteWithLens(lens);
- }
- timing.end();
- }
-
- // Only required for class merging, clear instance to save memory.
- classMergingEnqueuerExtension = null;
if (options.enableArgumentRemoval) {
SubtypingInfo subtypingInfo = appViewWithLiveness.appInfo().computeSubtypingInfo();
@@ -575,6 +562,23 @@
timing.end();
}
}
+ if (options.enableHorizontalClassMerging) {
+ timing.begin("HorizontalClassMerger");
+ HorizontalClassMerger merger =
+ new HorizontalClassMerger(
+ appViewWithLiveness, mainDexTracingResult, classMergingEnqueuerExtension);
+ DirectMappedDexApplication.Builder appBuilder =
+ appView.appInfo().app().asDirect().builder();
+ HorizontalClassMergerGraphLens lens = merger.run(appBuilder);
+ if (lens != null) {
+ appView.setHorizontallyMergedClasses(lens.getHorizontallyMergedClasses());
+ appView.rewriteWithLensAndApplication(lens, appBuilder.build());
+ }
+ timing.end();
+ }
+
+ // Only required for class merging, clear instance to save memory.
+ classMergingEnqueuerExtension = null;
}
// None of the optimizations above should lead to the creation of type lattice elements.
@@ -793,6 +797,22 @@
}
}
+ // Remove unneeded visibility bridges that have been inserted for member rebinding.
+ // This can only be done if we have AppInfoWithLiveness.
+ if (appView.appInfo().hasLiveness()) {
+ new VisibilityBridgeRemover(appView.withLiveness()).run();
+ } else {
+ // If we don't have AppInfoWithLiveness here, it must be because we are not shrinking. When
+ // we are not shrinking, we can't move visibility bridges. In principle, though, it would be
+ // possible to remove visibility bridges that have been synthesized by R8, but we currently
+ // do not have this information.
+ assert !options.isShrinking();
+ }
+
+ MemberRebindingIdentityLens memberRebindingLens =
+ MemberRebindingIdentityLensFactory.create(appView, executorService);
+ appView.setGraphLens(memberRebindingLens);
+
// Perform repackaging.
// TODO(b/165783399): Consider making repacking available without minification.
if (options.isMinifying() && options.testing.enableExperimentalRepackaging) {
@@ -803,7 +823,13 @@
RepackagingLens lens =
new Repackaging(appView.withLiveness()).run(appBuilder, executorService, timing);
if (lens != null) {
- appView.rewriteWithLensAndApplication(lens, appBuilder.build());
+ // Specify to use the member rebinding lens as the parent lens during the rewriting. This
+ // is needed to ensure that the rebound references are available during lens lookups.
+ // TODO(b/168282032): This call-site should not have to think about the parent lens that
+ // is used for the rewriting. Once the new member rebinding lens replaces the old member
+ // rebinding analysis it should be possible to clean this up.
+ appView.rewriteWithLensAndApplication(
+ lens, appBuilder.build(), memberRebindingLens.getPrevious());
}
}
@@ -866,18 +892,6 @@
return;
}
- // Remove unneeded visibility bridges that have been inserted for member rebinding.
- // This can only be done if we have AppInfoWithLiveness.
- if (appView.appInfo().hasLiveness()) {
- new VisibilityBridgeRemover(appView.withLiveness()).run();
- } else {
- // If we don't have AppInfoWithLiveness here, it must be because we are not shrinking. When
- // we are not shrinking, we can't move visibility bridges. In principle, though, it would be
- // possible to remove visibility bridges that have been synthesized by R8, but we currently
- // do not have this information.
- assert !options.isShrinking();
- }
-
// Validity checks.
assert getDirectApp(appView).verifyCodeObjectsOwners();
assert appView.appInfo().classes().stream().allMatch(clazz -> clazz.isValid(options));
diff --git a/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java b/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
index 5fb9822..b4b0d5a 100644
--- a/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
@@ -202,6 +202,10 @@
return r8Type("DexItemFactory", "graph");
}
+ private String arrayDequeType() {
+ return type("ArrayDeque", ImmutableList.of("java", "util"));
+ }
+
private String arraysType() {
return type("Arrays", ImmutableList.of("java", "util"));
}
@@ -464,7 +468,7 @@
+ "[] { "
+ values
+ " })",
- arraysType() + ".asList(" + stack + ")");
+ "new " + arrayDequeType() + "<>(" + arraysType() + ".asList(" + stack + "))");
}
private String frameTypeType(FrameType frameType) {
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 f128f76..a8386df 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
@@ -4,9 +4,12 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.cf.code.CfFrame.FrameType;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -42,6 +45,16 @@
this.type = type;
}
+ @Override
+ public int getCompareToId() {
+ return getAsmOpcode();
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ }
+
public Opcode getOpcode() {
return opcode;
}
@@ -181,4 +194,17 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forBinop();
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ..., value1, value2 →
+ // ..., result
+ FrameType frameType = FrameType.fromNumericType(type, factory);
+ frameBuilder.popAndDiscard(frameType, frameType).push(frameType);
+ }
}
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 5757b8c..734246c 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
@@ -4,8 +4,10 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -35,6 +37,16 @@
}
@Override
+ public int getCompareToId() {
+ return Opcodes.ARRAYLENGTH;
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ }
+
+ @Override
public void print(CfPrinter printer) {
printer.print(this);
}
@@ -57,4 +69,16 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forArrayLength();
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ..., arrayref →
+ // ..., length
+ frameBuilder.popAndDiscard(factory.objectArrayType).push(factory.intType);
+ }
}
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 7993e8e..3f93558 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
@@ -4,9 +4,12 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.cf.code.CfFrame.FrameType;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -32,6 +35,16 @@
this.type = type;
}
+ @Override
+ public int getCompareToId() {
+ return getLoadType();
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ }
+
public MemberType getType() {
return type;
}
@@ -102,4 +115,17 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forArrayGet();
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ..., arrayref, index →
+ // ..., value
+ frameBuilder.popAndDiscard(factory.objectArrayType, factory.intType);
+ frameBuilder.push(FrameType.fromMemberType(type, factory));
+ }
}
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 1c16397..bab53f5 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
@@ -4,9 +4,12 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.cf.code.CfFrame.FrameType;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -34,6 +37,16 @@
return type;
}
+ @Override
+ public int getCompareToId() {
+ return getStoreType();
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ }
+
private int getStoreType() {
switch (type) {
case OBJECT:
@@ -92,4 +105,18 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forArrayPut();
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ..., arrayref, index, value →
+ // ...
+ frameBuilder
+ .popAndDiscard(FrameType.fromMemberType(type, factory))
+ .popAndDiscard(factory.objectArrayType, factory.intType);
+ }
}
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 e263d02..f4bfd7a 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
@@ -4,6 +4,7 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
@@ -36,6 +37,16 @@
}
@Override
+ public int getCompareToId() {
+ return Opcodes.CHECKCAST;
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return type.slowCompareTo(((CfCheckCast) other).type);
+ }
+
+ @Override
public void write(
ProgramMethod context,
DexItemFactory dexItemFactory,
@@ -76,4 +87,16 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forCheckCast(type, context);
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ..., objectref →
+ // ..., objectref
+ frameBuilder.popAndDiscard(factory.objectType).push(type);
+ }
}
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 1eeb6b4..98b4cef 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
@@ -4,9 +4,12 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.cf.code.CfFrame.FrameType;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -39,6 +42,16 @@
this.type = type;
}
+ @Override
+ public int getCompareToId() {
+ return getAsmOpcode();
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ }
+
public Bias getBias() {
return bias;
}
@@ -106,4 +119,17 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forBinop();
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ..., value1, value2 →
+ // ..., result
+ FrameType frameType = FrameType.fromNumericType(type, factory);
+ frameBuilder.popAndDiscard(frameType, frameType).push(factory.intType);
+ }
}
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 2bb2444..adf0ba2 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
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
@@ -31,6 +32,16 @@
this.type = type;
}
+ @Override
+ public int getCompareToId() {
+ return CfCompareHelper.CONST_CLASS_COMPARE_ID;
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return type.slowCompareTo(((CfConstClass) other).type);
+ }
+
public DexType getType() {
return type;
}
@@ -99,4 +110,16 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forConstClass(type, context);
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ... →
+ // ..., value
+ frameBuilder.push(factory.classType);
+ }
}
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 d058eca..ff490b5 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
@@ -4,10 +4,12 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -24,7 +26,7 @@
public class CfConstMethodHandle extends CfInstruction {
- private DexMethodHandle handle;
+ private final DexMethodHandle handle;
public CfConstMethodHandle(DexMethodHandle handle) {
this.handle = handle;
@@ -35,6 +37,16 @@
}
@Override
+ public int getCompareToId() {
+ return CfCompareHelper.CONST_METHOD_HANDLE_COMPARE_ID;
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return handle.slowCompareTo(((CfConstMethodHandle) other).handle);
+ }
+
+ @Override
public void write(
ProgramMethod context,
DexItemFactory dexItemFactory,
@@ -76,4 +88,16 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forConstMethodHandle();
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ... →
+ // ..., value
+ frameBuilder.push(factory.methodHandleType);
+ }
}
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 e59a64e..5b9e4aa 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
@@ -4,10 +4,12 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
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.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -35,6 +37,16 @@
}
@Override
+ public int getCompareToId() {
+ return CfCompareHelper.CONST_METHOD_TYPE_COMPARE_ID;
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return type.slowCompareTo(((CfConstMethodType) other).type);
+ }
+
+ @Override
public void write(
ProgramMethod context,
DexItemFactory dexItemFactory,
@@ -74,4 +86,16 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forConstMethodType();
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ... →
+ // ..., value
+ frameBuilder.push(factory.methodTypeType);
+ }
}
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 e5740e7..9a42170 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
@@ -4,8 +4,10 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -35,6 +37,16 @@
}
@Override
+ public int getCompareToId() {
+ return Opcodes.ACONST_NULL;
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ }
+
+ @Override
public void print(CfPrinter printer) {
printer.print(this);
}
@@ -49,4 +61,16 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forConstInstruction();
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ... →
+ // ..., value
+ frameBuilder.push(DexItemFactory.nullValueType);
+ }
}
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 642eeb9..a919748 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
@@ -5,8 +5,10 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -18,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 java.util.Comparator;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -31,6 +34,18 @@
this.type = type;
}
+ @Override
+ public int getCompareToId() {
+ return CfCompareHelper.CONST_NUMBER_COMPARE_ID;
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return Comparator.comparing(CfConstNumber::getRawValue)
+ .thenComparing(CfConstNumber::getType)
+ .compare(this, (CfConstNumber) other);
+ }
+
public ValueType getType() {
return type;
}
@@ -147,4 +162,16 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forConstInstruction();
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ... →
+ // ..., value
+ frameBuilder.push(type.toPrimitiveType().toDexType(factory));
+ }
}
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 9037875..8ff71b9 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
@@ -4,9 +4,11 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.CfCompareHelper;
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.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -27,6 +29,16 @@
this.string = string;
}
+ @Override
+ public int getCompareToId() {
+ return CfCompareHelper.CONST_STRING_COMPARE_ID;
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return string.slowCompareTo(other.asConstString().string);
+ }
+
public DexString getString() {
return string;
}
@@ -79,4 +91,16 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forConstInstruction();
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ... →
+ // ..., value
+ frameBuilder.push(factory.stringType);
+ }
}
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 bdc470f..64c45fa 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
@@ -5,10 +5,12 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -33,6 +35,16 @@
this.nameComputationInfo = nameComputationInfo;
}
+ @Override
+ public int getCompareToId() {
+ return CfCompareHelper.CONST_STRING_DEX_ITEM_COMPARE_ID;
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return item.referenceCompareTo(((CfDexItemBasedConstString) other).item);
+ }
+
public DexReference getItem() {
return item;
}
@@ -96,4 +108,16 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forDexItemBasedConstString(item, context);
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ... →
+ // ..., value
+ frameBuilder.push(factory.stringType);
+ }
}
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 3cff4ee..ba90681 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
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
@@ -22,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 java.util.Comparator;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -47,6 +49,18 @@
}
@Override
+ public int getCompareToId() {
+ return opcode;
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return Comparator.comparing(CfFieldInstruction::getField, DexField::slowCompareTo)
+ .thenComparing(field -> field.declaringField, DexField::slowCompareTo)
+ .compare(this, (CfFieldInstruction) other);
+ }
+
+ @Override
public CfFieldInstruction asFieldInstruction() {
return this;
}
@@ -152,4 +166,37 @@
throw new Unreachable("Unexpected opcode " + opcode);
}
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ switch (opcode) {
+ case Opcodes.GETFIELD:
+ // ..., objectref →
+ // ..., value
+ frameBuilder.popAndDiscard(field.holder).push(field.type);
+ return;
+ case Opcodes.GETSTATIC:
+ // ..., →
+ // ..., value
+ frameBuilder.push(field.type);
+ return;
+ case Opcodes.PUTFIELD:
+ // ..., objectref, value →
+ // ...,
+ frameBuilder.popAndDiscard(field.holder, field.type);
+ return;
+ case Opcodes.PUTSTATIC:
+ // ..., value →
+ // ...
+ frameBuilder.pop(field.type);
+ return;
+ default:
+ throw new Unreachable("Unexpected opcode " + opcode);
+ }
+ }
}
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 f392338..1770409 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
@@ -7,12 +7,16 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.CfCodeStackMapValidatingException;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
import com.android.tools.r8.ir.conversion.IRBuilder;
@@ -20,8 +24,10 @@
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 it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
-import java.util.List;
+import java.util.ArrayDeque;
+import java.util.Deque;
import java.util.Objects;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -34,8 +40,8 @@
return new InitializedType(type);
}
- public static FrameType uninitializedNew(CfLabel label) {
- return new UninitializedNew(label);
+ public static FrameType uninitializedNew(CfLabel label, DexType typeToInitialize) {
+ return new UninitializedNew(label, typeToInitialize);
}
public static FrameType uninitializedThis() {
@@ -46,6 +52,14 @@
return Top.SINGLETON;
}
+ public static FrameType oneWord() {
+ return OneWord.SINGLETON;
+ }
+
+ public static FrameType twoWord() {
+ return TwoWord.SINGLETON;
+ }
+
abstract Object getTypeOpcode(GraphLens graphLens, NamingLens namingLens);
public boolean isWide() {
@@ -72,11 +86,54 @@
return null;
}
+ public DexType getUninitializedNewType() {
+ return null;
+ }
+
public boolean isTop() {
return false;
}
+ public boolean isOneWord() {
+ return false;
+ }
+
+ public boolean isTwoWord() {
+ return false;
+ }
+
private FrameType() {}
+
+ public static FrameType fromMemberType(MemberType memberType, DexItemFactory factory) {
+ switch (memberType) {
+ case OBJECT:
+ return FrameType.initialized(factory.objectType);
+ case BOOLEAN_OR_BYTE:
+ return FrameType.initialized(factory.intType);
+ case CHAR:
+ return FrameType.initialized(factory.intType);
+ case SHORT:
+ return FrameType.initialized(factory.intType);
+ case INT:
+ return FrameType.initialized(factory.intType);
+ case FLOAT:
+ return FrameType.initialized(factory.floatType);
+ case LONG:
+ return FrameType.initialized(factory.longType);
+ case DOUBLE:
+ return FrameType.initialized(factory.doubleType);
+ case INT_OR_FLOAT:
+ return FrameType.oneWord();
+ case LONG_OR_DOUBLE:
+ return FrameType.twoWord();
+ default:
+ throw new Unreachable("Unexpected MemberType: " + memberType);
+ }
+ }
+
+ public static FrameType fromNumericType(NumericType numericType, DexItemFactory factory) {
+ return FrameType.initialized(numericType.dexTypeFor(factory));
+ }
}
@Override
@@ -89,6 +146,18 @@
return this;
}
+ @Override
+ public int getCompareToId() {
+ return CfCompareHelper.FRAME_COMPARE_ID;
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, 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);
+ }
+
private static class InitializedType extends FrameType {
private final DexType type;
@@ -100,7 +169,7 @@
@Override
public String toString() {
- return type.toString();
+ return "Initialized(" + type.toString() + ")";
}
@Override
@@ -163,9 +232,11 @@
private static class UninitializedNew extends FrameType {
private final CfLabel label;
+ private final DexType type;
- private UninitializedNew(CfLabel label) {
+ private UninitializedNew(CfLabel label, DexType type) {
this.label = label;
+ this.type = type;
}
@Override
@@ -187,9 +258,15 @@
public CfLabel getUninitializedLabel() {
return label;
}
+
+ @Override
+ public DexType getUninitializedNewType() {
+ return type;
+ }
}
private static class UninitializedThis extends FrameType {
+
private UninitializedThis() {}
@Override
@@ -208,10 +285,55 @@
}
}
- private final Int2ReferenceSortedMap<FrameType> locals;
- private final List<FrameType> stack;
+ private static class OneWord extends FrameType {
- public CfFrame(Int2ReferenceSortedMap<FrameType> locals, List<FrameType> stack) {
+ private static final OneWord SINGLETON = new OneWord();
+
+ @Override
+ Object getTypeOpcode(GraphLens graphLens, NamingLens namingLens) {
+ throw new Unreachable("Should only be used for verification");
+ }
+
+ @Override
+ public boolean isOneWord() {
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "oneword";
+ }
+ }
+
+ private static class TwoWord extends FrameType {
+
+ private static final TwoWord SINGLETON = new TwoWord();
+
+ @Override
+ Object getTypeOpcode(GraphLens graphLens, NamingLens namingLens) {
+ throw new Unreachable("Should only be used for verification");
+ }
+
+ @Override
+ public boolean isWide() {
+ return true;
+ }
+
+ @Override
+ public boolean isTwoWord() {
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "twoword";
+ }
+ }
+
+ private final Int2ReferenceSortedMap<FrameType> locals;
+ private final Deque<FrameType> stack;
+
+ public CfFrame(Int2ReferenceSortedMap<FrameType> locals, Deque<FrameType> stack) {
assert locals.values().stream().allMatch(Objects::nonNull);
assert stack.stream().allMatch(Objects::nonNull);
this.locals = locals;
@@ -222,7 +344,7 @@
return locals;
}
- public List<FrameType> getStack() {
+ public Deque<FrameType> getStack() {
return stack;
}
@@ -252,8 +374,9 @@
return null;
}
Object[] stackTypes = new Object[stackCount];
- for (int i = 0; i < stackCount; i++) {
- stackTypes[i] = stack.get(i).getTypeOpcode(graphLens, namingLens);
+ int index = 0;
+ for (FrameType frameType : stack) {
+ stackTypes[index++] = frameType.getTypeOpcode(graphLens, namingLens);
}
return stackTypes;
}
@@ -305,7 +428,6 @@
@Override
public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) {
- // TODO(mathiasr): Verify stack map frames before building IR.
code.setStateFromFrame(this);
}
@@ -319,4 +441,46 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return ConstraintWithTarget.ALWAYS;
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ frameBuilder.verifyFrameAndSet(this);
+ }
+
+ public CfFrame markInstantiated(FrameType uninitializedType, DexType initType) {
+ if (uninitializedType.isInitialized()) {
+ throw CfCodeStackMapValidatingException.error(
+ "Cannot instantiate already instantiated type " + uninitializedType);
+ }
+ Int2ReferenceSortedMap<FrameType> newLocals = new Int2ReferenceAVLTreeMap<>();
+ for (int var : locals.keySet()) {
+ newLocals.put(var, getInitializedFrameType(uninitializedType, locals.get(var), initType));
+ }
+ Deque<FrameType> newStack = new ArrayDeque<>();
+ for (FrameType frameType : stack) {
+ newStack.addLast(getInitializedFrameType(uninitializedType, frameType, initType));
+ }
+ return new CfFrame(newLocals, newStack);
+ }
+
+ private FrameType getInitializedFrameType(FrameType unInit, FrameType other, DexType newType) {
+ assert !unInit.isInitialized();
+ if (other.isInitialized()) {
+ return other;
+ }
+ if (unInit.isUninitializedThis() && other.isUninitializedThis()) {
+ return FrameType.initialized(newType);
+ }
+ if (unInit.isUninitializedNew()
+ && other.isUninitializedNew()
+ && unInit.getUninitializedLabel() == other.getUninitializedLabel()) {
+ return FrameType.initialized(newType);
+ }
+ return other;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java
new file mode 100644
index 0000000..259ad12
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java
@@ -0,0 +1,379 @@
+// 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.cf.code;
+
+import com.android.tools.r8.cf.code.CfFrame.FrameType;
+import com.android.tools.r8.graph.CfCodeStackMapValidatingException;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.MapUtils;
+import com.android.tools.r8.utils.collections.ImmutableDeque;
+import com.android.tools.r8.utils.collections.ImmutableInt2ReferenceSortedMap;
+import com.google.common.collect.Sets;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BiPredicate;
+
+public class CfFrameVerificationHelper {
+
+ private final CfFrame NO_FRAME =
+ new CfFrame(
+ ImmutableInt2ReferenceSortedMap.<FrameType>builder().build(), ImmutableDeque.of());
+
+ private final Deque<FrameType> throwStack;
+
+ private CfFrame currentFrame = NO_FRAME;
+ private final DexType context;
+ private final Map<CfLabel, CfFrame> stateMap;
+ private final BiPredicate<DexType, DexType> isJavaAssignable;
+ private final DexItemFactory factory;
+ private final List<CfTryCatch> tryCatchRanges;
+
+ private final Deque<CfTryCatch> currentCatchRanges = new ArrayDeque<>();
+ private final Set<CfLabel> tryCatchRangeLabels;
+
+ public CfFrameVerificationHelper(
+ DexType context,
+ Map<CfLabel, CfFrame> stateMap,
+ List<CfTryCatch> tryCatchRanges,
+ BiPredicate<DexType, DexType> isJavaAssignable,
+ DexItemFactory factory) {
+ this.context = context;
+ this.stateMap = stateMap;
+ this.tryCatchRanges = tryCatchRanges;
+ this.isJavaAssignable = isJavaAssignable;
+ this.factory = factory;
+ throwStack = ImmutableDeque.of(FrameType.initialized(factory.throwableType));
+ // Compute all labels that marks a start or end to catch ranges.
+ tryCatchRangeLabels = Sets.newIdentityHashSet();
+ for (CfTryCatch tryCatchRange : tryCatchRanges) {
+ tryCatchRangeLabels.add(tryCatchRange.start);
+ tryCatchRangeLabels.add(tryCatchRange.end);
+ }
+ }
+
+ public DexItemFactory factory() {
+ return factory;
+ }
+
+ public FrameType readLocal(int index, DexType expectedType) {
+ verifyFrameIsSet();
+ FrameType frameType = currentFrame.getLocals().get(index);
+ if (frameType == null) {
+ throw CfCodeStackMapValidatingException.error("No local at index " + index);
+ }
+ verifyIsAssignable(frameType, expectedType);
+ return frameType;
+ }
+
+ public void storeLocal(int index, FrameType frameType) {
+ verifyFrameIsSet();
+ currentFrame.getLocals().put(index, frameType);
+ }
+
+ public FrameType pop() {
+ verifyFrameIsSet();
+ if (currentFrame.getStack().isEmpty()) {
+ throw CfCodeStackMapValidatingException.error("Cannot pop() from an empty stack");
+ }
+ return currentFrame.getStack().removeLast();
+ }
+
+ public FrameType pop(DexType expectedType) {
+ FrameType frameType = pop();
+ verifyIsAssignable(frameType, expectedType);
+ return frameType;
+ }
+
+ public CfFrameVerificationHelper popAndDiscard(DexType... expectedTypes) {
+ verifyFrameIsSet();
+ for (int i = expectedTypes.length - 1; i >= 0; i--) {
+ pop(expectedTypes[i]);
+ }
+ return this;
+ }
+
+ public FrameType pop(FrameType expectedType) {
+ FrameType frameType = pop();
+ verifyIsAssignable(frameType, expectedType);
+ return frameType;
+ }
+
+ public CfFrameVerificationHelper popAndDiscard(FrameType... expectedTypes) {
+ verifyFrameIsSet();
+ for (int i = expectedTypes.length - 1; i >= 0; i--) {
+ pop(expectedTypes[i]);
+ }
+ return this;
+ }
+
+ public void popAndInitialize(DexType context, DexType methodHolder) {
+ verifyFrameIsSet();
+ FrameType objectRef = pop(factory.objectType);
+ CfFrame newFrame =
+ currentFrame.markInstantiated(
+ objectRef, objectRef.isUninitializedNew() ? methodHolder : context);
+ setNoFrame();
+ verifyFrameAndSet(newFrame);
+ }
+
+ public CfFrameVerificationHelper push(FrameType type) {
+ verifyFrameIsSet();
+ currentFrame.getStack().addLast(type);
+ return this;
+ }
+
+ public CfFrameVerificationHelper push(DexType type) {
+ return push(FrameType.initialized(type));
+ }
+
+ public CfFrameVerificationHelper seenLabel(CfLabel label) {
+ if (tryCatchRangeLabels.contains(label)) {
+ for (CfTryCatch tryCatchRange : tryCatchRanges) {
+ if (tryCatchRange.start == label) {
+ currentCatchRanges.add(tryCatchRange);
+ // We can have fall-through into this range requiring the current frame being
+ // assignable to the handler frame. This is handled for locals when we see a throwing
+ // instruction, but we can validate here that the stack will be a single element stack
+ // [throwable].
+ CfFrame destinationFrame = stateMap.get(tryCatchRange.start);
+ if (destinationFrame == null) {
+ throw CfCodeStackMapValidatingException.error("No frame for target catch range target");
+ }
+ verifyStackIsAssignable(
+ destinationFrame.getStack(), throwStack, factory, isJavaAssignable);
+ }
+ }
+ currentCatchRanges.removeIf(currentRange -> currentRange.end == label);
+ }
+ return this;
+ }
+
+ private void verifyFrameIsSet() {
+ if (currentFrame == NO_FRAME) {
+ throw CfCodeStackMapValidatingException.error("Unexpected state change");
+ }
+ }
+
+ public void verifyFrameAndSet(CfFrame newFrame) {
+ if (currentFrame != NO_FRAME) {
+ verifyFrame(newFrame);
+ }
+ setFrame(newFrame);
+ }
+
+ private void setFrame(CfFrame frame) {
+ assert frame != NO_FRAME;
+ currentFrame =
+ new CfFrame(
+ new Int2ReferenceAVLTreeMap<>(frame.getLocals()), new ArrayDeque<>(frame.getStack()));
+ }
+
+ public void verifyExceptionEdges() {
+ for (CfTryCatch currentCatchRange : currentCatchRanges) {
+ for (CfLabel target : currentCatchRange.targets) {
+ CfFrame destinationFrame = stateMap.get(target);
+ if (destinationFrame == null) {
+ throw CfCodeStackMapValidatingException.error("No frame for target catch range target");
+ }
+ // We have to check all current handler targets have assignable locals and a 1-element
+ // stack assignable to throwable. It is not required that the the thrown error is
+ // handled.
+ verifyLocalsIsAssignable(
+ currentFrame.getLocals(), destinationFrame.getLocals(), factory, isJavaAssignable);
+ }
+ }
+ }
+
+ public CfFrame getFrame() {
+ return currentFrame;
+ }
+
+ public void verifyTarget(CfLabel label) {
+ verifyFrame(stateMap.get(label));
+ }
+
+ public void verifyFrame(CfFrame destinationFrame) {
+ if (destinationFrame == null) {
+ throw CfCodeStackMapValidatingException.error("No destination frame");
+ }
+ verifyFrame(destinationFrame.getLocals(), destinationFrame.getStack());
+ }
+
+ public void verifyFrame(Int2ReferenceSortedMap<FrameType> locals, Deque<FrameType> stack) {
+ verifyIsAssignable(
+ currentFrame.getLocals(),
+ currentFrame.getStack(),
+ locals,
+ stack,
+ factory,
+ isJavaAssignable);
+ }
+
+ public void setNoFrame() {
+ currentFrame = NO_FRAME;
+ }
+
+ public void clearStack() {
+ verifyFrameIsSet();
+ currentFrame.getStack().clear();
+ }
+
+ public void verifyIsAssignable(FrameType source, DexType target) {
+ if (!source.isInitialized()) {
+ if (source.isUninitializedThis() && target == context) {
+ return;
+ }
+ if (target == factory.objectType) {
+ return;
+ }
+ throw CfCodeStackMapValidatingException.error(
+ "The expected type " + source + " is not assignable to " + target.toSourceString());
+ }
+ if (!isJavaAssignable.test(source.getInitializedType(), target)) {
+ throw CfCodeStackMapValidatingException.error(
+ "The expected type " + source + " is not assignable to " + target.toSourceString());
+ }
+ }
+
+ public void verifyIsAssignable(FrameType source, FrameType target) {
+ if (!canBeAssigned(source, target, factory, isJavaAssignable)) {
+ throw CfCodeStackMapValidatingException.error(
+ "The expected type " + source + " is not assignable to " + target);
+ }
+ }
+
+ // Based on https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.10.1.4.
+ public static void verifyIsAssignable(
+ Int2ReferenceSortedMap<FrameType> sourceLocals,
+ Deque<FrameType> sourceStack,
+ Int2ReferenceSortedMap<FrameType> destLocals,
+ Deque<FrameType> destStack,
+ DexItemFactory factory,
+ BiPredicate<DexType, DexType> isJavaAssignable) {
+ verifyLocalsIsAssignable(sourceLocals, destLocals, factory, isJavaAssignable);
+ verifyStackIsAssignable(sourceStack, destStack, factory, isJavaAssignable);
+ }
+
+ private static void verifyLocalsIsAssignable(
+ Int2ReferenceSortedMap<FrameType> sourceLocals,
+ Int2ReferenceSortedMap<FrameType> destLocals,
+ DexItemFactory factory,
+ BiPredicate<DexType, DexType> isJavaAssignable) {
+ final int localsLastKey = sourceLocals.isEmpty() ? -1 : sourceLocals.lastIntKey();
+ final int otherLocalsLastKey = destLocals.isEmpty() ? -1 : destLocals.lastIntKey();
+ if (localsLastKey < otherLocalsLastKey) {
+ throw CfCodeStackMapValidatingException.error(
+ "Source locals "
+ + MapUtils.toString(sourceLocals)
+ + " have different local indices than "
+ + MapUtils.toString(destLocals));
+ }
+ for (int i = 0; i < otherLocalsLastKey; i++) {
+ final FrameType sourceType =
+ sourceLocals.containsKey(i) ? sourceLocals.get(i) : FrameType.top();
+ final FrameType destinationType =
+ destLocals.containsKey(i) ? destLocals.get(i) : FrameType.top();
+ if (!canBeAssigned(sourceType, destinationType, factory, isJavaAssignable)) {
+ throw CfCodeStackMapValidatingException.error(
+ "Could not assign '"
+ + MapUtils.toString(sourceLocals)
+ + "' to '"
+ + MapUtils.toString(destLocals)
+ + "'. The local at index "
+ + i
+ + " with '"
+ + sourceType
+ + "' not being assignable to '"
+ + destinationType
+ + "'");
+ }
+ }
+ }
+
+ private static void verifyStackIsAssignable(
+ Deque<FrameType> sourceStack,
+ Deque<FrameType> destStack,
+ DexItemFactory factory,
+ BiPredicate<DexType, DexType> isJavaAssignable) {
+ if (sourceStack.size() != destStack.size()) {
+ throw CfCodeStackMapValidatingException.error(
+ "Source stack "
+ + Arrays.toString(sourceStack.toArray())
+ + " and destination stack "
+ + Arrays.toString(destStack.toArray())
+ + " is not the same size");
+ }
+ final Iterator<FrameType> otherIterator = destStack.iterator();
+ int i = 0;
+ for (FrameType sourceType : sourceStack) {
+ final FrameType destinationType = otherIterator.next();
+ if (!canBeAssigned(sourceType, destinationType, factory, isJavaAssignable)) {
+ throw CfCodeStackMapValidatingException.error(
+ "Could not assign '"
+ + Arrays.toString(sourceStack.toArray())
+ + "' to '"
+ + Arrays.toString(destStack.toArray())
+ + "'. The stack value at index "
+ + i
+ + " (from bottom) with '"
+ + sourceType
+ + "' not being assignable to '"
+ + destinationType
+ + "'");
+ }
+ i++;
+ }
+ }
+
+ // Based on https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.10.1.2.
+ public static boolean canBeAssigned(
+ FrameType source,
+ FrameType target,
+ DexItemFactory factory,
+ BiPredicate<DexType, DexType> isJavaAssignable) {
+ if (target.isTop()) {
+ return true;
+ }
+ if (source.isTop()) {
+ return false;
+ }
+ if (source.isWide() != target.isWide()) {
+ return false;
+ }
+ if (target.isOneWord() || target.isTwoWord()) {
+ return true;
+ }
+ if (source.isUninitializedThis() && target.isUninitializedThis()) {
+ return true;
+ }
+ if (source.isUninitializedNew() && target.isUninitializedNew()) {
+ // TODO(b/168190134): Allow for picking the offset from the target if not set.
+ DexType uninitializedNewTypeSource = source.getUninitializedNewType();
+ DexType uninitializedNewTypeTarget = target.getUninitializedNewType();
+ return uninitializedNewTypeSource == null
+ || uninitializedNewTypeTarget == null
+ || uninitializedNewTypeSource == uninitializedNewTypeTarget;
+ }
+ // TODO(b/168190267): Clean-up the lattice.
+ if (!source.isInitialized()
+ && target.isInitialized()
+ && target.getInitializedType() == factory.objectType) {
+ return true;
+ }
+ if (source.isInitialized() && target.isInitialized()) {
+ // Both are instantiated types and we resort to primitive tyoe/java type hierarchy checking.
+ return isJavaAssignable.test(source.getInitializedType(), target.getInitializedType());
+ }
+ return false;
+ }
+}
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 0cd79d4..afc44f5 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
@@ -4,8 +4,10 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -28,6 +30,16 @@
}
@Override
+ public int getCompareToId() {
+ return Opcodes.GOTO;
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return helper.compareLabels(target, ((CfGoto) other).target);
+ }
+
+ @Override
public CfGoto asGoto() {
return this;
}
@@ -74,4 +86,15 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forJumpInstruction();
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ frameBuilder.verifyTarget(target);
+ frameBuilder.setNoFrame();
+ }
}
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 31497fd..fba8f74 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
@@ -5,8 +5,10 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -35,6 +37,19 @@
this.target = target;
}
+ @Override
+ public int getCompareToId() {
+ return getOpcode();
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ CfIf otherIf = (CfIf) other;
+ assert kind == otherIf.kind;
+ assert type == otherIf.type;
+ return helper.compareLabels(target, otherIf.target);
+ }
+
public ValueType getType() {
return type;
}
@@ -107,4 +122,18 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forJumpInstruction();
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ..., value →
+ // ...
+ frameBuilder.pop(
+ type.isObject() ? factory.objectType : type.toPrimitiveType().toDexType(factory));
+ frameBuilder.verifyTarget(target);
+ }
}
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 47cbcad..d4119ba 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
@@ -5,8 +5,10 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -35,6 +37,19 @@
this.target = target;
}
+ @Override
+ public int getCompareToId() {
+ return getOpcode();
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ CfIfCmp otherIf = (CfIfCmp) other;
+ assert kind == otherIf.kind;
+ assert type == otherIf.type;
+ return helper.compareLabels(target, otherIf.target);
+ }
+
public Type getKind() {
return kind;
}
@@ -108,4 +123,19 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forJumpInstruction();
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ..., value1, value2 →
+ // ...
+ DexType type =
+ this.type.isObject() ? factory.objectType : this.type.toPrimitiveType().toDexType(factory);
+ frameBuilder.popAndDiscard(type, type);
+ frameBuilder.verifyTarget(target);
+ }
}
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 4a37601..ab56f05 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
@@ -4,8 +4,10 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -17,7 +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 java.util.Comparator;
import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
public class CfIinc extends CfInstruction {
@@ -30,6 +34,18 @@
}
@Override
+ public int getCompareToId() {
+ return Opcodes.IINC;
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return Comparator.comparingInt(CfIinc::getLocalIndex)
+ .thenComparing(CfIinc::getIncrement)
+ .compare(this, (CfIinc) other);
+ }
+
+ @Override
public void write(
ProgramMethod context,
DexItemFactory dexItemFactory,
@@ -65,4 +81,14 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return ConstraintWithTarget.ALWAYS;
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ frameBuilder.readLocal(var, factory.intType);
+ }
}
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 b12d10e..70d5b53 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
@@ -4,6 +4,7 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
@@ -41,6 +42,16 @@
}
@Override
+ public int getCompareToId() {
+ return CfCompareHelper.CONST_CLASS_COMPARE_ID;
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return clazz.slowCompareTo(((CfInitClass) other).clazz);
+ }
+
+ @Override
public void write(
ProgramMethod context,
DexItemFactory dexItemFactory,
@@ -85,4 +96,16 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forInitClass(clazz, context);
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ..., →
+ // ..., value
+ frameBuilder.push(clazz);
+ }
}
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 c34d576..d7f5ef5 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
@@ -4,6 +4,7 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
@@ -35,6 +36,16 @@
}
@Override
+ public int getCompareToId() {
+ return Opcodes.INSTANCEOF;
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return type.slowCompareTo(other.asInstanceOf().type);
+ }
+
+ @Override
public boolean isInstanceOf() {
return true;
}
@@ -84,4 +95,16 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forInstanceOf(type, context);
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ..., objectref →
+ // ..., result
+ frameBuilder.popAndDiscard(factory.objectType).push(factory.intType);
+ }
}
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 da09f7b..39857bf 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
@@ -4,10 +4,12 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.ClasspathMethod;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -34,6 +36,34 @@
public abstract void print(CfPrinter printer);
+ /**
+ * Base compare id for each instruction.
+ *
+ * <p>The id is required to be unique for each instruction class and define a order on
+ * instructions up to the instructions data payload which is ordered by {@code internalCompareTo}.
+ * Currently we represent the ID using the ASM opcode of the instruction or in case the
+ * instruction is not represented externally, some non-overlapping ID defined in {@code
+ * CfCompareHelper}.
+ */
+ public abstract int getCompareToId();
+
+ /**
+ * Compare two instructions with the same compare id.
+ *
+ * <p>The internal compare may assume to only be called on instructions that have the same
+ * "compare id". Overrides of this method can assume 'other' to be of the same type (as this is a
+ * requirement for the defintion of the "compare id").
+ *
+ * <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 final int compareTo(CfInstruction o, CfCompareHelper helper) {
+ int diff = getCompareToId() - o.getCompareToId();
+ return diff != 0 ? diff : internalCompareTo(o, helper);
+ }
+
@Override
public String toString() {
CfPrinter printer = new CfPrinter();
@@ -145,6 +175,14 @@
return false;
}
+ public CfThrow asThrow() {
+ return null;
+ }
+
+ public boolean isThrow() {
+ return false;
+ }
+
public CfDexItemBasedConstString asDexItemBasedConstString() {
return null;
}
@@ -158,6 +196,10 @@
return false;
}
+ public boolean isReturnVoid() {
+ return false;
+ }
+
/** Return true if this instruction is CfIf or CfIfCmp. */
public boolean isConditionalJump() {
return false;
@@ -183,4 +225,11 @@
public abstract ConstraintWithTarget inliningConstraint(
InliningConstraints inliningConstraints, DexProgramClass context);
+
+ public abstract void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens);
}
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 4f21538..4256db3 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
@@ -8,6 +8,7 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -17,7 +18,7 @@
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.GraphLens.GraphLensLookupResult;
+import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.UseRegistry;
@@ -51,6 +52,18 @@
this.itf = itf;
}
+ @Override
+ public int getCompareToId() {
+ return getOpcode();
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ CfInvoke otherInvoke = other.asInvoke();
+ int itfDiff = Boolean.compare(itf, otherInvoke.itf);
+ return itfDiff != 0 ? itfDiff : method.slowCompareTo(otherInvoke.method);
+ }
+
public DexMethod getMethod() {
return method;
}
@@ -82,9 +95,9 @@
NamingLens namingLens,
LensCodeRewriterUtils rewriter,
MethodVisitor visitor) {
- GraphLensLookupResult lookup =
+ MethodLookupResult lookup =
graphLens.lookupMethod(method, context.getReference(), getInvokeType(context));
- DexMethod rewrittenMethod = lookup.getMethod();
+ DexMethod rewrittenMethod = lookup.getReference();
String owner = namingLens.lookupInternalName(rewrittenMethod.holder);
String name = namingLens.lookupName(rewrittenMethod).toString();
String desc = rewrittenMethod.proto.toDescriptorString(namingLens);
@@ -273,8 +286,8 @@
case Opcodes.INVOKESTATIC:
{
// Static invokes may have changed as a result of horizontal class merging.
- GraphLensLookupResult lookup = graphLens.lookupMethod(target, null, Type.STATIC);
- target = lookup.getMethod();
+ MethodLookupResult lookup = graphLens.lookupMethod(target, null, Type.STATIC);
+ target = lookup.getReference();
type = lookup.getType();
}
break;
@@ -292,8 +305,8 @@
}
// Virtual invokes may have changed to interface invokes as a result of member rebinding.
- GraphLensLookupResult lookup = graphLens.lookupMethod(target, null, type);
- target = lookup.getMethod();
+ MethodLookupResult lookup = graphLens.lookupMethod(target, null, type);
+ target = lookup.getReference();
type = lookup.getType();
}
break;
@@ -305,6 +318,29 @@
return inliningConstraints.forInvoke(target, type, context);
}
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ..., objectref, [arg1, [arg2 ...]] →
+ // ... [ returnType ]
+ // OR, for static method calls:
+ // ..., [arg1, [arg2 ...]] →
+ // ...
+ frameBuilder.popAndDiscard(this.method.proto.parameters.values);
+ if (opcode == Opcodes.INVOKESPECIAL && method.isInstanceInitializer(factory)) {
+ frameBuilder.popAndInitialize(context, method.holder);
+ } else if (opcode != Opcodes.INVOKESTATIC) {
+ frameBuilder.pop(method.holder);
+ }
+ if (this.method.proto.returnType != factory.voidType) {
+ frameBuilder.push(this.method.proto.returnType);
+ }
+ }
+
private static boolean noNeedToUseGraphLens(
DexMethod method, Invoke.Type type, GraphLens graphLens) {
assert graphLens.lookupMethod(method, null, type).getType() == type;
@@ -338,9 +374,8 @@
}
private DexEncodedMethod lookupMethodOnHolder(AppView<?> appView, DexMethod method) {
- GraphLensLookupResult lookupResult =
- appView.graphLens().lookupMethod(method, method, Type.DIRECT);
- DexMethod rewrittenMethod = lookupResult.getMethod();
+ MethodLookupResult lookupResult = appView.graphLens().lookupMethod(method, method, Type.DIRECT);
+ DexMethod rewrittenMethod = lookupResult.getReference();
// Directly lookup the program type for holder. This bypasses lookup order as well as looks
// directly on the application data, which bypasses and indirection or validation.
DexProgramClass clazz = appView.appInfo().unsafeDirectProgramTypeLookup(rewrittenMethod.holder);
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 a455305..b151132 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
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
@@ -29,6 +30,7 @@
import java.util.List;
import org.objectweb.asm.Handle;
import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
public class CfInvokeDynamic extends CfInstruction {
@@ -40,6 +42,16 @@
}
@Override
+ public int getCompareToId() {
+ return Opcodes.INVOKEDYNAMIC;
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return callSite.compareTo(((CfInvokeDynamic) other).callSite);
+ }
+
+ @Override
public void write(
ProgramMethod context,
DexItemFactory dexItemFactory,
@@ -130,4 +142,19 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forInvokeCustom();
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ..., [arg1, [arg2 ...]] →
+ // ...
+ frameBuilder.popAndDiscard(callSite.methodProto.parameters.values);
+ if (callSite.methodProto.returnType != factory.voidType) {
+ frameBuilder.push(callSite.methodProto.returnType);
+ }
+ }
}
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 47ee09b..75f1a0f 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
@@ -5,8 +5,11 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.CfCodeStackMapValidatingException;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -33,6 +36,16 @@
}
@Override
+ public int getCompareToId() {
+ throw error();
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ throw error();
+ }
+
+ @Override
public void write(
ProgramMethod context,
DexItemFactory dexItemFactory,
@@ -60,6 +73,18 @@
throw error();
}
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // JSR/RET instructions cannot be verified since we have not type-checking way for addresses
+ // on the stack/locals. We have to abandon.
+ throw CfCodeStackMapValidatingException.error("Unexpected JSR/RET instruction");
+ }
+
public int getLocal() {
return local;
}
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 b27fd00..af69cbb 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
@@ -4,8 +4,10 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -31,6 +33,16 @@
}
@Override
+ public int getCompareToId() {
+ return CfCompareHelper.LABEL_COMPARE_ID;
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return helper.compareLabels(this, other.asLabel());
+ }
+
+ @Override
public CfLabel asLabel() {
return this;
}
@@ -72,4 +84,14 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return ConstraintWithTarget.ALWAYS;
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // This is a no-op.
+ }
}
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 17e0ecd..591b608 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
@@ -5,8 +5,10 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -32,6 +34,16 @@
this.type = type;
}
+ @Override
+ public int getCompareToId() {
+ return getLoadType();
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return Integer.compare(var, other.asLoad().var);
+ }
+
private int getLoadType() {
switch (type) {
case OBJECT:
@@ -101,4 +113,19 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forLoad();
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ... →
+ // ..., objectref
+ frameBuilder.push(
+ frameBuilder.readLocal(
+ getLocalIndex(),
+ type.isObject() ? factory.objectType : type.toPrimitiveType().toDexType(factory)));
+ }
}
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 19a3bfe..3f487f8 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
@@ -4,9 +4,12 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.cf.code.CfFrame.FrameType;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -44,6 +47,16 @@
this.type = type;
}
+ @Override
+ public int getCompareToId() {
+ return getAsmOpcode();
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ }
+
public NumericType getType() {
return type;
}
@@ -153,4 +166,27 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forBinop();
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ..., value1, value2 →
+ // ..., result
+ FrameType value1Type = FrameType.fromNumericType(type, factory);
+ FrameType value2Type;
+ switch (opcode) {
+ case And:
+ case Or:
+ case Xor:
+ value2Type = value1Type;
+ break;
+ default:
+ value2Type = FrameType.initialized(factory.intType);
+ }
+ frameBuilder.popAndDiscard(value1Type, value2Type).push(value1Type);
+ }
}
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 24b580b..8515ece 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
@@ -4,8 +4,11 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.cf.code.CfFrame.FrameType;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -34,6 +37,16 @@
}
@Override
+ public int getCompareToId() {
+ return getAsmOpcode();
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ }
+
+ @Override
public void write(
ProgramMethod context,
DexItemFactory dexItemFactory,
@@ -42,7 +55,11 @@
NamingLens namingLens,
LensCodeRewriterUtils rewriter,
MethodVisitor visitor) {
- visitor.visitInsn(type == Type.ENTER ? Opcodes.MONITORENTER : Opcodes.MONITOREXIT);
+ visitor.visitInsn(getAsmOpcode());
+ }
+
+ private int getAsmOpcode() {
+ return type == Type.ENTER ? Opcodes.MONITORENTER : Opcodes.MONITOREXIT;
}
@Override
@@ -66,4 +83,16 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forMonitor();
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ..., objectref →
+ // ...
+ frameBuilder.pop(FrameType.initialized(factory.objectType));
+ }
}
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 33f4244..b00a3fe 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
@@ -4,6 +4,7 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
@@ -20,7 +21,9 @@
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 org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
public class CfMultiANewArray extends CfInstruction {
@@ -41,6 +44,18 @@
}
@Override
+ public int getCompareToId() {
+ return Opcodes.MULTIANEWARRAY;
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return Comparator.comparingInt(CfMultiANewArray::getDimensions)
+ .thenComparing(CfMultiANewArray::getType, DexType::slowCompareTo)
+ .compare(this, ((CfMultiANewArray) other));
+ }
+
+ @Override
public void write(
ProgramMethod context,
DexItemFactory dexItemFactory,
@@ -81,4 +96,19 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forInvokeMultiNewArray(type, context);
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ..., count1, [count2, ...] →
+ // ..., arrayref
+ for (int i = 0; i < dimensions; i++) {
+ frameBuilder.pop(factory.intType);
+ }
+ frameBuilder.push(type);
+ }
}
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 88274b3..37c37f4 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
@@ -4,9 +4,12 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.cf.code.CfFrame.FrameType;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -35,6 +38,16 @@
}
@Override
+ public int getCompareToId() {
+ return getAsmOpcode();
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ }
+
+ @Override
public void write(
ProgramMethod context,
DexItemFactory dexItemFactory,
@@ -95,4 +108,17 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forUnop();
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ..., value →
+ // ..., result
+ FrameType frameType = FrameType.fromNumericType(type, factory);
+ frameBuilder.popAndDiscard(frameType).push(frameType);
+ }
}
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 83b7e63..b4939b6 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
@@ -4,6 +4,8 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.cf.code.CfFrame.FrameType;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
@@ -35,6 +37,16 @@
}
@Override
+ public int getCompareToId() {
+ return Opcodes.NEW;
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return type.slowCompareTo(((CfNew) other).type);
+ }
+
+ @Override
public void write(
ProgramMethod context,
DexItemFactory dexItemFactory,
@@ -72,4 +84,16 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forNewInstance(type, context);
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ... →
+ // ..., objectref
+ frameBuilder.push(FrameType.uninitializedNew(new CfLabel(), type));
+ }
}
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 3c9a746..41f2029 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
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
@@ -38,6 +39,16 @@
return type;
}
+ @Override
+ public int getCompareToId() {
+ return type.isPrimitiveArrayType() ? Opcodes.NEWARRAY : Opcodes.ANEWARRAY;
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return type.slowCompareTo(((CfNewArray) other).type);
+ }
+
private int getPrimitiveTypeCode() {
switch (type.descriptor.content[1]) {
case 'Z':
@@ -123,4 +134,17 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forNewArrayEmpty(type, context);
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ..., count →
+ // ..., arrayref
+ assert type.isArrayType();
+ frameBuilder.popAndDiscard(factory.intType).push(type);
+ }
}
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 e2b6044..5d6205a 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
@@ -4,8 +4,10 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -22,6 +24,16 @@
public class CfNop extends CfInstruction {
@Override
+ public int getCompareToId() {
+ return Opcodes.NOP;
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ }
+
+ @Override
public void write(
ProgramMethod context,
DexItemFactory dexItemFactory,
@@ -53,4 +65,14 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return ConstraintWithTarget.ALWAYS;
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // This is an actual Nop.
+ }
}
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 ce3ab3b..6c82b1d 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
@@ -4,9 +4,12 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.cf.code.CfFrame.FrameType;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -36,6 +39,16 @@
this.to = to;
}
+ @Override
+ public int getCompareToId() {
+ return getAsmOpcode();
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ }
+
public NumericType getFromType() {
return from;
}
@@ -53,7 +66,7 @@
NamingLens namingLens,
LensCodeRewriterUtils rewriter,
MethodVisitor visitor) {
- visitor.visitInsn(this.getAsmOpcode());
+ visitor.visitInsn(getAsmOpcode());
}
@Override
@@ -166,4 +179,18 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forUnop();
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ..., value →
+ // ..., result
+ frameBuilder
+ .popAndDiscard(FrameType.fromNumericType(from, factory))
+ .push(FrameType.fromNumericType(to, factory));
+ }
}
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 e30be70..fe22ae9 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
@@ -4,8 +4,10 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -30,6 +32,18 @@
}
@Override
+ public int getCompareToId() {
+ return CfCompareHelper.POSITION_COMPARE_ID;
+ }
+
+ @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);
+ }
+
+ @Override
public void write(
ProgramMethod context,
DexItemFactory dexItemFactory,
@@ -81,4 +95,14 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return ConstraintWithTarget.ALWAYS;
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // This is a no-op.
+ }
}
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 c5198d1..e1cebfd 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
@@ -5,8 +5,10 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -34,6 +36,16 @@
return type;
}
+ @Override
+ public int getCompareToId() {
+ return getOpcode();
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ }
+
private int getOpcode() {
switch (type) {
case INT:
@@ -89,4 +101,16 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forReturn();
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ assert returnType != null;
+ frameBuilder.popAndDiscard(returnType);
+ frameBuilder.setNoFrame();
+ }
}
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 8f7bb37..5a7e864 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
@@ -4,8 +4,10 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -27,6 +29,16 @@
}
@Override
+ public int getCompareToId() {
+ return Opcodes.RETURN;
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ }
+
+ @Override
public void write(
ProgramMethod context,
DexItemFactory dexItemFactory,
@@ -49,6 +61,11 @@
}
@Override
+ public boolean isReturnVoid() {
+ return true;
+ }
+
+ @Override
public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) {
builder.addReturn();
}
@@ -58,4 +75,14 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forReturn();
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ frameBuilder.setNoFrame();
+ }
}
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 a7a39b6..205808f 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
@@ -4,10 +4,13 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.cf.code.CfFrame.FrameType;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -79,6 +82,16 @@
}
@Override
+ public int getCompareToId() {
+ return opcode.opcode;
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ }
+
+ @Override
public void write(
ProgramMethod context,
DexItemFactory dexItemFactory,
@@ -329,4 +342,167 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return ConstraintWithTarget.ALWAYS;
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+
+ switch (opcode) {
+ case Pop:
+ // ..., value →
+ // ...
+ frameBuilder.pop(FrameType.oneWord());
+ return;
+ case Pop2:
+ // ..., value2, value1 →
+ // ...
+ // or, for double and long:
+ // ..., value →
+ // ...
+ final FrameType pop = frameBuilder.pop();
+ if (!pop.isWide()) {
+ frameBuilder.verifyIsAssignable(pop, FrameType.oneWord());
+ frameBuilder.pop(FrameType.oneWord());
+ }
+ return;
+ case Dup:
+ {
+ // ..., value →
+ // ..., value, value
+ FrameType topValue = frameBuilder.pop(FrameType.oneWord());
+ frameBuilder.push(topValue).push(topValue);
+ return;
+ }
+ case DupX1:
+ {
+ // ..., value2, value1 →
+ // ..., value1, value2, value1
+ FrameType value1 = frameBuilder.pop(FrameType.oneWord());
+ FrameType value2 = frameBuilder.pop(FrameType.oneWord());
+ frameBuilder.push(value1).push(value2).push(value1);
+ return;
+ }
+ case DupX2:
+ {
+ // ..., value3, value2, value1 →
+ // ..., value1, value3, value2, value1
+ // or, if value2 is double or long:
+ // ..., value2, value1 →
+ // ..., value1, value2, value1
+ FrameType value1 = frameBuilder.pop(FrameType.oneWord());
+ FrameType value2 = frameBuilder.pop();
+ if (!value2.isWide()) {
+ frameBuilder.verifyIsAssignable(value2, FrameType.oneWord());
+ FrameType value3 = frameBuilder.pop(FrameType.oneWord());
+ frameBuilder.push(value1).push(value3);
+ } else {
+ frameBuilder.push(value1);
+ }
+ frameBuilder.push(value2).push(value1);
+ return;
+ }
+ case Dup2:
+ {
+ // ..., value2, value1 →
+ // ..., value2, value1, value2, value1
+ // or, for value1 being long or double:
+ // ..., value →
+ // ..., value, value
+ FrameType value1 = frameBuilder.pop();
+ if (!value1.isWide()) {
+ frameBuilder.verifyIsAssignable(value1, FrameType.oneWord());
+ FrameType value2 = frameBuilder.pop(FrameType.oneWord());
+ frameBuilder.push(value2).push(value1).push(value2);
+ } else {
+ frameBuilder.push(value1);
+ }
+ frameBuilder.push(value1);
+ return;
+ }
+ case Dup2X1:
+ {
+ // ..., value3, value2, value1 →
+ // ..., value2, value1, value3, value2, value1
+ // or, for value1 being a long or double:
+ // ..., value2, value1 →
+ // ..., value1, value2, value1
+ FrameType value1 = frameBuilder.pop();
+ FrameType value2 = frameBuilder.pop(FrameType.oneWord());
+ if (!value1.isWide()) {
+ frameBuilder.verifyIsAssignable(value1, FrameType.oneWord());
+ FrameType value3 = frameBuilder.pop(FrameType.oneWord());
+ frameBuilder.push(value2).push(value1).push(value3);
+ } else {
+ frameBuilder.push(value1);
+ }
+ frameBuilder.push(value2);
+ frameBuilder.push(value1);
+ return;
+ }
+ case Dup2X2:
+ {
+ // (1)
+ // ..., value4, value3, value2, value1 →
+ // ..., value2, value1, value4, value3, value2, value1
+ // (2) OR, if value1 is long or double
+ // ..., value3, value2, value1 →
+ // ..., value1, value3, value2, value1
+ // (3) OR if value3 is long or double
+ // ..., value3, value2, value1 →
+ // ..., value2, value1, value3, value2, value1
+ // (4) OR, where value1 and value2 is double or long:
+ // ..., value2, value1 →
+ // ..., value1, value2, value1
+ FrameType value1 = frameBuilder.pop();
+ FrameType value2 = frameBuilder.pop();
+ if (!value1.isWide()) {
+ FrameType value3 = frameBuilder.pop();
+ if (!value3.isWide()) {
+ // (1)
+ frameBuilder.verifyIsAssignable(value1, FrameType.oneWord());
+ frameBuilder.verifyIsAssignable(value2, FrameType.oneWord());
+ frameBuilder.verifyIsAssignable(value3, FrameType.oneWord());
+ FrameType value4 = frameBuilder.pop(FrameType.oneWord());
+ frameBuilder
+ .push(value2)
+ .push(value1)
+ .push(value4)
+ .push(value3)
+ .push(value2)
+ .push(value1);
+ } else {
+ // (3)
+ frameBuilder.verifyIsAssignable(value1, FrameType.oneWord());
+ frameBuilder.verifyIsAssignable(value2, FrameType.oneWord());
+ frameBuilder.push(value2).push(value1).push(value3).push(value2).push(value1);
+ }
+ } else if (!value2.isWide()) {
+ // (2)
+ frameBuilder.verifyIsAssignable(value2, FrameType.oneWord());
+ FrameType value3 = frameBuilder.pop(FrameType.oneWord());
+ frameBuilder.push(value1).push(value3).push(value2).push(value1);
+ } else {
+ // (4)
+ assert value2.isWide();
+ frameBuilder.push(value1).push(value2).push(value1);
+ }
+ return;
+ }
+ case Swap:
+ {
+ // ..., value2, value1 →
+ // ..., value1, value2
+ FrameType value1 = frameBuilder.pop(FrameType.oneWord());
+ FrameType value2 = frameBuilder.pop(FrameType.oneWord());
+ frameBuilder.push(value1).push(value2);
+ return;
+ }
+ default:
+ throw new Unreachable("Invalid opcode for CfStackInstruction");
+ }
+ }
}
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 d46d3a3..1856c84 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
@@ -4,9 +4,12 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.cf.code.CfFrame.FrameType;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -32,6 +35,16 @@
this.type = type;
}
+ @Override
+ public int getCompareToId() {
+ return getStoreType();
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return Integer.compare(var, other.asStore().var);
+ }
+
private int getStoreType() {
switch (type) {
case OBJECT:
@@ -100,4 +113,42 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forStore();
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ..., ref →
+ // ...
+ FrameType pop = frameBuilder.pop();
+ switch (type) {
+ case OBJECT:
+ frameBuilder.verifyIsAssignable(pop, factory.objectType);
+ frameBuilder.storeLocal(var, pop);
+ return;
+ case INT:
+ frameBuilder.verifyIsAssignable(pop, factory.intType);
+ frameBuilder.storeLocal(var, FrameType.initialized(factory.intType));
+ return;
+ case FLOAT:
+ frameBuilder.verifyIsAssignable(pop, factory.floatType);
+ frameBuilder.storeLocal(var, FrameType.initialized(factory.floatType));
+ return;
+ case LONG:
+ frameBuilder.verifyIsAssignable(pop, factory.longType);
+ frameBuilder.storeLocal(var, FrameType.initialized(factory.longType));
+ frameBuilder.storeLocal(var + 1, FrameType.initialized(factory.longType));
+ return;
+ case DOUBLE:
+ frameBuilder.verifyIsAssignable(pop, factory.doubleType);
+ frameBuilder.storeLocal(var, FrameType.initialized(factory.doubleType));
+ frameBuilder.storeLocal(var + 1, FrameType.initialized(factory.doubleType));
+ return;
+ default:
+ throw new Unreachable("Unexpected type " + type);
+ }
+ }
}
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 edc5244..62effd7 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,9 +3,13 @@
// 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.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -17,11 +21,14 @@
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 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;
+import org.objectweb.asm.Opcodes;
public class CfSwitch extends CfInstruction {
@@ -41,6 +48,20 @@
assert kind != Kind.TABLE || keys.length == 1;
}
+ @Override
+ public int getCompareToId() {
+ return kind == Kind.LOOKUP ? Opcodes.LOOKUPSWITCH : Opcodes.TABLESWITCH;
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, 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);
+ }
+
public Kind getKind() {
return kind;
}
@@ -117,4 +138,20 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forJumpInstruction();
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ..., index/key →
+ // ...
+ frameBuilder.pop(factory.intType);
+ frameBuilder.verifyTarget(defaultTarget);
+ for (CfLabel target : targets) {
+ frameBuilder.verifyTarget(target);
+ }
+ }
}
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 aaa7280..b8b31b1 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
@@ -4,8 +4,10 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -28,6 +30,26 @@
}
@Override
+ public int getCompareToId() {
+ return Opcodes.ATHROW;
+ }
+
+ @Override
+ public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
+ return CfCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ }
+
+ @Override
+ public boolean isThrow() {
+ return true;
+ }
+
+ @Override
+ public CfThrow asThrow() {
+ return this;
+ }
+
+ @Override
public void write(
ProgramMethod context,
DexItemFactory dexItemFactory,
@@ -60,4 +82,18 @@
InliningConstraints inliningConstraints, DexProgramClass context) {
return inliningConstraints.forJumpInstruction();
}
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ..., objectref →
+ // objectref
+ frameBuilder.pop(factory.throwableType);
+ // The exception edges are verified in CfCode since this is a throwing instruction.
+ frameBuilder.setNoFrame();
+ }
}
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 5c1aa30..8e9752b 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
@@ -3,11 +3,14 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf.code;
+import com.android.tools.r8.graph.CfCompareHelper;
import com.android.tools.r8.graph.DexType;
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 java.util.ArrayList;
+import java.util.Comparator;
import java.util.List;
public class CfTryCatch {
@@ -43,4 +46,12 @@
}
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(DexType::slowCompareTo))
+ .thenComparing(c -> c.targets, ComparatorUtils.listComparator(helper::compareLabels))
+ .compare(this, other);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/code/CheckCast.java b/src/main/java/com/android/tools/r8/code/CheckCast.java
index 6ca2dc5..06edf90 100644
--- a/src/main/java/com/android/tools/r8/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/code/CheckCast.java
@@ -6,11 +6,13 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
import com.android.tools.r8.graph.OffsetToObjectMapping;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.conversion.IRBuilder;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
+import java.nio.ShortBuffer;
public class CheckCast extends Format21c<DexType> {
@@ -42,6 +44,11 @@
}
@Override
+ int internalCompareBBBB(Format21c<?> other) {
+ return BBBB.slowCompareTo((DexType) other.BBBB);
+ }
+
+ @Override
public void collectIndexedItems(
IndexedItemCollection indexedItems,
ProgramMethod context,
@@ -52,6 +59,18 @@
}
@Override
+ public void write(
+ ShortBuffer dest,
+ ProgramMethod context,
+ GraphLens graphLens,
+ ObjectToOffsetMapping mapping,
+ LensCodeRewriterUtils rewriter) {
+ DexType rewritten = graphLens.lookupType(getType());
+ writeFirst(AA, dest);
+ write16BitReference(rewritten, dest, mapping);
+ }
+
+ @Override
public CheckCast asCheckCast() {
return this;
}
diff --git a/src/main/java/com/android/tools/r8/code/ConstClass.java b/src/main/java/com/android/tools/r8/code/ConstClass.java
index ec8a13a..3c6d03e 100644
--- a/src/main/java/com/android/tools/r8/code/ConstClass.java
+++ b/src/main/java/com/android/tools/r8/code/ConstClass.java
@@ -6,11 +6,13 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
import com.android.tools.r8.graph.OffsetToObjectMapping;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.conversion.IRBuilder;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
+import java.nio.ShortBuffer;
public class ConstClass extends Format21c<DexType> {
@@ -27,6 +29,11 @@
}
@Override
+ int internalCompareBBBB(Format21c<?> other) {
+ return BBBB.slowCompareTo((DexType) other.BBBB);
+ }
+
+ @Override
public String getName() {
return NAME;
}
@@ -52,6 +59,18 @@
}
@Override
+ public void write(
+ ShortBuffer dest,
+ ProgramMethod context,
+ GraphLens graphLens,
+ ObjectToOffsetMapping mapping,
+ LensCodeRewriterUtils rewriter) {
+ DexType rewritten = graphLens.lookupType(getType());
+ writeFirst(AA, dest);
+ write16BitReference(rewritten, dest, mapping);
+ }
+
+ @Override
public void registerUse(UseRegistry registry) {
registry.registerConstClass(getType());
}
diff --git a/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java b/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java
index 75fc2dc..d69329a 100644
--- a/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java
@@ -51,6 +51,11 @@
}
@Override
+ int internalCompareBBBB(Format21c<?> other) {
+ return BBBB.slowCompareTo((DexMethodHandle) other.BBBB);
+ }
+
+ @Override
public String toString(ClassNameMapper naming) {
return formatString("v" + AA + ", \"" + BBBB.toString() + "\"");
}
diff --git a/src/main/java/com/android/tools/r8/code/ConstMethodType.java b/src/main/java/com/android/tools/r8/code/ConstMethodType.java
index 4767025..ba6e89f 100644
--- a/src/main/java/com/android/tools/r8/code/ConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/code/ConstMethodType.java
@@ -50,6 +50,11 @@
}
@Override
+ int internalCompareBBBB(Format21c<?> other) {
+ return BBBB.slowCompareTo((DexProto) other.BBBB);
+ }
+
+ @Override
public String toString(ClassNameMapper naming) {
return formatString("v" + AA + ", \"" + BBBB.toString() + "\"");
}
diff --git a/src/main/java/com/android/tools/r8/code/ConstString.java b/src/main/java/com/android/tools/r8/code/ConstString.java
index 8bccec9..0b44f8a 100644
--- a/src/main/java/com/android/tools/r8/code/ConstString.java
+++ b/src/main/java/com/android/tools/r8/code/ConstString.java
@@ -34,6 +34,11 @@
}
@Override
+ int internalCompareBBBB(Format21c<?> other) {
+ return BBBB.slowCompareTo((DexString) other.BBBB);
+ }
+
+ @Override
public void collectIndexedItems(
IndexedItemCollection indexedItems,
ProgramMethod context,
@@ -88,7 +93,8 @@
if (index != (index & 0xffff)) {
throw new InternalCompilerError("String-index overflow.");
}
- super.write(dest, context, graphLens, mapping, rewriter);
+ writeFirst(AA, dest);
+ write16BitReference(BBBB, dest, mapping);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/DexCompareHelper.java b/src/main/java/com/android/tools/r8/code/DexCompareHelper.java
new file mode 100644
index 0000000..0587c08
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DexCompareHelper.java
@@ -0,0 +1,29 @@
+// 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.code;
+
+public class DexCompareHelper {
+
+ // Integer constants to ensure that there is a well order for all DEX instructions including
+ // virtual instructions represented in our internal encoding.
+ static final int INIT_CLASS_COMPARE_ID;
+ static final int DEX_ITEM_CONST_STRING_COMPARE_ID;
+
+ private static int HIGHEST_DEX_OPCODE = 0xFF;
+
+ static {
+ int lastId = HIGHEST_DEX_OPCODE;
+ INIT_CLASS_COMPARE_ID = ++lastId;
+ DEX_ITEM_CONST_STRING_COMPARE_ID = ++lastId;
+ }
+
+ // Helper to signal that the concrete instruction is uniquely determined by its ID/opcode.
+ public static int compareIdUniquelyDeterminesEquality(
+ Instruction instruction1, Instruction instruction2) {
+ assert instruction1.getClass() == instruction2.getClass();
+ assert instruction1.getCompareToId() == instruction2.getCompareToId();
+ assert instruction1.toString().equals(instruction2.toString());
+ return 0;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DexInitClass.java b/src/main/java/com/android/tools/r8/code/DexInitClass.java
index 52cd9e1..2d9cda9 100644
--- a/src/main/java/com/android/tools/r8/code/DexInitClass.java
+++ b/src/main/java/com/android/tools/r8/code/DexInitClass.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.naming.ClassNameMapper;
import java.nio.ShortBuffer;
+import java.util.Comparator;
public class DexInitClass extends Base2Format {
@@ -71,6 +72,11 @@
throw new Unreachable();
}
+ @Override
+ int getCompareToId() {
+ return DexCompareHelper.INIT_CLASS_COMPARE_ID;
+ }
+
private int getOpcode(DexField field) {
FieldMemberType type = FieldMemberType.fromDexType(field.type);
switch (type) {
@@ -121,12 +127,10 @@
}
@Override
- public boolean equals(Object other) {
- if (other == null || getClass() != other.getClass()) {
- return false;
- }
- DexInitClass initClass = (DexInitClass) other;
- return dest == initClass.dest && clazz == initClass.clazz;
+ final int internalCompareTo(Instruction other) {
+ return Comparator.comparingInt((DexInitClass i) -> i.dest)
+ .thenComparing(i -> i.clazz, DexType::slowCompareTo)
+ .compare(this, (DexInitClass) other);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/DexItemBasedConstString.java b/src/main/java/com/android/tools/r8/code/DexItemBasedConstString.java
index c50bba8..18bdfd3 100644
--- a/src/main/java/com/android/tools/r8/code/DexItemBasedConstString.java
+++ b/src/main/java/com/android/tools/r8/code/DexItemBasedConstString.java
@@ -63,6 +63,16 @@
}
@Override
+ int getCompareToId() {
+ return DexCompareHelper.DEX_ITEM_CONST_STRING_COMPARE_ID;
+ }
+
+ @Override
+ int internalCompareBBBB(Format21c<?> other) {
+ return BBBB.referenceCompareTo(((DexItemBasedConstString) other).BBBB);
+ }
+
+ @Override
public DexItemBasedConstString asDexItemBasedConstString() {
return this;
}
diff --git a/src/main/java/com/android/tools/r8/code/FillArrayDataPayload.java b/src/main/java/com/android/tools/r8/code/FillArrayDataPayload.java
index 20d412f..52d3e07 100644
--- a/src/main/java/com/android/tools/r8/code/FillArrayDataPayload.java
+++ b/src/main/java/com/android/tools/r8/code/FillArrayDataPayload.java
@@ -9,9 +9,11 @@
import com.android.tools.r8.ir.conversion.IRBuilder;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.ComparatorUtils;
import com.android.tools.r8.utils.StringUtils;
import java.nio.ShortBuffer;
import java.util.Arrays;
+import java.util.Comparator;
public class FillArrayDataPayload extends Nop {
@@ -60,13 +62,11 @@
}
@Override
- public boolean equals(Object other) {
- if (!super.equals(other)) {
- return false;
- }
- FillArrayDataPayload that = (FillArrayDataPayload) other;
- return size == that.size && element_width == that.element_width
- && Arrays.equals(data, that.data);
+ final int internalCompareTo(Instruction other) {
+ return Comparator.comparingInt((FillArrayDataPayload i) -> i.element_width)
+ .thenComparingLong(i -> i.size)
+ .thenComparing(i -> i.data, ComparatorUtils::compareShortArray)
+ .compare(this, (FillArrayDataPayload) other);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/FilledNewArray.java b/src/main/java/com/android/tools/r8/code/FilledNewArray.java
index 5198876..b432eec 100644
--- a/src/main/java/com/android/tools/r8/code/FilledNewArray.java
+++ b/src/main/java/com/android/tools/r8/code/FilledNewArray.java
@@ -43,6 +43,11 @@
}
@Override
+ int internalCompareBBBB(Format35c<?> other) {
+ return BBBB.slowCompareTo((DexType) other.BBBB);
+ }
+
+ @Override
public void collectIndexedItems(
IndexedItemCollection indexedItems,
ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java b/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java
index de651f5..13dc9a5 100644
--- a/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java
+++ b/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java
@@ -43,6 +43,11 @@
}
@Override
+ int internalCompareBBBB(Format3rc<?> other) {
+ return BBBB.slowCompareTo((DexType) other.BBBB);
+ }
+
+ @Override
public void collectIndexedItems(
IndexedItemCollection indexedItems,
ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/code/Format10t.java b/src/main/java/com/android/tools/r8/code/Format10t.java
index d34ecb2..528ef92 100644
--- a/src/main/java/com/android/tools/r8/code/Format10t.java
+++ b/src/main/java/com/android/tools/r8/code/Format10t.java
@@ -43,11 +43,8 @@
}
@Override
- public final boolean equals(Object other) {
- if (other == null || (this.getClass() != other.getClass())) {
- return false;
- }
- return ((Format10t) other).AA == AA;
+ final int internalCompareTo(Instruction other) {
+ return Byte.compare(AA, ((Format10t) other).AA);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/Format10x.java b/src/main/java/com/android/tools/r8/code/Format10x.java
index 6ffc866..bc8feed 100644
--- a/src/main/java/com/android/tools/r8/code/Format10x.java
+++ b/src/main/java/com/android/tools/r8/code/Format10x.java
@@ -33,16 +33,6 @@
}
@Override
- public int hashCode() {
- return getClass().hashCode();
- }
-
- @Override
- public boolean equals(Object other) {
- return (other != null) && (this.getClass() == other.getClass());
- }
-
- @Override
public String toString(ClassNameMapper naming) {
return formatString(null);
}
diff --git a/src/main/java/com/android/tools/r8/code/Format11n.java b/src/main/java/com/android/tools/r8/code/Format11n.java
index 933beba..61d845e 100644
--- a/src/main/java/com/android/tools/r8/code/Format11n.java
+++ b/src/main/java/com/android/tools/r8/code/Format11n.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.ComparatorUtils;
import java.nio.ShortBuffer;
abstract class Format11n extends Base1Format {
@@ -52,12 +53,9 @@
}
@Override
- public final boolean equals(Object other) {
- if (other == null || (this.getClass() != other.getClass())) {
- return false;
- }
+ final int internalCompareTo(Instruction other) {
Format11n o = (Format11n) other;
- return o.A == A && o.B == B;
+ return ComparatorUtils.compareInts(A, o.A, B, o.B);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/Format11x.java b/src/main/java/com/android/tools/r8/code/Format11x.java
index 5d627bd..4509fbd 100644
--- a/src/main/java/com/android/tools/r8/code/Format11x.java
+++ b/src/main/java/com/android/tools/r8/code/Format11x.java
@@ -43,11 +43,8 @@
}
@Override
- public final boolean equals(Object other) {
- if (other == null || (this.getClass() != other.getClass())) {
- return false;
- }
- return ((Format11x) other).AA == AA;
+ final int internalCompareTo(Instruction other) {
+ return Short.compare(AA, ((Format11x) other).AA);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/Format12x.java b/src/main/java/com/android/tools/r8/code/Format12x.java
index 36284b9..db7ca5a 100644
--- a/src/main/java/com/android/tools/r8/code/Format12x.java
+++ b/src/main/java/com/android/tools/r8/code/Format12x.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.ComparatorUtils;
import java.nio.ShortBuffer;
abstract class Format12x extends Base1Format {
@@ -46,14 +47,12 @@
}
@Override
- public final boolean equals(Object other) {
- if (other == null || this.getClass() != other.getClass()) {
- return false;
- }
+ final int internalCompareTo(Instruction other) {
Format12x o = (Format12x) other;
- return o.A == A && o.B == B;
+ return ComparatorUtils.compareInts(A, o.A, B, o.B);
}
+
@Override
public String toString(ClassNameMapper naming) {
return formatString("v" + A + ", v" + B);
diff --git a/src/main/java/com/android/tools/r8/code/Format20t.java b/src/main/java/com/android/tools/r8/code/Format20t.java
index 42d172a..8f34250 100644
--- a/src/main/java/com/android/tools/r8/code/Format20t.java
+++ b/src/main/java/com/android/tools/r8/code/Format20t.java
@@ -43,11 +43,8 @@
}
@Override
- public final boolean equals(Object other) {
- if (other == null || this.getClass() != other.getClass()) {
- return false;
- }
- return ((Format20t) other).AAAA == AAAA;
+ final int internalCompareTo(Instruction other) {
+ return Short.compare(AAAA, ((Format20t) other).AAAA);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/Format21c.java b/src/main/java/com/android/tools/r8/code/Format21c.java
index 9118072..074b79a 100644
--- a/src/main/java/com/android/tools/r8/code/Format21c.java
+++ b/src/main/java/com/android/tools/r8/code/Format21c.java
@@ -4,13 +4,8 @@
package com.android.tools.r8.code;
import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.IndexedDexItem;
-import com.android.tools.r8.graph.ObjectToOffsetMapping;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.naming.ClassNameMapper;
-import java.nio.ShortBuffer;
import java.util.function.BiPredicate;
abstract class Format21c<T extends IndexedDexItem> extends Base2Format {
@@ -32,30 +27,19 @@
}
@Override
- public void write(
- ShortBuffer dest,
- ProgramMethod context,
- GraphLens graphLens,
- ObjectToOffsetMapping mapping,
- LensCodeRewriterUtils rewriter) {
- writeFirst(AA, dest);
- write16BitReference(BBBB, dest, mapping);
- }
-
- @Override
public final int hashCode() {
return ((BBBB.hashCode() << 8) | AA) ^ getClass().hashCode();
}
@Override
- public final boolean equals(Object other) {
- if (other == null || this.getClass() != other.getClass()) {
- return false;
- }
+ final int internalCompareTo(Instruction other) {
Format21c<?> o = (Format21c<?>) other;
- return o.AA == AA && o.BBBB.equals(BBBB);
+ int aaDiff = Short.compare(AA, o.AA);
+ return aaDiff != 0 ? aaDiff : internalCompareBBBB(o);
}
+ abstract int internalCompareBBBB(Format21c<?> other);
+
@Override
public String toString(ClassNameMapper naming) {
return formatString(
diff --git a/src/main/java/com/android/tools/r8/code/Format21h.java b/src/main/java/com/android/tools/r8/code/Format21h.java
index 848990d..537a020 100644
--- a/src/main/java/com/android/tools/r8/code/Format21h.java
+++ b/src/main/java/com/android/tools/r8/code/Format21h.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.graph.ObjectToOffsetMapping;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
+import com.android.tools.r8.utils.ComparatorUtils;
import java.nio.ShortBuffer;
abstract class Format21h extends Base2Format {
@@ -47,12 +48,9 @@
}
@Override
- public final boolean equals(Object other) {
- if (other == null || this.getClass() != other.getClass()) {
- return false;
- }
+ final int internalCompareTo(Instruction other) {
Format21h o = (Format21h) other;
- return o.AA == AA && o.BBBB == BBBB;
+ return ComparatorUtils.compareInts(AA, o.AA, BBBB, o.BBBB);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/Format21s.java b/src/main/java/com/android/tools/r8/code/Format21s.java
index 31c4bc9..3f4e0fc 100644
--- a/src/main/java/com/android/tools/r8/code/Format21s.java
+++ b/src/main/java/com/android/tools/r8/code/Format21s.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.ComparatorUtils;
import com.android.tools.r8.utils.StringUtils;
import java.nio.ShortBuffer;
@@ -49,12 +50,9 @@
}
@Override
- public final boolean equals(Object other) {
- if (other == null || this.getClass() != other.getClass()) {
- return false;
- }
+ final int internalCompareTo(Instruction other) {
Format21s o = (Format21s) other;
- return o.AA == AA && o.BBBB == BBBB;
+ return ComparatorUtils.compareInts(AA, o.AA, BBBB, o.BBBB);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/Format21t.java b/src/main/java/com/android/tools/r8/code/Format21t.java
index 5e52bac..e580a1f 100644
--- a/src/main/java/com/android/tools/r8/code/Format21t.java
+++ b/src/main/java/com/android/tools/r8/code/Format21t.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.ir.conversion.IRBuilder;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.ComparatorUtils;
import java.nio.ShortBuffer;
public abstract class Format21t extends Base2Format {
@@ -51,12 +52,9 @@
}
@Override
- public final boolean equals(Object other) {
- if (other == null || this.getClass() != other.getClass()) {
- return false;
- }
+ final int internalCompareTo(Instruction other) {
Format21t o = (Format21t) other;
- return o.AA == AA && o.BBBB == BBBB;
+ return ComparatorUtils.compareInts(AA, o.AA, BBBB, o.BBBB);
}
public abstract Type getType();
diff --git a/src/main/java/com/android/tools/r8/code/Format22b.java b/src/main/java/com/android/tools/r8/code/Format22b.java
index 48955dc..5bbd0da 100644
--- a/src/main/java/com/android/tools/r8/code/Format22b.java
+++ b/src/main/java/com/android/tools/r8/code/Format22b.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.ComparatorUtils;
import com.android.tools.r8.utils.StringUtils;
import java.nio.ShortBuffer;
@@ -53,12 +54,9 @@
}
@Override
- public final boolean equals(Object other) {
- if (other == null || this.getClass() != other.getClass()) {
- return false;
- }
+ final int internalCompareTo(Instruction other) {
Format22b o = (Format22b) other;
- return o.AA == AA && o.BB == BB && o.CC == CC;
+ return ComparatorUtils.compareInts(AA, o.AA, BB, o.BB, CC, o.CC);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/Format22c.java b/src/main/java/com/android/tools/r8/code/Format22c.java
index 0ff7841..bd16c8f 100644
--- a/src/main/java/com/android/tools/r8/code/Format22c.java
+++ b/src/main/java/com/android/tools/r8/code/Format22c.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.IndexedDexItem;
import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.ComparatorUtils;
import java.util.function.BiPredicate;
public abstract class Format22c<T extends DexReference> extends Base2Format {
@@ -37,12 +38,10 @@
}
@Override
- public final boolean equals(Object other) {
- if (other == null || this.getClass() != other.getClass()) {
- return false;
- }
- Format22c<?> o = (Format22c<?>) other;
- return o.A == A && o.B == B && o.CCCC.equals(CCCC);
+ final int internalCompareTo(Instruction other) {
+ Format22c<? extends DexReference> o = (Format22c<? extends DexReference>) other;
+ int diff = ComparatorUtils.compareInts(A, o.A, B, o.B);
+ return diff != 0 ? diff : CCCC.referenceCompareTo(o.CCCC);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/Format22s.java b/src/main/java/com/android/tools/r8/code/Format22s.java
index 1b9aeb4..8492eae 100644
--- a/src/main/java/com/android/tools/r8/code/Format22s.java
+++ b/src/main/java/com/android/tools/r8/code/Format22s.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.ComparatorUtils;
import com.android.tools.r8.utils.StringUtils;
import java.nio.ShortBuffer;
@@ -53,12 +54,9 @@
}
@Override
- public final boolean equals(Object other) {
- if (other == null || this.getClass() != other.getClass()) {
- return false;
- }
+ final int internalCompareTo(Instruction other) {
Format22s o = (Format22s) other;
- return o.A == A && o.B == B && o.CCCC == CCCC;
+ return ComparatorUtils.compareInts(A, o.A, B, o.B, CCCC, o.CCCC);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/Format22t.java b/src/main/java/com/android/tools/r8/code/Format22t.java
index 4e75d23..5c64988 100644
--- a/src/main/java/com/android/tools/r8/code/Format22t.java
+++ b/src/main/java/com/android/tools/r8/code/Format22t.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.ir.conversion.IRBuilder;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.ComparatorUtils;
import java.nio.ShortBuffer;
public abstract class Format22t extends Base2Format {
@@ -55,12 +56,9 @@
}
@Override
- public final boolean equals(Object other) {
- if (other == null || this.getClass() != other.getClass()) {
- return false;
- }
+ final int internalCompareTo(Instruction other) {
Format22t o = (Format22t) other;
- return o.A == A && o.B == B && o.CCCC == CCCC;
+ return ComparatorUtils.compareInts(A, o.A, B, o.B, CCCC, o.CCCC);
}
public abstract Type getType();
diff --git a/src/main/java/com/android/tools/r8/code/Format22x.java b/src/main/java/com/android/tools/r8/code/Format22x.java
index 901af1e..626e7f9 100644
--- a/src/main/java/com/android/tools/r8/code/Format22x.java
+++ b/src/main/java/com/android/tools/r8/code/Format22x.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.ComparatorUtils;
import java.nio.ShortBuffer;
abstract class Format22x extends Base2Format {
@@ -48,14 +49,12 @@
}
@Override
- public final boolean equals(Object other) {
- if (other == null || this.getClass() != other.getClass()) {
- return false;
- }
+ final int internalCompareTo(Instruction other) {
Format22x o = (Format22x) other;
- return o.AA == AA && o.BBBB == BBBB;
+ return ComparatorUtils.compareInts(AA, o.AA, BBBB, o.BBBB);
}
+
@Override
public String toString(ClassNameMapper naming) {
return formatString("v" + AA + ", v" + (int)BBBB);
diff --git a/src/main/java/com/android/tools/r8/code/Format23x.java b/src/main/java/com/android/tools/r8/code/Format23x.java
index e9d030b..a87dddf 100644
--- a/src/main/java/com/android/tools/r8/code/Format23x.java
+++ b/src/main/java/com/android/tools/r8/code/Format23x.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.ComparatorUtils;
import java.nio.ShortBuffer;
abstract class Format23x extends Base2Format {
@@ -52,12 +53,9 @@
}
@Override
- public final boolean equals(Object other) {
- if (other == null || this.getClass() != other.getClass()) {
- return false;
- }
+ final int internalCompareTo(Instruction other) {
Format23x o = (Format23x) other;
- return o.AA == AA && o.BB == BB && o.CC == CC;
+ return ComparatorUtils.compareInts(AA, o.AA, BB, o.BB, CC, o.CC);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/Format30t.java b/src/main/java/com/android/tools/r8/code/Format30t.java
index dcf5064..81dcf86 100644
--- a/src/main/java/com/android/tools/r8/code/Format30t.java
+++ b/src/main/java/com/android/tools/r8/code/Format30t.java
@@ -42,11 +42,8 @@
}
@Override
- public final boolean equals(Object other) {
- if (other == null || (this.getClass() != other.getClass())) {
- return false;
- }
- return ((Format30t) other).AAAAAAAA == AAAAAAAA;
+ final int internalCompareTo(Instruction other) {
+ return Integer.compare(AAAAAAAA, ((Format30t) other).AAAAAAAA);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/Format31c.java b/src/main/java/com/android/tools/r8/code/Format31c.java
index 023aeac..a1ae51e 100644
--- a/src/main/java/com/android/tools/r8/code/Format31c.java
+++ b/src/main/java/com/android/tools/r8/code/Format31c.java
@@ -51,12 +51,10 @@
}
@Override
- public final boolean equals(Object other) {
- if (other == null || (this.getClass() != other.getClass())) {
- return false;
- }
+ final int internalCompareTo(Instruction other) {
Format31c o = (Format31c) other;
- return o.AA == AA && o.BBBBBBBB.equals(BBBBBBBB);
+ int diff = Short.compare(AA, o.AA);
+ return diff != 0 ? diff : BBBBBBBB.slowCompareTo(o.BBBBBBBB);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/Format31i.java b/src/main/java/com/android/tools/r8/code/Format31i.java
index 843319b..3d0d27e 100644
--- a/src/main/java/com/android/tools/r8/code/Format31i.java
+++ b/src/main/java/com/android/tools/r8/code/Format31i.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.ComparatorUtils;
import java.nio.ShortBuffer;
abstract class Format31i extends Base3Format {
@@ -47,12 +48,9 @@
}
@Override
- public final boolean equals(Object other) {
- if (other == null || (this.getClass() != other.getClass())) {
- return false;
- }
+ final int internalCompareTo(Instruction other) {
Format31i o = (Format31i) other;
- return o.AA == AA && o.BBBBBBBB == BBBBBBBB;
+ return ComparatorUtils.compareInts(AA, o.AA, BBBBBBBB, o.BBBBBBBB);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/Format31t.java b/src/main/java/com/android/tools/r8/code/Format31t.java
index 9c7fdfd..8e36d79 100644
--- a/src/main/java/com/android/tools/r8/code/Format31t.java
+++ b/src/main/java/com/android/tools/r8/code/Format31t.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.ComparatorUtils;
import java.nio.ShortBuffer;
public abstract class Format31t extends Base3Format {
@@ -62,12 +63,9 @@
}
@Override
- public final boolean equals(Object other) {
- if (other == null || (this.getClass() != other.getClass())) {
- return false;
- }
+ final int internalCompareTo(Instruction other) {
Format31t o = (Format31t) other;
- return o.AA == AA && o.BBBBBBBB == BBBBBBBB;
+ return ComparatorUtils.compareInts(AA, o.AA, BBBBBBBB, o.BBBBBBBB);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/Format32x.java b/src/main/java/com/android/tools/r8/code/Format32x.java
index 34ecf4f..a5360db 100644
--- a/src/main/java/com/android/tools/r8/code/Format32x.java
+++ b/src/main/java/com/android/tools/r8/code/Format32x.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.ComparatorUtils;
import java.nio.ShortBuffer;
abstract class Format32x extends Base3Format {
@@ -50,12 +51,9 @@
}
@Override
- public final boolean equals(Object other) {
- if (other == null || (this.getClass() != other.getClass())) {
- return false;
- }
+ final int internalCompareTo(Instruction other) {
Format32x o = (Format32x) other;
- return o.AAAA == AAAA && o.BBBB == BBBB;
+ return ComparatorUtils.compareInts(AAAA, o.AAAA, BBBB, o.BBBB);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/Format35c.java b/src/main/java/com/android/tools/r8/code/Format35c.java
index 8c2cb22..8e446db 100644
--- a/src/main/java/com/android/tools/r8/code/Format35c.java
+++ b/src/main/java/com/android/tools/r8/code/Format35c.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.IndexedDexItem;
import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.ComparatorUtils;
import java.util.function.BiPredicate;
public abstract class Format35c<T extends IndexedDexItem> extends Base3Format {
@@ -55,15 +56,21 @@
}
@Override
- public final boolean equals(Object other) {
- if (other == null || (this.getClass() != other.getClass())) {
- return false;
- }
- Format35c o = (Format35c) other;
- return o.A == A && o.C == C && o.D == D && o.E == E && o.F == F && o.G == G
- && o.BBBB.equals(BBBB);
+ final int internalCompareTo(Instruction other) {
+ Format35c<?> o = (Format35c<?>) other;
+ int diff =
+ ComparatorUtils.compareInts(
+ A, o.A,
+ C, o.C,
+ D, o.D,
+ E, o.E,
+ F, o.F,
+ G, o.G);
+ return diff != 0 ? diff : internalCompareBBBB(o);
}
+ abstract int internalCompareBBBB(Format35c<?> other);
+
private void appendRegisterArguments(StringBuilder builder, String separator) {
builder.append("{ ");
int[] values = new int[]{C, D, E, F, G};
diff --git a/src/main/java/com/android/tools/r8/code/Format3rc.java b/src/main/java/com/android/tools/r8/code/Format3rc.java
index 98e78cc..9102ecb 100644
--- a/src/main/java/com/android/tools/r8/code/Format3rc.java
+++ b/src/main/java/com/android/tools/r8/code/Format3rc.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.IndexedDexItem;
import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.ComparatorUtils;
import java.util.function.BiPredicate;
public abstract class Format3rc<T extends IndexedDexItem> extends Base3Format {
@@ -40,14 +41,14 @@
}
@Override
- public final boolean equals(Object other) {
- if (other == null || (this.getClass() != other.getClass())) {
- return false;
- }
+ final int internalCompareTo(Instruction other) {
Format3rc<?> o = (Format3rc<?>) other;
- return o.AA == AA && o.CCCC == CCCC && o.BBBB.equals(BBBB);
+ int diff = ComparatorUtils.compareInts(AA, o.AA, CCCC, o.CCCC);
+ return diff != 0 ? diff : internalCompareBBBB(o);
}
+ abstract int internalCompareBBBB(Format3rc<?> other);
+
private void appendRegisterRange(StringBuilder builder) {
int firstRegister = CCCC;
builder.append("{ ");
diff --git a/src/main/java/com/android/tools/r8/code/Format45cc.java b/src/main/java/com/android/tools/r8/code/Format45cc.java
index f6820c8..aa62780 100644
--- a/src/main/java/com/android/tools/r8/code/Format45cc.java
+++ b/src/main/java/com/android/tools/r8/code/Format45cc.java
@@ -9,13 +9,14 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.GraphLens.GraphLensLookupResult;
+import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
import com.android.tools.r8.graph.IndexedDexItem;
import com.android.tools.r8.graph.ObjectToOffsetMapping;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.ComparatorUtils;
import java.nio.ShortBuffer;
/** Format45cc for instructions of size 4, with 5 registers and 2 constant pool index. */
@@ -76,19 +77,21 @@
}
@Override
- public final boolean equals(Object other) {
- if (other == null || (this.getClass() != other.getClass())) {
- return false;
- }
+ final int internalCompareTo(Instruction other) {
Format45cc o = (Format45cc) other;
- return o.A == A
- && o.C == C
- && o.D == D
- && o.E == E
- && o.F == F
- && o.G == G
- && o.BBBB.equals(BBBB)
- && o.HHHH.equals(HHHH);
+ int diff =
+ ComparatorUtils.compareInts(
+ A, o.A,
+ C, o.C,
+ D, o.D,
+ E, o.E,
+ F, o.F,
+ G, o.G);
+ if (diff != 0) {
+ return diff;
+ }
+ int bDiff = BBBB.slowCompareTo(o.BBBB);
+ return bDiff != 0 ? bDiff : HHHH.slowCompareTo(o.HHHH);
}
@Override
@@ -97,10 +100,10 @@
ProgramMethod context,
GraphLens graphLens,
LensCodeRewriterUtils rewriter) {
- GraphLensLookupResult lookup =
+ MethodLookupResult lookup =
graphLens.lookupMethod(getMethod(), context.getReference(), Type.POLYMORPHIC);
assert lookup.getType() == Type.POLYMORPHIC;
- lookup.getMethod().collectIndexedItems(indexedItems);
+ lookup.getReference().collectIndexedItems(indexedItems);
DexProto rewrittenProto = rewriter.rewriteProto(getProto());
rewrittenProto.collectIndexedItems(indexedItems);
@@ -113,11 +116,11 @@
GraphLens graphLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
- GraphLensLookupResult lookup =
+ MethodLookupResult lookup =
graphLens.lookupMethod(getMethod(), context.getReference(), Type.POLYMORPHIC);
assert lookup.getType() == Type.POLYMORPHIC;
writeFirst(A, G, dest);
- write16BitReference(lookup.getMethod(), dest, mapping);
+ write16BitReference(lookup.getReference(), dest, mapping);
write16BitValue(combineBytes(makeByte(F, E), makeByte(D, C)), dest);
DexProto rewrittenProto = rewriter.rewriteProto(getProto());
diff --git a/src/main/java/com/android/tools/r8/code/Format4rcc.java b/src/main/java/com/android/tools/r8/code/Format4rcc.java
index 64bbc1e..690b230 100644
--- a/src/main/java/com/android/tools/r8/code/Format4rcc.java
+++ b/src/main/java/com/android/tools/r8/code/Format4rcc.java
@@ -8,7 +8,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.GraphLens.GraphLensLookupResult;
+import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
import com.android.tools.r8.graph.IndexedDexItem;
import com.android.tools.r8.graph.ObjectToOffsetMapping;
import com.android.tools.r8.graph.ProgramMethod;
@@ -16,6 +16,7 @@
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.naming.ClassNameMapper;
import java.nio.ShortBuffer;
+import java.util.Comparator;
import java.util.function.BiPredicate;
/** Format4rcc for instructions of size 4, with a range of registers and 2 constant pool index. */
@@ -51,11 +52,11 @@
GraphLens graphLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
- GraphLensLookupResult lookup =
+ MethodLookupResult lookup =
graphLens.lookupMethod(getMethod(), context.getReference(), Type.POLYMORPHIC);
assert lookup.getType() == Type.POLYMORPHIC;
writeFirst(AA, dest);
- write16BitReference(lookup.getMethod(), dest, mapping);
+ write16BitReference(lookup.getReference(), dest, mapping);
write16BitValue(CCCC, dest);
DexProto rewrittenProto = rewriter.rewriteProto(getProto());
@@ -69,12 +70,12 @@
}
@Override
- public final boolean equals(Object other) {
- if (other == null || (this.getClass() != other.getClass())) {
- return false;
- }
- Format4rcc o = (Format4rcc) other;
- return o.AA == AA && o.CCCC == CCCC && o.BBBB.equals(BBBB) && o.HHHH.equals(HHHH);
+ final int internalCompareTo(Instruction other) {
+ return Comparator.comparingInt((Format4rcc i) -> i.AA)
+ .thenComparingInt(i -> i.CCCC)
+ .thenComparing(i -> i.BBBB, DexMethod::slowCompareTo)
+ .thenComparing(i -> i.HHHH, DexProto::slowCompareTo)
+ .compare(this, (Format4rcc) other);
}
@Override
@@ -113,10 +114,10 @@
ProgramMethod context,
GraphLens graphLens,
LensCodeRewriterUtils rewriter) {
- GraphLensLookupResult lookup =
+ MethodLookupResult lookup =
graphLens.lookupMethod(getMethod(), context.getReference(), Type.POLYMORPHIC);
assert lookup.getType() == Type.POLYMORPHIC;
- lookup.getMethod().collectIndexedItems(indexedItems);
+ lookup.getReference().collectIndexedItems(indexedItems);
DexProto rewrittenProto = rewriter.rewriteProto(getProto());
rewrittenProto.collectIndexedItems(indexedItems);
diff --git a/src/main/java/com/android/tools/r8/code/Format51l.java b/src/main/java/com/android/tools/r8/code/Format51l.java
index face456..6c583c4 100644
--- a/src/main/java/com/android/tools/r8/code/Format51l.java
+++ b/src/main/java/com/android/tools/r8/code/Format51l.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.naming.ClassNameMapper;
import java.nio.ShortBuffer;
+import java.util.Comparator;
abstract class Format51l extends Base5Format {
@@ -47,12 +48,10 @@
}
@Override
- public final boolean equals(Object other) {
- if (other == null || this.getClass() != other.getClass()) {
- return false;
- }
- Format51l o = (Format51l) other;
- return o.AA == AA && o.BBBBBBBBBBBBBBBB == BBBBBBBBBBBBBBBB;
+ final int internalCompareTo(Instruction other) {
+ return Comparator.comparingInt((Format51l i) -> i.AA)
+ .thenComparingLong(i -> i.BBBBBBBBBBBBBBBB)
+ .compare(this, (Format51l) other);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/Instruction.java b/src/main/java/com/android/tools/r8/code/Instruction.java
index 766638c..4ff81dc 100644
--- a/src/main/java/com/android/tools/r8/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/code/Instruction.java
@@ -21,7 +21,7 @@
import java.nio.ShortBuffer;
import java.util.function.BiPredicate;
-public abstract class Instruction {
+public abstract class Instruction implements Comparable<Instruction> {
public static final Instruction[] EMPTY_ARRAY = {};
public final static int[] NO_TARGETS = null;
@@ -282,11 +282,29 @@
}
@Override
- public abstract boolean equals(Object obj);
+ public final boolean equals(Object other) {
+ return other instanceof Instruction && compareTo((Instruction) other) == 0;
+ }
@Override
public abstract int hashCode();
+ int getCompareToId() {
+ return getOpcode();
+ }
+
+ // Abstract compare-to called only if the opcode/compare-id of the instruction matches.
+ abstract int internalCompareTo(Instruction other);
+
+ @Override
+ public final int compareTo(Instruction other) {
+ if (this == other) {
+ return 0;
+ }
+ int opcodeDiff = Integer.compare(getCompareToId(), other.getCompareToId());
+ return opcodeDiff != 0 ? opcodeDiff : internalCompareTo(other);
+ }
+
public abstract String getName();
public abstract String getSmaliName();
diff --git a/src/main/java/com/android/tools/r8/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/code/InvokeCustom.java
index 1355f13..fb6660a 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeCustom.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeCustom.java
@@ -44,6 +44,11 @@
}
@Override
+ int internalCompareBBBB(Format35c<?> other) {
+ return BBBB.compareTo((DexCallSite) other.BBBB);
+ }
+
+ @Override
public void collectIndexedItems(
IndexedItemCollection indexedItems,
ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/code/InvokeCustomRange.java b/src/main/java/com/android/tools/r8/code/InvokeCustomRange.java
index 2d09e4f..71e8399 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeCustomRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeCustomRange.java
@@ -44,6 +44,11 @@
}
@Override
+ int internalCompareBBBB(Format3rc<?> other) {
+ return BBBB.compareTo((DexCallSite) other.BBBB);
+ }
+
+ @Override
public void collectIndexedItems(
IndexedItemCollection indexedItems,
ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/code/InvokeMethod.java
index c8539f3..de46296 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeMethod.java
@@ -6,7 +6,7 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.GraphLens.GraphLensLookupResult;
+import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
import com.android.tools.r8.graph.ObjectToOffsetMapping;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.Invoke;
@@ -30,7 +30,7 @@
GraphLens graphLens,
LensCodeRewriterUtils rewriter) {
DexMethod rewritten =
- graphLens.lookupMethod(getMethod(), context.getReference(), getInvokeType()).getMethod();
+ graphLens.lookupMethod(getMethod(), context.getReference(), getInvokeType()).getReference();
rewritten.collectIndexedItems(indexedItems);
}
@@ -42,16 +42,21 @@
public abstract Invoke.Type getInvokeType();
@Override
+ int internalCompareBBBB(Format35c<?> other) {
+ return BBBB.slowCompareTo((DexMethod) other.BBBB);
+ }
+
+ @Override
public void write(
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
- GraphLensLookupResult lookup =
+ MethodLookupResult lookup =
graphLens.lookupMethod(getMethod(), context.getReference(), getInvokeType());
writeFirst(A, G, dest, lookup.getType().getDexOpcode());
- write16BitReference(lookup.getMethod(), dest, mapping);
+ write16BitReference(lookup.getReference(), dest, mapping);
write16BitValue(combineBytes(makeByte(F, E), makeByte(D, C)), dest);
}
}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeMethodRange.java b/src/main/java/com/android/tools/r8/code/InvokeMethodRange.java
index 4e4d705..102d793 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeMethodRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeMethodRange.java
@@ -6,7 +6,7 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.GraphLens.GraphLensLookupResult;
+import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
import com.android.tools.r8.graph.ObjectToOffsetMapping;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.Invoke.Type;
@@ -30,7 +30,7 @@
GraphLens graphLens,
LensCodeRewriterUtils rewriter) {
DexMethod rewritten =
- graphLens.lookupMethod(getMethod(), context.getReference(), getInvokeType()).getMethod();
+ graphLens.lookupMethod(getMethod(), context.getReference(), getInvokeType()).getReference();
rewritten.collectIndexedItems(indexedItems);
}
@@ -42,16 +42,21 @@
public abstract Type getInvokeType();
@Override
+ int internalCompareBBBB(Format3rc<?> other) {
+ return BBBB.slowCompareTo((DexMethod) other.BBBB);
+ }
+
+ @Override
public void write(
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
- GraphLensLookupResult lookup =
+ MethodLookupResult lookup =
graphLens.lookupMethod(getMethod(), context.getReference(), getInvokeType());
writeFirst(AA, dest, lookup.getType().getDexOpcodeRange());
- write16BitReference(lookup.getMethod(), dest, mapping);
+ write16BitReference(lookup.getReference(), dest, mapping);
write16BitValue(CCCC, dest);
}
}
diff --git a/src/main/java/com/android/tools/r8/code/NewInstance.java b/src/main/java/com/android/tools/r8/code/NewInstance.java
index da58744..d3d25c6 100644
--- a/src/main/java/com/android/tools/r8/code/NewInstance.java
+++ b/src/main/java/com/android/tools/r8/code/NewInstance.java
@@ -6,11 +6,13 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
import com.android.tools.r8.graph.OffsetToObjectMapping;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.conversion.IRBuilder;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
+import java.nio.ShortBuffer;
public class NewInstance extends Format21c<DexType> {
@@ -42,6 +44,11 @@
}
@Override
+ int internalCompareBBBB(Format21c<?> other) {
+ return BBBB.slowCompareTo((DexType) other.BBBB);
+ }
+
+ @Override
public void collectIndexedItems(
IndexedItemCollection indexedItems,
ProgramMethod context,
@@ -52,6 +59,18 @@
}
@Override
+ public void write(
+ ShortBuffer dest,
+ ProgramMethod context,
+ GraphLens graphLens,
+ ObjectToOffsetMapping mapping,
+ LensCodeRewriterUtils rewriter) {
+ DexType rewritten = graphLens.lookupType(getType());
+ writeFirst(AA, dest);
+ write16BitReference(rewritten, dest, mapping);
+ }
+
+ @Override
public void registerUse(UseRegistry registry) {
registry.registerNewInstance(getType());
}
diff --git a/src/main/java/com/android/tools/r8/code/Nop.java b/src/main/java/com/android/tools/r8/code/Nop.java
index 748d7ca..ae56ec8 100644
--- a/src/main/java/com/android/tools/r8/code/Nop.java
+++ b/src/main/java/com/android/tools/r8/code/Nop.java
@@ -31,9 +31,16 @@
}
}
+ // Notice that this must be overridden by the "Nop" subtypes!
+ @Override
+ int internalCompareTo(Instruction other) {
+ return DexCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ }
+
+ // Notice that this must be overridden by the "Nop" subtypes!
@Override
public int hashCode() {
- return NAME.hashCode() * 7 + super.hashCode();
+ return NAME.hashCode() * 7;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/PackedSwitchPayload.java b/src/main/java/com/android/tools/r8/code/PackedSwitchPayload.java
index c7caf43..4ea070c 100644
--- a/src/main/java/com/android/tools/r8/code/PackedSwitchPayload.java
+++ b/src/main/java/com/android/tools/r8/code/PackedSwitchPayload.java
@@ -8,9 +8,11 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.ComparatorUtils;
import com.android.tools.r8.utils.StringUtils;
import java.nio.ShortBuffer;
import java.util.Arrays;
+import java.util.Comparator;
public class PackedSwitchPayload extends SwitchPayload {
@@ -56,12 +58,11 @@
}
@Override
- public boolean equals(Object other) {
- if (!super.equals(other)) {
- return false;
- }
- PackedSwitchPayload that = (PackedSwitchPayload) other;
- return size == that.size && first_key == that.first_key && Arrays.equals(targets, that.targets);
+ final int internalCompareTo(Instruction other) {
+ return Comparator.comparingInt((PackedSwitchPayload i) -> i.size)
+ .thenComparingInt(i -> first_key)
+ .thenComparing(i -> i.targets, ComparatorUtils::compareIntArray)
+ .compare(this, (PackedSwitchPayload) other);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/ReturnVoid.java b/src/main/java/com/android/tools/r8/code/ReturnVoid.java
index 328e032..68cfb51 100644
--- a/src/main/java/com/android/tools/r8/code/ReturnVoid.java
+++ b/src/main/java/com/android/tools/r8/code/ReturnVoid.java
@@ -33,6 +33,16 @@
}
@Override
+ final int internalCompareTo(Instruction other) {
+ return DexCompareHelper.compareIdUniquelyDeterminesEquality(this, other);
+ }
+
+ @Override
+ public int hashCode() {
+ return NAME.hashCode();
+ }
+
+ @Override
public int[] getTargets() {
return EXIT_TARGET;
}
diff --git a/src/main/java/com/android/tools/r8/code/SgetOrSput.java b/src/main/java/com/android/tools/r8/code/SgetOrSput.java
index 1f77122..48112fc 100644
--- a/src/main/java/com/android/tools/r8/code/SgetOrSput.java
+++ b/src/main/java/com/android/tools/r8/code/SgetOrSput.java
@@ -6,8 +6,10 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
+import java.nio.ShortBuffer;
abstract class SgetOrSput extends Format21c<DexField> {
@@ -30,7 +32,24 @@
}
@Override
+ public void write(
+ ShortBuffer dest,
+ ProgramMethod context,
+ GraphLens graphLens,
+ ObjectToOffsetMapping mapping,
+ LensCodeRewriterUtils rewriter) {
+ DexField rewritten = graphLens.lookupField(getField());
+ writeFirst(AA, dest);
+ write16BitReference(rewritten, dest, mapping);
+ }
+
+ @Override
public final DexField getField() {
return BBBB;
}
+
+ @Override
+ int internalCompareBBBB(Format21c<?> other) {
+ return BBBB.slowCompareTo((DexField) other.BBBB);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/code/SparseSwitchPayload.java b/src/main/java/com/android/tools/r8/code/SparseSwitchPayload.java
index b73779b..1a1b1e9 100644
--- a/src/main/java/com/android/tools/r8/code/SparseSwitchPayload.java
+++ b/src/main/java/com/android/tools/r8/code/SparseSwitchPayload.java
@@ -8,9 +8,11 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.ComparatorUtils;
import com.android.tools.r8.utils.StringUtils;
import java.nio.ShortBuffer;
import java.util.Arrays;
+import java.util.Comparator;
public class SparseSwitchPayload extends SwitchPayload {
@@ -62,13 +64,11 @@
}
@Override
- public boolean equals(Object other) {
- if (!super.equals(other)) {
- return false;
- }
- SparseSwitchPayload that = (SparseSwitchPayload) other;
- return size == that.size && Arrays.equals(keys, that.keys) && Arrays
- .equals(targets, that.targets);
+ final int internalCompareTo(Instruction other) {
+ return Comparator.comparingInt((SparseSwitchPayload i) -> i.size)
+ .thenComparing(i -> i.keys, ComparatorUtils::compareIntArray)
+ .thenComparing(i -> i.targets, ComparatorUtils::compareIntArray)
+ .compare(this, (SparseSwitchPayload) other);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java
index a416e51..978a7e7 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -859,7 +859,7 @@
int insnsSize = dexReader.getUint();
short[] code = new short[insnsSize];
Try[] tries = new Try[triesSize];
- DexCode.TryHandler[] handlers = null;
+ TryHandler[] handlers = new TryHandler[0];
if (insnsSize != 0) {
for (int i = 0; i < insnsSize; i++) {
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index d9103a5..bb5706d 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -408,7 +408,7 @@
}
result += insnSize * 2;
result += code.tries.length * 8;
- if ((code.handlers != null) && (code.handlers.length > 0)) {
+ if (code.handlers.length > 0) {
result = alignSize(4, result);
result += LebUtils.sizeAsUleb128(code.handlers.length);
for (TryHandler handler : code.handlers) {
diff --git a/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java b/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
index e05af85..128ea1d 100644
--- a/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
+++ b/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
@@ -194,9 +194,6 @@
private TryHandler[] rewriteHandlerOffsets() {
DexCode code = method.getCode().asDexCode();
- if (code.handlers == null) {
- return null;
- }
TryHandler[] result = new TryHandler[code.handlers.length];
for (int i = 0; i < code.handlers.length; i++) {
TryHandler handler = code.handlers[i];
@@ -545,21 +542,19 @@
tryRangeStartAndEndTargets.put(start.getOffset(), start);
tryRangeStartAndEndTargets.put(end.getOffset(), end);
}
- if (code.handlers != null) {
- for (TryHandler handler : code.handlers) {
- List<Instruction> targets = new ArrayList<>();
- if (handler.catchAllAddr != NO_HANDLER) {
- Instruction target = offsetToInstruction.get(handler.catchAllAddr);
- assert target != null;
- targets.add(target);
- }
- for (TypeAddrPair pair : handler.pairs) {
- Instruction target = offsetToInstruction.get(pair.addr);
- assert target != null;
- targets.add(target);
- }
- handlerTargets.put(handler, targets);
+ for (TryHandler handler : code.handlers) {
+ List<Instruction> targets = new ArrayList<>();
+ if (handler.catchAllAddr != NO_HANDLER) {
+ Instruction target = offsetToInstruction.get(handler.catchAllAddr);
+ assert target != null;
+ targets.add(target);
}
+ for (TypeAddrPair pair : handler.pairs) {
+ Instruction target = offsetToInstruction.get(pair.addr);
+ assert target != null;
+ targets.add(target);
+ }
+ handlerTargets.put(handler, targets);
}
}
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 2e0ab82..e4f0a0c 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -76,7 +76,6 @@
private static final int MAX_PREFILL_ENTRIES = MAX_ENTRIES - 5000;
private final int id;
- private final GraphLens graphLens;
private final VirtualFileIndexedItemCollection indexedItems;
private final IndexedItemTransaction transaction;
private final FeatureSplit featureSplit;
@@ -121,7 +120,6 @@
DexProgramClass primaryClass,
FeatureSplit featureSplit) {
this.id = id;
- this.graphLens = graphLens;
this.indexedItems = new VirtualFileIndexedItemCollection(graphLens, initClassLens, namingLens);
this.transaction =
new IndexedItemTransaction(indexedItems, appView, graphLens, initClassLens, namingLens);
@@ -637,12 +635,12 @@
@Override
public boolean addField(DexField field) {
- return fields.add(graphLens.lookupField(field));
+ return fields.add(field);
}
@Override
public boolean addMethod(DexMethod method) {
- return methods.add(graphLens.lookupMethod(method));
+ return methods.add(method);
}
@Override
@@ -657,9 +655,8 @@
@Override
public boolean addType(DexType type) {
- DexType rewritten = graphLens.lookupType(type);
- assert SyntheticItems.verifyNotInternalSynthetic(rewritten);
- return types.add(rewritten);
+ assert SyntheticItems.verifyNotInternalSynthetic(type);
+ return types.add(type);
}
@Override
@@ -761,12 +758,12 @@
@Override
public boolean addField(DexField field) {
- return maybeInsert(base.graphLens.lookupField(field), fields, base.fields);
+ return maybeInsert(field, fields, base.fields);
}
@Override
public boolean addMethod(DexMethod method) {
- return maybeInsert(base.graphLens.lookupMethod(method), methods, base.methods);
+ return maybeInsert(method, methods, base.methods);
}
@Override
@@ -781,9 +778,8 @@
@Override
public boolean addType(DexType type) {
- DexType rewritten = base.graphLens.lookupType(type);
- assert SyntheticItems.verifyNotInternalSynthetic(rewritten);
- return maybeInsert(rewritten, types, base.types);
+ assert SyntheticItems.verifyNotInternalSynthetic(type);
+ return maybeInsert(type, types, base.types);
}
@Override
@@ -808,19 +804,18 @@
@Override
public DexString getRenamedDescriptor(DexType type) {
- return namingLens.lookupDescriptor(base.graphLens.lookupType(type));
+ return namingLens.lookupDescriptor(type);
}
@Override
public DexString getRenamedName(DexMethod method) {
- DexMethod mappedMethod = base.graphLens.lookupMethod(method);
- assert namingLens.verifyRenamingConsistentWithResolution(mappedMethod);
- return namingLens.lookupName(mappedMethod);
+ assert namingLens.verifyRenamingConsistentWithResolution(method);
+ return namingLens.lookupName(method);
}
@Override
public DexString getRenamedName(DexField field) {
- return namingLens.lookupName(base.graphLens.lookupField(field));
+ return namingLens.lookupName(field);
}
int getNumberOfMethods() {
diff --git a/src/main/java/com/android/tools/r8/graph/AbstractAccessContexts.java b/src/main/java/com/android/tools/r8/graph/AbstractAccessContexts.java
index e078248..4615f1f 100644
--- a/src/main/java/com/android/tools/r8/graph/AbstractAccessContexts.java
+++ b/src/main/java/com/android/tools/r8/graph/AbstractAccessContexts.java
@@ -182,7 +182,7 @@
}
}
- Map<DexField, ProgramMethodSet> getAccessesWithContexts() {
+ public Map<DexField, ProgramMethodSet> getAccessesWithContexts() {
return accessesWithContexts;
}
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 8792d0c..4bbcead 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
@@ -233,11 +233,15 @@
}
private boolean wasSet(int flag) {
- return (originalFlags & flag) != 0;
+ return isSet(originalFlags, flag);
}
protected boolean isSet(int flag) {
- return (modifiedFlags & flag) != 0;
+ return isSet(modifiedFlags, flag);
+ }
+
+ public static boolean isSet(int flag, int flags) {
+ return (flags & flag) != 0;
}
protected void set(int flag) {
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index 6da4e08..d2d137b 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.utils.BooleanBox;
import com.android.tools.r8.utils.InternalOptions;
import java.util.Collection;
+import java.util.function.Consumer;
public class AppInfo implements DexDefinitionSupplier {
@@ -125,6 +126,12 @@
return app.classesWithDeterministicOrder();
}
+ public void forEachMethod(Consumer<ProgramMethod> consumer) {
+ for (DexProgramClass clazz : classes()) {
+ clazz.forEachProgramMethod(consumer);
+ }
+ }
+
@Override
public DexClass definitionFor(DexType type) {
return definitionForWithoutExistenceAssert(type);
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 f89f265..f4407b4 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -6,7 +6,7 @@
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.graph.DexValue.DexValueString;
-import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
+import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.graph.analysis.InitializedClassesInInstanceMethodsAnalysis.InitializedClassesInInstanceMethods;
import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
import com.android.tools.r8.graph.classmerging.MergedClasses;
@@ -214,7 +214,7 @@
}
public GraphLens clearCodeRewritings() {
- return graphLens = graphLens.withCodeRewritingsApplied();
+ return graphLens = graphLens.withCodeRewritingsApplied(dexItemFactory());
}
public AppServices appServices() {
@@ -520,36 +520,54 @@
return !cfByteCodePassThrough.isEmpty();
}
- public void rewriteWithLens(NestedGraphLens lens) {
+ public void rewriteWithLens(NonIdentityGraphLens lens) {
if (lens != null) {
- rewriteWithLens(lens, appInfo().app().asDirect(), withLiveness());
+ rewriteWithLens(lens, appInfo().app().asDirect(), withLiveness(), lens.getPrevious());
}
}
- public void rewriteWithApplication(DirectMappedDexApplication application) {
- assert application != null;
- rewriteWithLens(null, application, withLiveness());
- }
-
public void rewriteWithLensAndApplication(
- NestedGraphLens lens, DirectMappedDexApplication application) {
+ NonIdentityGraphLens lens, DirectMappedDexApplication application) {
+ rewriteWithLensAndApplication(lens, application, lens.getPrevious());
+ }
+
+ public void rewriteWithLensAndApplication(
+ NonIdentityGraphLens lens, DirectMappedDexApplication application, GraphLens appliedLens) {
assert lens != null;
assert application != null;
- rewriteWithLens(lens, application, withLiveness());
+ rewriteWithLens(lens, application, withLiveness(), appliedLens);
}
private static void rewriteWithLens(
- NestedGraphLens lens,
+ NonIdentityGraphLens lens,
DirectMappedDexApplication application,
- AppView<AppInfoWithLiveness> appView) {
- if (lens != null) {
- boolean changed = appView.setGraphLens(lens);
- assert changed;
- assert application.verifyWithLens(lens);
+ AppView<AppInfoWithLiveness> appView,
+ GraphLens appliedLens) {
+ if (lens == null) {
+ return;
}
- appView.setAppInfo(appView.appInfo().rewrittenWithLens(application, lens));
- if (appView.hasInitClassLens()) {
- appView.setInitClassLens(appView.initClassLens().rewrittenWithLens(lens));
+
+ boolean changed = appView.setGraphLens(lens);
+ assert changed;
+ assert application.verifyWithLens(lens);
+
+ // The application has already been rewritten with the given applied lens. Therefore, we
+ // temporarily replace that lens with the identity lens to avoid the overhead of traversing
+ // the entire lens chain upon each lookup during the rewriting.
+ NonIdentityGraphLens temporaryRootLens = lens;
+ while (temporaryRootLens.getPrevious() != appliedLens) {
+ GraphLens previousLens = temporaryRootLens.getPrevious();
+ assert previousLens.isNonIdentityLens();
+ temporaryRootLens = previousLens.asNonIdentityLens();
}
+
+ temporaryRootLens.withAlternativeParentLens(
+ GraphLens.getIdentityLens(),
+ () -> {
+ appView.setAppInfo(appView.appInfo().rewrittenWithLens(application, lens));
+ if (appView.hasInitClassLens()) {
+ appView.setInitClassLens(appView.initClassLens().rewrittenWithLens(lens));
+ }
+ });
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
index c9e4d4d..ae54120 100644
--- a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
@@ -4,12 +4,16 @@
package com.android.tools.r8.graph;
-import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
+import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
import com.android.tools.r8.utils.MapUtils;
+import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import java.util.IdentityHashMap;
+import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* A graph lens that will not lead to any code rewritings in the {@link
@@ -18,11 +22,10 @@
*
* <p>The mappings from the original program to the generated program are kept, though.
*/
-public class AppliedGraphLens extends GraphLens {
+public final class AppliedGraphLens extends NonIdentityGraphLens {
- private final AppView<?> appView;
-
- private final BiMap<DexType, DexType> originalTypeNames = HashBiMap.create();
+ private final BidirectionalManyToOneMap<DexType, DexType> renamedTypeNames =
+ new BidirectionalManyToOneMap<>();
private final BiMap<DexField, DexField> originalFieldSignatures = HashBiMap.create();
private final BiMap<DexMethod, DexMethod> originalMethodSignatures = HashBiMap.create();
@@ -32,21 +35,10 @@
private final Map<DexMethod, DexMethod> extraOriginalMethodSignatures = new IdentityHashMap<>();
public AppliedGraphLens(AppView<? extends AppInfoWithClassHierarchy> appView) {
- this.appView = appView;
-
+ super(appView.dexItemFactory(), GraphLens.getIdentityLens());
for (DexProgramClass clazz : appView.appInfo().classes()) {
// Record original type names.
- {
- DexType type = clazz.type;
- if (appView.verticallyMergedClasses() != null
- && !appView.verticallyMergedClasses().hasBeenMergedIntoSubtype(type)) {
- DexType original = appView.graphLens().getOriginalType(type);
- if (original != type) {
- DexType existing = originalTypeNames.forcePut(type, original);
- assert existing == null;
- }
- }
- }
+ recordOriginalTypeNames(clazz, appView);
// Record original field signatures.
for (DexEncodedField encodedField : clazz.fields()) {
@@ -82,9 +74,41 @@
MapUtils.removeIdentityMappings(extraOriginalMethodSignatures);
}
+ private void recordOriginalTypeNames(
+ DexProgramClass clazz, AppView<? extends AppInfoWithClassHierarchy> appView) {
+ DexType type = clazz.getType();
+ VerticallyMergedClasses verticallyMergedClasses = appView.verticallyMergedClasses();
+ if (verticallyMergedClasses != null && verticallyMergedClasses.hasBeenMergedIntoSubtype(type)) {
+ return;
+ }
+
+ DexType original = appView.graphLens().getOriginalType(type);
+ if (verticallyMergedClasses != null) {
+ List<DexType> sources = verticallyMergedClasses.getSourcesFor(type);
+ if (!sources.isEmpty()) {
+ renamedTypeNames.put(original, type);
+ sources.forEach(source -> renamedTypeNames.put(source, type));
+ return;
+ }
+ }
+
+ if (original != type) {
+ renamedTypeNames.put(original, type);
+ }
+ }
+
+ @Override
+ public boolean isAppliedLens() {
+ return true;
+ }
+
@Override
public DexType getOriginalType(DexType type) {
- return originalTypeNames.getOrDefault(type, type);
+ Set<DexType> originalTypeNames = renamedTypeNames.getKeys(type);
+ if (!originalTypeNames.isEmpty()) {
+ return originalTypeNames.iterator().next();
+ }
+ return type;
}
@Override
@@ -113,27 +137,29 @@
}
@Override
- public DexType lookupType(DexType type) {
- if (appView.verticallyMergedClasses() != null
- && appView.verticallyMergedClasses().hasBeenMergedIntoSubtype(type)) {
- return lookupType(appView.verticallyMergedClasses().getTargetFor(type));
- }
- return originalTypeNames.inverse().getOrDefault(type, type);
- }
-
- @Override
- public GraphLensLookupResult lookupMethod(DexMethod method, DexMethod context, Invoke.Type type) {
- return GraphLens.getIdentityLens().lookupMethod(method, context, type);
- }
-
- @Override
public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(DexMethod method) {
return GraphLens.getIdentityLens().lookupPrototypeChangesForMethodDefinition(method);
}
@Override
- public DexField lookupField(DexField field) {
- return field;
+ public DexType internalDescribeLookupClassType(DexType previous) {
+ return renamedTypeNames.getOrDefault(previous, previous);
+ }
+
+ @Override
+ protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
+ return previous;
+ }
+
+ @Override
+ public MethodLookupResult internalDescribeLookupMethod(
+ MethodLookupResult previous, DexMethod context) {
+ return previous;
+ }
+
+ @Override
+ protected DexMethod internalGetPreviousMethodSignature(DexMethod method) {
+ return method;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
index 491178a..bd6c452 100644
--- a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
+++ b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
@@ -85,6 +85,32 @@
for (DexType value : clazz.interfaces.values) {
ps.println("# Implements: '" + value.toSourceString() + "'");
}
+ if (!clazz.getInnerClasses().isEmpty()) {
+ ps.println("# InnerClasses:");
+ for (InnerClassAttribute innerClassAttribute : clazz.getInnerClasses()) {
+ ps.println(
+ "# Outer: "
+ + (innerClassAttribute.getOuter() != null
+ ? innerClassAttribute.getOuter().toSourceString()
+ : "-")
+ + ", inner: "
+ + innerClassAttribute.getInner().toSourceString()
+ + ", inner name: "
+ + innerClassAttribute.getInnerName()
+ + ", access: "
+ + Integer.toHexString(innerClassAttribute.getAccess()));
+ }
+ }
+ EnclosingMethodAttribute enclosingMethodAttribute = clazz.getEnclosingMethodAttribute();
+ if (enclosingMethodAttribute != null) {
+ ps.println("# EnclosingMethod:");
+ if (enclosingMethodAttribute.getEnclosingClass() != null) {
+ ps.println("# Class: " + enclosingMethodAttribute.getEnclosingClass().toSourceString());
+ } else {
+ ps.println(
+ "# Method: " + enclosingMethodAttribute.getEnclosingMethod().toSourceString());
+ }
+ }
}
ps.println();
}
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 b74a480..b6355dc 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -5,11 +5,15 @@
import static com.android.tools.r8.graph.DexCode.FAKE_THIS_PREFIX;
import static com.android.tools.r8.graph.DexCode.FAKE_THIS_SUFFIX;
+import static com.android.tools.r8.ir.conversion.CfSourceCode.canThrowHelper;
+import static org.objectweb.asm.Opcodes.ACC_STATIC;
import static org.objectweb.asm.Opcodes.V1_5;
import static org.objectweb.asm.Opcodes.V1_6;
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.cf.code.CfFrame;
+import com.android.tools.r8.cf.code.CfFrame.FrameType;
+import com.android.tools.r8.cf.code.CfFrameVerificationHelper;
import com.android.tools.r8.cf.code.CfIinc;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfLabel;
@@ -19,7 +23,11 @@
import com.android.tools.r8.cf.code.CfTryCatch;
import com.android.tools.r8.errors.InvalidDebugInfoException;
import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfo;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.NumberGenerator;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -35,17 +43,26 @@
import com.android.tools.r8.utils.InternalOptions;
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.Map;
import java.util.Map.Entry;
+import java.util.function.BiPredicate;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
-public class CfCode extends Code {
+public class CfCode extends Code implements Comparable<CfCode> {
public static class LocalVariableInfo {
@@ -87,6 +104,14 @@
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);
+ }
+
@Override
public String toString() {
return "" + index + " => " + local;
@@ -181,6 +206,41 @@
return this;
}
+ @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);
+ }
+
+ 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;
+ }
+
private boolean shouldAddParameterNames(DexEncodedMethod method, AppView<?> appView) {
// Don't add parameter information if the code already has full debug information.
// Note: This fast path can cause a method to loose its parameter info, if the debug info turned
@@ -207,6 +267,7 @@
NamingLens namingLens,
LensCodeRewriterUtils rewriter,
MethodVisitor visitor) {
+ assert verifyFrames(method.getDefinition(), appView, null, false);
DexItemFactory dexItemFactory = appView.dexItemFactory();
GraphLens graphLens = appView.graphLens();
InitClassLens initClassLens = appView.initClassLens();
@@ -299,6 +360,7 @@
@Override
public IRCode buildIR(ProgramMethod method, AppView<?> appView, Origin origin) {
+ // TODO(b/164396438): Assert that we can validate frames.
return internalBuildPossiblyWithLocals(method, method, appView, null, null, origin, null);
}
@@ -313,6 +375,7 @@
MethodProcessor methodProcessor) {
assert valueNumberGenerator != null;
assert callerPosition != null;
+ // TODO(b/164396438): Assert that we can validate frames.
return internalBuildPossiblyWithLocals(
context, method, appView, valueNumberGenerator, callerPosition, origin, methodProcessor);
}
@@ -526,7 +589,6 @@
// IR processing.
inliningConstraints.disallowStaticInterfaceMethodCalls();
}
-
// Model a synchronized method as having a monitor instruction.
ConstraintWithTarget constraint =
method.getDefinition().isSynchronized()
@@ -579,4 +641,249 @@
new LocalVariableInfo(
thisLocalInfo.index, debugLocalInfo, thisLocalInfo.start, thisLocalInfo.end));
}
+
+ public boolean verifyFrames(
+ DexEncodedMethod method, AppView<?> appView, Origin origin, boolean applyProtoTypeChanges) {
+ if (!appView.options().testing.readInputStackMaps
+ || appView.options().testing.disableStackMapVerification) {
+ return true;
+ }
+ if (method.hasClassFileVersion() && method.getClassFileVersion() <= V1_6) {
+ return true;
+ }
+ if (!method.isInstanceInitializer()
+ && appView
+ .graphLens()
+ .getOriginalMethodSignature(method.method)
+ .isInstanceInitializer(appView.dexItemFactory())) {
+ // We cannot verify instance initializers if they are moved.
+ return true;
+ }
+ // Build a map from labels to frames.
+ Map<CfLabel, CfFrame> stateMap = new IdentityHashMap<>();
+ List<CfLabel> labels = new ArrayList<>();
+ boolean requireStackMapFrame = !tryCatchRanges.isEmpty();
+ for (CfInstruction instruction : instructions) {
+ if (instruction.isFrame()) {
+ CfFrame frame = instruction.asFrame();
+ if (!labels.isEmpty()) {
+ for (CfLabel label : labels) {
+ if (stateMap.containsKey(label)) {
+ return reportStackMapError(
+ CfCodeStackMapValidatingException.multipleFramesForLabel(
+ origin,
+ appView.graphLens().getOriginalMethodSignature(method.method),
+ appView),
+ appView);
+ }
+ stateMap.put(label, frame);
+ }
+ } else if (instruction != instructions.get(0)) {
+ // From b/168212806, it is possible that the first instruction is a frame.
+ return reportStackMapError(
+ CfCodeStackMapValidatingException.unexpectedStackMapFrame(
+ origin, appView.graphLens().getOriginalMethodSignature(method.method), appView),
+ appView);
+ }
+ }
+ // We are trying to map a frame to a label, but we can have positions in between, so skip
+ // those.
+ if (instruction.isPosition()) {
+ continue;
+ } else if (instruction.isLabel()) {
+ labels.add(instruction.asLabel());
+ } else {
+ labels.clear();
+ }
+ if (!requireStackMapFrame) {
+ requireStackMapFrame = instruction.isJump() && !finalAndExitInstruction(instruction);
+ }
+ }
+ // If there are no frames but we have seen a jump instruction, we cannot verify the stack map.
+ if (requireStackMapFrame && stateMap.isEmpty()) {
+ return reportStackMapError(
+ CfCodeStackMapValidatingException.noFramesForMethodWithJumps(
+ origin, appView.graphLens().getOriginalMethodSignature(method.method), appView),
+ appView);
+ }
+ DexType context = appView.graphLens().lookupType(method.holder());
+ DexType returnType = appView.graphLens().lookupType(method.method.getReturnType());
+ RewrittenPrototypeDescription rewrittenDescription = RewrittenPrototypeDescription.none();
+ if (applyProtoTypeChanges) {
+ rewrittenDescription =
+ appView.graphLens().lookupPrototypeChangesForMethodDefinition(method.method);
+ if (!rewrittenDescription.isEmpty()
+ && rewrittenDescription.getRewrittenReturnInfo() != null) {
+ returnType = rewrittenDescription.getRewrittenReturnInfo().getOldType();
+ }
+ }
+ CfFrameVerificationHelper builder =
+ new CfFrameVerificationHelper(
+ context,
+ stateMap,
+ tryCatchRanges,
+ isAssignablePredicate(appView),
+ appView.dexItemFactory());
+ if (stateMap.containsKey(null)) {
+ assert !shouldComputeInitialFrame();
+ builder.verifyFrameAndSet(stateMap.get(null));
+ } else if (shouldComputeInitialFrame()) {
+ builder.verifyFrameAndSet(
+ new CfFrame(
+ computeInitialLocals(context, method, rewrittenDescription), new ArrayDeque<>()));
+ }
+ for (int i = 0; i < instructions.size(); i++) {
+ CfInstruction instruction = instructions.get(i);
+ try {
+ // Check the exceptional edge prior to evaluating the instruction. The local state is stable
+ // at this point as store operations are not throwing and the current stack does not
+ // affect the exceptional transfer (the exception edge is always a singleton stack).
+ if (canThrowHelper(instruction, appView.options().isGeneratingClassFiles())) {
+ assert !instruction.isStore();
+ builder.verifyExceptionEdges();
+ }
+ instruction.evaluate(
+ builder, context, returnType, appView.dexItemFactory(), appView.initClassLens());
+ } catch (CfCodeStackMapValidatingException ex) {
+ return reportStackMapError(
+ CfCodeStackMapValidatingException.toDiagnostics(
+ origin,
+ appView.graphLens().getOriginalMethodSignature(method.method),
+ i,
+ instruction,
+ ex.getMessage(),
+ appView),
+ appView);
+ }
+ }
+ return true;
+ }
+
+ private boolean reportStackMapError(CfCodeDiagnostics diagnostics, AppView<?> appView) {
+ // Stack maps was required from version V1_6 (50), but the JVM gave a grace-period and only
+ // started enforcing stack maps from 51 in JVM 8. As a consequence, we have different android
+ // libraries that has V1_7 code but has no stack maps. To not fail on compilations we only
+ // report a warning.
+ appView.options().reporter.warning(diagnostics);
+ return false;
+ }
+
+ private boolean finalAndExitInstruction(CfInstruction instruction) {
+ boolean isReturnOrThrow = instruction.isThrow() || instruction.isReturn();
+ if (!isReturnOrThrow) {
+ return false;
+ }
+ for (int i = instructions.size() - 1; i >= 0; i--) {
+ CfInstruction instr = instructions.get(i);
+ if (instr == instruction) {
+ return true;
+ }
+ if (instr.isPosition() || instr.isLabel()) {
+ continue;
+ }
+ return false;
+ }
+ throw new Unreachable("Instruction " + instruction + " should be in instructions");
+ }
+
+ private boolean shouldComputeInitialFrame() {
+ for (CfInstruction instruction : instructions) {
+ if (instruction.isFrame()) {
+ return false;
+ } else if (!instruction.isLabel() && !instruction.isPosition()) {
+ return true;
+ }
+ }
+ // We should never see a method with only labels and positions.
+ assert false;
+ return true;
+ }
+
+ private Int2ReferenceSortedMap<FrameType> computeInitialLocals(
+ DexType context, DexEncodedMethod method, RewrittenPrototypeDescription protoTypeChanges) {
+ int accessFlags =
+ protoTypeChanges.isEmpty()
+ ? method.accessFlags.modifiedFlags
+ : method.accessFlags.originalFlags;
+ Int2ReferenceSortedMap<FrameType> initialLocals = new Int2ReferenceAVLTreeMap<>();
+ int index = 0;
+ if (method.isInstanceInitializer()) {
+ initialLocals.put(index++, FrameType.uninitializedThis());
+ } else if (!MethodAccessFlags.isSet(ACC_STATIC, accessFlags)) {
+ initialLocals.put(index++, FrameType.initialized(context));
+ }
+ ArgumentInfoCollection argumentsInfo = protoTypeChanges.getArgumentInfoCollection();
+ DexType[] parameters = method.method.proto.parameters.values;
+ int originalNumberOfArguments =
+ parameters.length
+ + argumentsInfo.numberOfRemovedArguments()
+ + initialLocals.size()
+ - protoTypeChanges.numberOfExtraParameters();
+ int argumentIndex = index;
+ int usedArgumentIndex = 0;
+ while (argumentIndex < originalNumberOfArguments) {
+ ArgumentInfo argumentInfo = argumentsInfo.getArgumentInfo(argumentIndex++);
+ DexType localType;
+ if (argumentInfo.isRemovedArgumentInfo()) {
+ localType = argumentInfo.asRemovedArgumentInfo().getType();
+ } else {
+ if (argumentInfo.isRewrittenTypeInfo()) {
+ assert parameters[usedArgumentIndex] == argumentInfo.asRewrittenTypeInfo().getNewType();
+ localType = argumentInfo.asRewrittenTypeInfo().getOldType();
+ } else {
+ localType = parameters[usedArgumentIndex];
+ }
+ usedArgumentIndex++;
+ }
+ FrameType frameType = FrameType.initialized(localType);
+ initialLocals.put(index++, frameType);
+ if (localType.isWideType()) {
+ initialLocals.put(index++, frameType);
+ }
+ }
+ return initialLocals;
+ }
+
+ private BiPredicate<DexType, DexType> isAssignablePredicate(AppView<?> appView) {
+ return (source, target) -> isAssignable(source, target, appView);
+ }
+
+ // Rules found at https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.10.1.2
+ private boolean isAssignable(DexType source, DexType target, AppView<?> appView) {
+ DexItemFactory factory = appView.dexItemFactory();
+ source = byteCharShortOrBooleanToInt(source, factory);
+ target = byteCharShortOrBooleanToInt(target, factory);
+ if (source == target) {
+ return true;
+ }
+ if (source.isPrimitiveType() || target.isPrimitiveType()) {
+ return false;
+ }
+ // Both are now references - everything is assignable to object.
+ if (target == factory.objectType) {
+ return true;
+ }
+ // isAssignable(null, class(_, _)).
+ // isAssignable(null, arrayOf(_)).
+ if (source == DexItemFactory.nullValueType) {
+ return true;
+ }
+ if (target.isArrayType() != target.isArrayType()) {
+ return false;
+ }
+ if (target.isArrayType()) {
+ return isAssignable(
+ target.toArrayElementType(factory), target.toArrayElementType(factory), appView);
+ }
+ // TODO(b/166570659): Do a sub-type check that allows for missing classes in hierarchy.
+ return MemberType.fromDexType(source) == MemberType.fromDexType(target);
+ }
+
+ private DexType byteCharShortOrBooleanToInt(DexType type, DexItemFactory factory) {
+ // byte, char, short and boolean has verification type int.
+ if (type.isByteType() || type.isCharType() || type.isShortType() || type.isBooleanType()) {
+ return factory.intType;
+ }
+ return type;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/CfCodeDiagnostics.java b/src/main/java/com/android/tools/r8/graph/CfCodeDiagnostics.java
new file mode 100644
index 0000000..764a812
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/CfCodeDiagnostics.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.MethodPosition;
+import com.android.tools.r8.position.Position;
+
+public class CfCodeDiagnostics implements Diagnostic {
+
+ @Override
+ public Origin getOrigin() {
+ return origin;
+ }
+
+ @Override
+ public Position getPosition() {
+ return methodPosition;
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ return diagnosticMessage;
+ }
+
+ private final Origin origin;
+ private final MethodPosition methodPosition;
+ private final String diagnosticMessage;
+
+ CfCodeDiagnostics(Origin origin, DexMethod method, String diagnosticMessage) {
+ this.origin = origin;
+ this.methodPosition = new MethodPosition(method.asMethodReference());
+ this.diagnosticMessage = diagnosticMessage;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/CfCodeStackMapValidatingException.java b/src/main/java/com/android/tools/r8/graph/CfCodeStackMapValidatingException.java
new file mode 100644
index 0000000..6c54a1a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/CfCodeStackMapValidatingException.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.graph;
+
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.origin.Origin;
+
+public class CfCodeStackMapValidatingException extends RuntimeException {
+
+ private CfCodeStackMapValidatingException(String message) {
+ super(message);
+ }
+
+ public static CfCodeStackMapValidatingException error(String messsage) {
+ return new CfCodeStackMapValidatingException(messsage);
+ }
+
+ public static CfCodeDiagnostics unexpectedStackMapFrame(
+ Origin origin, DexMethod method, AppView<?> appView) {
+ StringBuilder sb = new StringBuilder("Unexpected stack map frame without target");
+ if (appView.enableWholeProgramOptimizations()) {
+ sb.append(" In later version of R8, the method may be assumed not reachable.");
+ }
+ return new CfCodeDiagnostics(origin, method, sb.toString());
+ }
+
+ public static CfCodeDiagnostics multipleFramesForLabel(
+ Origin origin, DexMethod method, AppView<?> appView) {
+ StringBuilder sb = new StringBuilder("Multiple frames for label");
+ if (appView.enableWholeProgramOptimizations()) {
+ sb.append(" In later version of R8, the method may be assumed not reachable.");
+ }
+ return new CfCodeDiagnostics(origin, method, sb.toString());
+ }
+
+ public static CfCodeDiagnostics noFramesForMethodWithJumps(
+ Origin origin, DexMethod method, AppView<?> appView) {
+ StringBuilder sb =
+ new StringBuilder("Expected stack map table for method with non-linear control flow.");
+ if (appView.enableWholeProgramOptimizations()) {
+ sb.append(" In later version of R8, the method may be assumed not reachable.");
+ }
+ return new CfCodeDiagnostics(origin, method, sb.toString());
+ }
+
+ public static CfCodeDiagnostics toDiagnostics(
+ Origin origin,
+ DexMethod method,
+ int instructionIndex,
+ CfInstruction instruction,
+ String detailMessage,
+ AppView<?> appView) {
+ StringBuilder sb =
+ new StringBuilder("Invalid stack map table at ")
+ .append(instructionIndex)
+ .append(": ")
+ .append(instruction)
+ .append(", error: ")
+ .append(detailMessage)
+ .append(".");
+ if (appView.enableWholeProgramOptimizations()) {
+ sb.append(" In later version of R8, the method may be assumed not reachable.");
+ }
+ return new CfCodeDiagnostics(origin, method, sb.toString());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/CfCompareHelper.java b/src/main/java/com/android/tools/r8/graph/CfCompareHelper.java
new file mode 100644
index 0000000..b05814e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/CfCompareHelper.java
@@ -0,0 +1,76 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.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 it.unimi.dsi.fastutil.objects.Reference2IntMap;
+import java.util.Comparator;
+import java.util.List;
+import org.objectweb.asm.Opcodes;
+
+public class CfCompareHelper {
+
+ // Integer constants to ensure that there is a well order for all CF instructions including
+ // virtual instructions represented in our internal encoding.
+ public static final int CONST_CLASS_COMPARE_ID;
+ public static final int CONST_STRING_COMPARE_ID;
+ public static final int CONST_STRING_DEX_ITEM_COMPARE_ID;
+ public static final int CONST_NUMBER_COMPARE_ID;
+ public static final int CONST_METHOD_TYPE_COMPARE_ID;
+ public static final int CONST_METHOD_HANDLE_COMPARE_ID;
+ public static final int FRAME_COMPARE_ID;
+ public static final int INIT_CLASS_COMPARE_ID;
+ public static final int LABEL_COMPARE_ID;
+ public static final int POSITION_COMPARE_ID;
+
+ static {
+ int lastId = Opcodes.IFNONNULL;
+ CONST_CLASS_COMPARE_ID = ++lastId;
+ CONST_STRING_COMPARE_ID = ++lastId;
+ CONST_STRING_DEX_ITEM_COMPARE_ID = ++lastId;
+ CONST_NUMBER_COMPARE_ID = ++lastId;
+ CONST_METHOD_TYPE_COMPARE_ID = ++lastId;
+ CONST_METHOD_HANDLE_COMPARE_ID = ++lastId;
+ FRAME_COMPARE_ID = ++lastId;
+ INIT_CLASS_COMPARE_ID = ++lastId;
+ LABEL_COMPARE_ID = ++lastId;
+ POSITION_COMPARE_ID = ++lastId;
+ }
+
+ // Helper to signal that the concrete instruction is uniquely determined by its ID/opcode.
+ public static int 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;
+ }
+
+ public int compareLabels(CfLabel label1, CfLabel label2) {
+ return labels1.getInt(label1) - labels2.getInt(label2);
+ }
+
+ public Comparator<List<CfInstruction>> instructionComparator() {
+ return ComparatorUtils.listComparator((x, y) -> x.compareTo(y, this));
+ }
+
+ public Comparator<List<CfTryCatch>> tryCatchRangesComparator() {
+ return ComparatorUtils.listComparator((x, y) -> x.compareTo(y, this));
+ }
+
+ public Comparator<List<CfCode.LocalVariableInfo>> localVariablesComparator() {
+ return ComparatorUtils.listComparator((x, y) -> x.compareTo(y, this));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java b/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
index 4dcc108..36f43ba 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
@@ -64,8 +64,7 @@
}
public static ClassAccessFlags fromDexAccessFlags(int access) {
- // Assume that the SUPER flag should be set (behaviour for Java versions > 1.1).
- return new ClassAccessFlags((access & DEX_FLAGS) | Constants.ACC_SUPER);
+ return new ClassAccessFlags(access & DEX_FLAGS);
}
public static ClassAccessFlags fromCfAccessFlags(int access) {
@@ -84,6 +83,10 @@
@Override
public int getAsCfAccessFlags() {
+ assert !isInterface() || isAbstract();
+ assert !isInterface() || !isSuper();
+ assert !isInterface() || !isFinal();
+ assert !isInterface() || !isEnum();
return materialize();
}
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 0761cac..3bb9931 100644
--- a/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java
@@ -9,8 +9,9 @@
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 {
+public class DebugLocalInfo implements Comparable<DebugLocalInfo> {
public enum PrintLevel {
NONE,
@@ -30,6 +31,14 @@
this.signature = signature;
}
+ @Override
+ public int compareTo(DebugLocalInfo other) {
+ return Comparator.comparing((DebugLocalInfo info) -> info.name, DexString::slowCompareTo)
+ .thenComparing(info -> info.type, DexType::slowCompareTo)
+ .thenComparing(info -> info.signature, Comparator.nullsFirst(DexString::slowCompareTo))
+ .compare(this, other);
+ }
+
public static boolean localsInfoMapsEqual(
Int2ReferenceMap<DebugLocalInfo> set0, Int2ReferenceMap<DebugLocalInfo> set1) {
if (set0 == null) {
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 e3bf2d8..cde4638 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -562,7 +562,11 @@
}
public DexType getType() {
- return this.type;
+ return type;
+ }
+
+ public DexType getSuperType() {
+ return superType;
}
public boolean hasClassInitializer() {
@@ -817,6 +821,11 @@
return null;
}
+ public void forEachNestMember(Consumer<DexType> consumer) {
+ assert isNestHost();
+ getNestMembersClassAttributes().forEach(member -> consumer.accept(member.getNestMember()));
+ }
+
public NestHostClassAttribute getNestHostClassAttribute() {
return nestHost;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndField.java b/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
index 7b11467..288b336 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
@@ -4,21 +4,11 @@
package com.android.tools.r8.graph;
-import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.origin.Origin;
-
-public class DexClassAndField {
-
- private final DexClass holder;
- private final DexEncodedField field;
+public class DexClassAndField extends DexClassAndMember<DexEncodedField, DexField> {
DexClassAndField(DexClass holder, DexEncodedField field) {
- assert holder != null;
- assert field != null;
- assert holder.type == field.holder();
+ super(holder, field);
assert holder.isProgramClass() == (this instanceof ProgramField);
- this.holder = holder;
- this.field = field;
}
public static DexClassAndField create(DexClass holder, DexEncodedField field) {
@@ -29,26 +19,6 @@
}
}
- public DexClass getHolder() {
- return holder;
- }
-
- public DexType getHolderType() {
- return holder.type;
- }
-
- public DexEncodedField getDefinition() {
- return field;
- }
-
- public DexField getReference() {
- return field.field;
- }
-
- public Origin getOrigin() {
- return holder.origin;
- }
-
public boolean isProgramField() {
return false;
}
@@ -56,23 +26,4 @@
public ProgramField asProgramField() {
return null;
}
-
- public String toSourceString() {
- return getReference().toSourceString();
- }
-
- @Override
- public String toString() {
- return toSourceString();
- }
-
- @Override
- public boolean equals(Object object) {
- throw new Unreachable("Unsupported attempt at comparing Class and DexClassAndField");
- }
-
- @Override
- public int hashCode() {
- throw new Unreachable("Unsupported attempt at computing the hashcode of DexClassAndField");
- }
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java
new file mode 100644
index 0000000..0bc5348
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java
@@ -0,0 +1,66 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.origin.Origin;
+
+public abstract class DexClassAndMember<
+ D extends DexEncodedMember<D, R>, R extends DexMember<D, R>> {
+
+ private final DexClass holder;
+ private final D definition;
+
+ public DexClassAndMember(DexClass holder, D definition) {
+ assert holder != null;
+ assert definition != null;
+ assert holder.type == definition.holder();
+ this.holder = holder;
+ this.definition = definition;
+ }
+
+ public DexType getContextType() {
+ return getHolderType();
+ }
+
+ public DexClass getHolder() {
+ return holder;
+ }
+
+ public DexType getHolderType() {
+ return holder.type;
+ }
+
+ public D getDefinition() {
+ return definition;
+ }
+
+ public R getReference() {
+ return definition.toReference();
+ }
+
+ public Origin getOrigin() {
+ return holder.origin;
+ }
+
+ public String toSourceString() {
+ return getReference().toSourceString();
+ }
+
+ @Override
+ public String toString() {
+ return toSourceString();
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ throw new Unreachable("Unsupported attempt at comparing Class and DexClassAndMember");
+ }
+
+ @Override
+ public int hashCode() {
+ throw new Unreachable("Unsupported attempt at computing the hash code of DexClassAndMember");
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
index c6c2f07..87d1bb9 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
@@ -4,21 +4,12 @@
package com.android.tools.r8.graph;
-import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.origin.Origin;
-
-public class DexClassAndMethod implements LookupTarget {
-
- private final DexClass holder;
- private final DexEncodedMethod method;
+public class DexClassAndMethod extends DexClassAndMember<DexEncodedMethod, DexMethod>
+ implements LookupTarget {
DexClassAndMethod(DexClass holder, DexEncodedMethod method) {
- assert holder != null;
- assert method != null;
- assert holder.type == method.holder();
+ super(holder, method);
assert holder.isProgramClass() == (this instanceof ProgramMethod);
- this.holder = holder;
- this.method = method;
}
public static DexClassAndMethod create(DexClass holder, DexEncodedMethod method) {
@@ -33,16 +24,6 @@
}
@Override
- public boolean equals(Object object) {
- throw new Unreachable("Unsupported attempt at comparing Class and DexClassAndMethod");
- }
-
- @Override
- public int hashCode() {
- throw new Unreachable("Unsupported attempt at computing the hashcode of DexClassAndMethod");
- }
-
- @Override
public boolean isMethodTarget() {
return true;
}
@@ -52,26 +33,6 @@
return this;
}
- public DexClass getHolder() {
- return holder;
- }
-
- public DexType getHolderType() {
- return holder.type;
- }
-
- public DexEncodedMethod getDefinition() {
- return method;
- }
-
- public DexMethod getReference() {
- return method.method;
- }
-
- public Origin getOrigin() {
- return holder.origin;
- }
-
public boolean isClasspathMethod() {
return false;
}
@@ -87,13 +48,4 @@
public ProgramMethod asProgramMethod() {
return null;
}
-
- public String toSourceString() {
- return method.method.toSourceString();
- }
-
- @Override
- public String toString() {
- return toSourceString();
- }
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index b8a1e8f..ce47f1e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import static com.android.tools.r8.utils.ComparatorUtils.arrayComparator;
+
import com.android.tools.r8.code.Instruction;
import com.android.tools.r8.code.ReturnVoid;
import com.android.tools.r8.code.SwitchPayload;
@@ -20,11 +22,13 @@
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.ComparatorUtils;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.base.Strings;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import java.util.Arrays;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -32,7 +36,7 @@
import java.util.Set;
// DexCode corresponds to code item in dalvik/dex-format.html
-public class DexCode extends Code {
+public class DexCode extends Code implements Comparable<DexCode> {
static final String FAKE_THIS_PREFIX = "_";
static final String FAKE_THIS_SUFFIX = "this";
@@ -63,6 +67,9 @@
this.tries = tries;
this.handlers = handlers;
this.debugInfo = debugInfo;
+ assert tries != null;
+ assert handlers != null;
+ assert instructions != null;
hashCode(); // Cache the hash code eagerly.
}
@@ -159,11 +166,6 @@
return new DexDebugInfo(debugInfo.startLine, newParameters, debugInfo.events);
}
- public int codeSizeInBytes() {
- Instruction last = instructions[instructions.length - 1];
- return last.getOffset() + last.getSize();
- }
-
@Override
public int computeHashCode() {
return incomingRegisterSize * 2
@@ -176,37 +178,26 @@
}
@Override
- public boolean computeEquals(Object other) {
- if (other instanceof DexCode) {
- DexCode o = (DexCode) other;
- if (incomingRegisterSize != o.incomingRegisterSize) {
- return false;
- }
- if (registerSize != o.registerSize) {
- return false;
- }
- if (outgoingRegisterSize != o.outgoingRegisterSize) {
- return false;
- }
- if (debugInfo == null) {
- if (o.debugInfo != null) {
- return false;
- }
- } else {
- if (!debugInfo.equals(o.debugInfo)) {
- return false;
- }
- }
- if (!Arrays.equals(tries, o.tries)) {
- return false;
- }
- if (!Arrays.equals(handlers, o.handlers)) {
- return false;
- }
- // Save the most expensive operation to last.
- return Arrays.equals(instructions, o.instructions);
+ public int compareTo(DexCode other) {
+ if (this == other) {
+ return 0;
}
- return false;
+ int diff =
+ Comparator.comparingInt((DexCode c) -> c.incomingRegisterSize)
+ .thenComparingInt(c -> c.registerSize)
+ .thenComparingInt(c -> c.outgoingRegisterSize)
+ .thenComparing(c -> c.tries, arrayComparator())
+ .thenComparing(c -> c.handlers, arrayComparator())
+ .thenComparing(c -> c.debugInfo, Comparator.nullsFirst(DexDebugInfo::compareTo))
+ .thenComparing((DexCode c) -> c.instructions, arrayComparator())
+ .compare(this, other);
+ assert (diff == 0) == (0 == toString().compareTo(other.toString()));
+ return diff;
+ }
+
+ @Override
+ public boolean computeEquals(Object other) {
+ return other instanceof DexCode && compareTo((DexCode) other) == 0;
}
@Override
@@ -259,11 +250,9 @@
for (Instruction insn : instructions) {
insn.registerUse(registry);
}
- if (handlers != null) {
- for (TryHandler handler : handlers) {
- for (TypeAddrPair pair : handler.pairs) {
- registry.registerTypeReference(pair.type);
- }
+ for (TryHandler handler : handlers) {
+ for (TypeAddrPair pair : handler.pairs) {
+ registry.registerTypeReference(pair.type);
}
}
}
@@ -330,14 +319,12 @@
builder.append(atry.toString());
builder.append('\n');
}
- if (handlers != null) {
- builder.append("Handlers (numbers are offsets)\n");
- for (int handlerIndex = 0; handlerIndex < handlers.length; handlerIndex++) {
- TryHandler handler = handlers[handlerIndex];
- builder.append(" ").append(handlerIndex).append(": ");
- builder.append(handler.toString());
- builder.append('\n');
- }
+ builder.append("Handlers (numbers are offsets)\n");
+ for (int handlerIndex = 0; handlerIndex < handlers.length; handlerIndex++) {
+ TryHandler handler = handlers[handlerIndex];
+ builder.append(" ").append(handlerIndex).append(": ");
+ builder.append(handler.toString());
+ builder.append('\n');
}
}
return builder.toString();
@@ -393,13 +380,11 @@
builder.append(atry.toString());
builder.append('\n');
}
- if (handlers != null) {
builder.append("Handlers (numbers are offsets)\n");
for (TryHandler handler : handlers) {
builder.append(handler.toString());
builder.append('\n');
}
- }
}
return builder.toString();
}
@@ -422,11 +407,9 @@
if (debugInfo != null) {
getDebugInfoForWriting().collectIndexedItems(indexedItems, graphLens);
}
- if (handlers != null) {
for (TryHandler handler : handlers) {
handler.collectIndexedItems(indexedItems, graphLens);
}
- }
}
public DexDebugInfoForWriting getDebugInfoForWriting() {
@@ -460,7 +443,12 @@
}
}
- public static class Try extends DexItem {
+ public int codeSizeInBytes() {
+ Instruction last = instructions[instructions.length - 1];
+ return last.getOffset() + last.getSize();
+ }
+
+ public static class Try extends DexItem implements Comparable<Try> {
public static final int NO_INDEX = -1;
@@ -487,20 +475,18 @@
@Override
public boolean equals(Object other) {
+ return other instanceof Try && compareTo((Try) other) == 0;
+ }
+
+ @Override
+ public int compareTo(Try other) {
if (this == other) {
- return true;
+ return 0;
}
- if (other instanceof Try) {
- Try o = (Try) other;
- if (startAddress != o.startAddress) {
- return false;
- }
- if (instructionCount != o.instructionCount) {
- return false;
- }
- return handlerIndex == o.handlerIndex;
- }
- return false;
+ return ComparatorUtils.compareInts(
+ startAddress, other.startAddress,
+ instructionCount, other.instructionCount,
+ handlerIndex, other.handlerIndex);
}
@Override
@@ -521,7 +507,7 @@
}
- public static class TryHandler extends DexItem {
+ public static class TryHandler extends DexItem implements Comparable<TryHandler> {
public static final int NO_HANDLER = -1;
@@ -540,17 +526,17 @@
@Override
public boolean equals(Object other) {
+ return other instanceof TryHandler && compareTo((TryHandler) other) == 0;
+ }
+
+ @Override
+ public int compareTo(TryHandler other) {
if (this == other) {
- return true;
+ return 0;
}
- if (other instanceof TryHandler) {
- TryHandler o = (TryHandler) other;
- if (catchAllAddr != o.catchAllAddr) {
- return false;
- }
- return Arrays.equals(pairs, o.pairs);
- }
- return false;
+ return Comparator.comparingInt((TryHandler h) -> h.catchAllAddr)
+ .thenComparing(h -> h.pairs, arrayComparator())
+ .compare(this, other);
}
public void collectIndexedItems(IndexedItemCollection indexedItems, GraphLens graphLens) {
@@ -585,7 +571,7 @@
return builder.toString();
}
- public static class TypeAddrPair extends DexItem {
+ public static class TypeAddrPair extends DexItem implements Comparable<TypeAddrPair> {
public final DexType type;
public final /* offset */ int addr;
@@ -613,14 +599,17 @@
@Override
public boolean equals(Object other) {
+ return other instanceof TypeAddrPair && compareTo((TypeAddrPair) other) == 0;
+ }
+
+ @Override
+ public int compareTo(TypeAddrPair other) {
if (this == other) {
- return true;
+ return 0;
}
- if (other instanceof TypeAddrPair) {
- TypeAddrPair o = (TypeAddrPair) other;
- return type.equals(o.type) && addr == o.addr;
- }
- return false;
+ return Comparator.comparingInt((TypeAddrPair p) -> p.addr)
+ .thenComparing(p -> p.type, DexType::slowCompareTo)
+ .compare(this, other);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
index 0e59082..903686d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
@@ -8,9 +8,14 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.ir.code.Position;
+import java.util.Comparator;
import java.util.Objects;
-abstract public class DexDebugEvent extends DexItem {
+public abstract class DexDebugEvent extends DexItem implements Comparable<DexDebugEvent> {
+
+ // Compare ID(s) for virtual debug events.
+ private static final int DBG_SET_INLINE_FRAME_COMPARE_ID = Constants.DBG_LAST_SPECIAL + 1;
+
public static final DexDebugEvent[] EMPTY_ARRAY = {};
public void collectIndexedItems(IndexedItemCollection collection, GraphLens graphLens) {
@@ -30,7 +35,22 @@
abstract public int hashCode();
@Override
- abstract public boolean equals(Object other);
+ public final boolean equals(Object other) {
+ return other instanceof DexDebugEvent && compareTo((DexDebugEvent) other) == 0;
+ }
+
+ abstract int getCompareToId();
+
+ abstract int internalCompareTo(DexDebugEvent other);
+
+ @Override
+ public final int compareTo(DexDebugEvent other) {
+ if (this == other) {
+ return 0;
+ }
+ int diff = Integer.compare(getCompareToId(), other.getCompareToId());
+ return diff != 0 ? diff : internalCompareTo(other);
+ }
public abstract void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping);
@@ -77,9 +97,13 @@
}
@Override
- public boolean equals(Object other) {
- return (other instanceof AdvancePC)
- && (delta == ((AdvancePC) other).delta);
+ int getCompareToId() {
+ return Constants.DBG_ADVANCE_PC;
+ }
+
+ @Override
+ int internalCompareTo(DexDebugEvent other) {
+ return Integer.compare(delta, ((AdvancePC) other).delta);
}
}
@@ -110,8 +134,14 @@
}
@Override
- public boolean equals(Object other) {
- return other instanceof SetPrologueEnd;
+ int getCompareToId() {
+ return Constants.DBG_SET_PROLOGUE_END;
+ }
+
+ @Override
+ int internalCompareTo(DexDebugEvent other) {
+ assert other instanceof SetPrologueEnd;
+ return 0;
}
}
@@ -142,8 +172,14 @@
}
@Override
- public boolean equals(Object other) {
- return other instanceof SetEpilogueBegin;
+ int getCompareToId() {
+ return Constants.DBG_SET_EPILOGUE_BEGIN;
+ }
+
+ @Override
+ int internalCompareTo(DexDebugEvent other) {
+ assert other instanceof SetEpilogueBegin;
+ return 0;
}
}
@@ -178,9 +214,13 @@
}
@Override
- public boolean equals(Object other) {
- return (other instanceof AdvanceLine)
- && (delta == ((AdvanceLine) other).delta);
+ int getCompareToId() {
+ return Constants.DBG_ADVANCE_LINE;
+ }
+
+ @Override
+ int internalCompareTo(DexDebugEvent other) {
+ return Integer.compare(delta, ((AdvanceLine) other).delta);
}
}
@@ -253,21 +293,17 @@
}
@Override
- public boolean equals(Object other) {
- if (!(other instanceof StartLocal)) {
- return false;
- }
- StartLocal o = (StartLocal) other;
- if (registerNum != o.registerNum) {
- return false;
- }
- if (!Objects.equals(name, o.name)) {
- return false;
- }
- if (!Objects.equals(type, o.type)) {
- return false;
- }
- return Objects.equals(signature, o.signature);
+ int getCompareToId() {
+ return Constants.DBG_START_LOCAL;
+ }
+
+ @Override
+ int internalCompareTo(DexDebugEvent other) {
+ return Comparator.comparingInt((StartLocal e) -> e.registerNum)
+ .thenComparing(e -> e.name, DexString::slowCompareTo)
+ .thenComparing(e -> e.type, DexType::slowCompareTo)
+ .thenComparing(e -> e.signature, Comparator.nullsFirst(DexString::slowCompareTo))
+ .compare(this, (StartLocal) other);
}
}
@@ -302,9 +338,13 @@
}
@Override
- public boolean equals(Object other) {
- return (other instanceof EndLocal)
- && (registerNum == ((EndLocal) other).registerNum);
+ int getCompareToId() {
+ return Constants.DBG_END_LOCAL;
+ }
+
+ @Override
+ int internalCompareTo(DexDebugEvent other) {
+ return Integer.compare(registerNum, ((EndLocal) other).registerNum);
}
}
@@ -339,9 +379,13 @@
}
@Override
- public boolean equals(Object other) {
- return (other instanceof RestartLocal)
- && (registerNum == ((RestartLocal) other).registerNum);
+ int getCompareToId() {
+ return Constants.DBG_RESTART_LOCAL;
+ }
+
+ @Override
+ int internalCompareTo(DexDebugEvent other) {
+ return Integer.compare(registerNum, ((RestartLocal) other).registerNum);
}
}
@@ -381,9 +425,13 @@
}
@Override
- public boolean equals(Object other) {
- return (other instanceof SetFile)
- && fileName.equals(((SetFile) other).fileName);
+ int getCompareToId() {
+ return Constants.DBG_SET_FILE;
+ }
+
+ @Override
+ int internalCompareTo(DexDebugEvent other) {
+ return fileName.slowCompareTo(((SetFile) other).fileName);
}
}
@@ -419,12 +467,15 @@
}
@Override
- public boolean equals(Object other) {
- if (!(other instanceof SetInlineFrame)) {
- return false;
- }
- SetInlineFrame o = (SetInlineFrame) other;
- return callee == o.callee && Objects.equals(caller, o.caller);
+ int getCompareToId() {
+ return DBG_SET_INLINE_FRAME_COMPARE_ID;
+ }
+
+ @Override
+ int internalCompareTo(DexDebugEvent other) {
+ return Comparator.comparing((SetInlineFrame e) -> e.callee, DexMethod::slowCompareTo)
+ .thenComparing(e -> e.caller, Comparator.nullsFirst(Position::compareTo))
+ .compare(this, (SetInlineFrame) other);
}
@Override
@@ -484,9 +535,13 @@
}
@Override
- public boolean equals(Object other) {
- return (other instanceof Default)
- && (value == ((Default) other).value);
+ int getCompareToId() {
+ return Constants.DBG_FIRST_SPECIAL;
+ }
+
+ @Override
+ int internalCompareTo(DexDebugEvent other) {
+ return Integer.compare(value, ((Default) other).value);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
index ed4f2c5..5e44a57 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
@@ -5,10 +5,12 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.utils.ComparatorUtils;
import java.util.Arrays;
+import java.util.Comparator;
import java.util.List;
-public class DexDebugInfo extends CachedHashValueDexItem {
+public class DexDebugInfo extends CachedHashValueDexItem implements Comparable<DexDebugInfo> {
public final int startLine;
public final DexString[] parameters;
@@ -40,18 +42,21 @@
}
@Override
- public boolean computeEquals(Object other) {
- if (other instanceof DexDebugInfo) {
- DexDebugInfo o = (DexDebugInfo) other;
- if (startLine != o.startLine) {
- return false;
- }
- if (!Arrays.equals(parameters, o.parameters)) {
- return false;
- }
- return Arrays.equals(events, o.events);
+ public final boolean computeEquals(Object other) {
+ return other instanceof DexDebugInfo && compareTo((DexDebugInfo) other) == 0;
+ }
+
+ @Override
+ public final int compareTo(DexDebugInfo other) {
+ if (this == other) {
+ return 0;
}
- return false;
+ return Comparator.comparingInt((DexDebugInfo i) -> i.startLine)
+ .thenComparing(
+ i -> i.parameters,
+ ComparatorUtils.arrayComparator(Comparator.nullsFirst(DexString::slowCompareTo)))
+ .thenComparing(i -> i.events, ComparatorUtils.arrayComparator())
+ .compare(this, other);
}
public void collectIndexedItems(IndexedItemCollection indexedItems, GraphLens graphLens) {
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 e480799..cb4eb4e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.utils.ArrayUtils;
import java.util.Arrays;
+import java.util.function.Consumer;
import java.util.function.Function;
public class DexEncodedAnnotation extends DexItem {
@@ -30,6 +31,12 @@
}
}
+ public void forEachElement(Consumer<DexAnnotationElement> consumer) {
+ for (DexAnnotationElement element : elements) {
+ consumer.accept(element);
+ }
+ }
+
public DexAnnotationElement getElement(int i) {
return elements[i];
}
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 c40383d..4936ceb 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -298,11 +298,21 @@
public int syntheticCompareTo(DexEncodedMethod other) {
assert annotations().isEmpty();
assert parameterAnnotationsList.isEmpty();
- return Comparator.comparing(DexEncodedMethod::proto, DexProto::slowCompareTo)
- .thenComparingInt(m -> m.accessFlags.getAsCfAccessFlags())
- // TODO(b/158159959): Implement structural compareTo on code.
- .thenComparing(m -> m.getCode().toString())
- .compare(this, other);
+ Comparator<DexEncodedMethod> comparator =
+ Comparator.comparing(DexEncodedMethod::proto, DexProto::slowCompareTo)
+ .thenComparingInt(m -> m.accessFlags.getAsCfAccessFlags());
+ if (code.isCfCode() && other.getCode().isCfCode()) {
+ comparator = comparator.thenComparing(m -> m.getCode().asCfCode());
+ } else if (code.isDexCode() && other.getCode().isDexCode()) {
+ comparator = comparator.thenComparing(m -> m.getCode().asDexCode());
+ } else {
+ throw new Unreachable(
+ "Unexpected attempt to compare incompatible synthetic objects: "
+ + code
+ + " and "
+ + other.getCode());
+ }
+ return comparator.compare(this, other);
}
public DexType getHolderType() {
@@ -765,6 +775,10 @@
return builder.toString();
}
+ public ParameterAnnotationsList getParameterAnnotations() {
+ return parameterAnnotationsList;
+ }
+
public void clearParameterAnnotations() {
parameterAnnotationsList = ParameterAnnotationsList.empty();
}
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 a58ec39..fcb79fe 100644
--- a/src/main/java/com/android/tools/r8/graph/DexField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexField.java
@@ -23,6 +23,14 @@
}
}
+ public DexType getHolderType() {
+ return holder;
+ }
+
+ public DexType getType() {
+ return type;
+ }
+
@Override
public DexEncodedField lookupOnClass(DexClass clazz) {
return clazz != null ? clazz.lookupField(this) : null;
@@ -149,4 +157,8 @@
public String toSourceString() {
return type.toSourceString() + " " + holder.toSourceString() + "." + name.toSourceString();
}
+
+ public DexField withHolder(DexType holder, DexItemFactory dexItemFactory) {
+ return dexItemFactory.createField(holder, type, name);
+ }
}
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 26f114f..b3e11f5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -29,6 +29,10 @@
}
}
+ public DexType getHolderType() {
+ return holder;
+ }
+
public DexString getName() {
return name;
}
@@ -71,6 +75,10 @@
return clazz != null ? clazz.lookupMember(this) : null;
}
+ public ProgramMethod lookupOnProgramClass(DexProgramClass clazz) {
+ return clazz != null ? clazz.lookupProgramMethod(this) : null;
+ }
+
@Override
public String toString() {
return "Method " + holder + "." + name + " " + proto.toString();
@@ -232,7 +240,11 @@
&& proto == dexItemFactory.deserializeLambdaMethodProto;
}
- public boolean isInstanceInitializer(DexDefinitionSupplier definitions) {
- return definitions.dexItemFactory().isConstructor(this);
+ public boolean isInstanceInitializer(DexItemFactory factory) {
+ return factory.isConstructor(this);
+ }
+
+ public DexMethod withHolder(DexType holder, DexItemFactory dexItemFactory) {
+ return dexItemFactory.createMethod(holder, proto, name);
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index f0efe20..ca7a264 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -29,7 +29,8 @@
import java.util.function.Predicate;
import java.util.function.Supplier;
-public class DexProgramClass extends DexClass implements Supplier<DexProgramClass> {
+public class DexProgramClass extends DexClass
+ implements ProgramDefinition, Supplier<DexProgramClass> {
@FunctionalInterface
public interface ChecksumSupplier {
@@ -491,6 +492,16 @@
return this;
}
+ @Override
+ public DexType getContextType() {
+ return getType();
+ }
+
+ @Override
+ public DexProgramClass getDefinition() {
+ return this;
+ }
+
public void setInitialClassFileVersion(int initialClassFileVersion) {
assert this.initialClassFileVersion == -1 && initialClassFileVersion > 0;
this.initialClassFileVersion = initialClassFileVersion;
diff --git a/src/main/java/com/android/tools/r8/graph/DexProto.java b/src/main/java/com/android/tools/r8/graph/DexProto.java
index 09985c3..a0cacd9 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProto.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProto.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.naming.NamingLens;
import com.google.common.hash.Hasher;
+import java.util.function.Consumer;
public class DexProto extends IndexedDexItem implements PresortedComparable<DexProto> {
@@ -21,6 +22,11 @@
this.parameters = parameters;
}
+ public void forEachType(Consumer<DexType> consumer) {
+ consumer.accept(returnType);
+ parameters.forEach(consumer);
+ }
+
public DexType getParameter(int index) {
return parameters.values[index];
}
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 b296b43..c60efe5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexReference.java
+++ b/src/main/java/com/android/tools/r8/graph/DexReference.java
@@ -8,9 +8,7 @@
import java.util.function.Consumer;
import java.util.function.Function;
-/**
- * A common interface for {@link DexType}, {@link DexField}, and {@link DexMethod}.
- */
+/** A common interface for {@link DexType}, {@link DexField}, and {@link DexMethod}. */
public abstract class DexReference extends IndexedDexItem {
public abstract <T> T apply(
@@ -62,4 +60,30 @@
public DexMethod asDexMethod() {
return null;
}
+
+ private int referenceTypeOrder() {
+ if (isDexType()) {
+ return 1;
+ }
+ if (isDexField()) {
+ return 2;
+ }
+ assert isDexMethod();
+ return 3;
+ }
+
+ public int referenceCompareTo(DexReference o) {
+ int typeDiff = referenceTypeOrder() - o.referenceTypeOrder();
+ if (typeDiff != 0) {
+ return typeDiff;
+ }
+ if (isDexType()) {
+ return asDexType().slowCompareTo(o.asDexType());
+ }
+ if (isDexField()) {
+ return asDexField().slowCompareTo(o.asDexField());
+ }
+ assert isDexMethod();
+ return asDexMethod().slowCompareTo(o.asDexMethod());
+ }
}
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 bc7e26f..500da1e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.horizontalclassmerging.SyntheticArgumentClass;
import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter;
import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring;
@@ -251,6 +252,12 @@
return descriptor.content[0] == 'D';
}
+ public boolean isNullValueType() {
+ boolean isNullValueType = descriptor.content[0] == 'N';
+ assert !isNullValueType || this == DexItemFactory.nullValueType;
+ return isNullValueType;
+ }
+
public boolean isArrayType() {
char firstChar = (char) descriptor.content[0];
return firstChar == '[';
@@ -313,6 +320,7 @@
// Any entry that is removed from here must be added to OLD_SYNTHESIZED_NAMES to ensure that
// newer releases can be used to merge previous builds.
return name.contains(ENUM_UNBOXING_UTILITY_CLASS_SUFFIX) // Shared among enums.
+ || name.contains(SyntheticArgumentClass.SYNTHETIC_CLASS_SUFFIX)
|| name.contains(LAMBDA_CLASS_NAME_PREFIX) // Could collide.
|| name.contains(LAMBDA_GROUP_CLASS_NAME_PREFIX) // Could collide.
|| name.contains(DISPATCH_CLASS_NAME_SUFFIX) // Shared on reference.
@@ -407,13 +415,19 @@
public DexType replacePackage(String newPackageDescriptor, DexItemFactory dexItemFactory) {
assert isClassType();
String descriptorString = toDescriptorString();
- int lastPackageSeparator = descriptorString.lastIndexOf('/');
- String newDescriptorString = "L" + newPackageDescriptor + "/";
- if (lastPackageSeparator >= 0) {
- newDescriptorString += descriptorString.substring(lastPackageSeparator + 1);
- } else {
- newDescriptorString += descriptorString.substring(1);
+ String newDescriptorString = "L";
+ if (!newPackageDescriptor.isEmpty()) {
+ newDescriptorString += newPackageDescriptor + "/";
}
+ newDescriptorString += DescriptorUtils.getSimpleClassNameFromDescriptor(descriptorString) + ";";
+ return dexItemFactory.createType(newDescriptorString);
+ }
+
+ public DexType addSuffix(String suffix, DexItemFactory dexItemFactory) {
+ assert isClassType();
+ String descriptorString = toDescriptorString();
+ int endIndex = descriptorString.length() - 1;
+ String newDescriptorString = descriptorString.substring(0, endIndex) + suffix + ";";
return dexItemFactory.createType(newDescriptorString);
}
@@ -426,7 +440,7 @@
}
public DexType toArrayElementType(DexItemFactory dexItemFactory) {
- assert this.isArrayType();
+ assert isArrayType();
DexString newDesc =
dexItemFactory.createString(
descriptor.size - 1,
@@ -458,6 +472,17 @@
return getPackageOrName(false);
}
+ public String getSimpleName() {
+ assert isClassType();
+ return DescriptorUtils.getSimpleClassNameFromDescriptor(toDescriptorString());
+ }
+
+ public DexType withSimpleName(String newSimpleName, DexItemFactory dexItemFactory) {
+ assert isClassType();
+ return dexItemFactory.createType(
+ DescriptorUtils.replaceSimpleClassNameInDescriptor(toDescriptorString(), newSimpleName));
+ }
+
/** Get the fully qualified name using '/' in place of '.', aka the "internal type name" in ASM */
public String getInternalName() {
assert isClassType() || isArrayType();
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 c61de91..ae25597 100644
--- a/src/main/java/com/android/tools/r8/graph/DexTypeList.java
+++ b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.ArrayUtils;
import java.util.Arrays;
+import java.util.function.Consumer;
import java.util.stream.Stream;
public class DexTypeList extends DexItem {
@@ -34,6 +35,12 @@
return ArrayUtils.contains(values, type);
}
+ public void forEach(Consumer<DexType> consumer) {
+ for (DexType value : values) {
+ consumer.accept(value);
+ }
+ }
+
@Override
public int hashCode() {
return Arrays.hashCode(values);
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 2fe8c3f..273c69a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexValue.java
+++ b/src/main/java/com/android/tools/r8/graph/DexValue.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.EncodedValueUtils;
import java.util.Arrays;
+import java.util.function.Consumer;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Type;
@@ -1381,6 +1382,12 @@
this.values = values;
}
+ public void forEachElement(Consumer<DexValue> consumer) {
+ for (DexValue value : values) {
+ consumer.accept(value);
+ }
+ }
+
public DexValue[] getValues() {
return values;
}
@@ -1474,6 +1481,10 @@
this.value = value;
}
+ public DexEncodedAnnotation getValue() {
+ return value;
+ }
+
@Override
public DexValueKind getValueKind() {
return DexValueKind.ANNOTATION;
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
index c82fcf0..88869df 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
@@ -9,11 +9,20 @@
import java.util.Map;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
+import java.util.function.Function;
public class FieldAccessInfoCollectionImpl
implements FieldAccessInfoCollection<FieldAccessInfoImpl> {
- private Map<DexField, FieldAccessInfoImpl> infos = new IdentityHashMap<>();
+ private final Map<DexField, FieldAccessInfoImpl> infos;
+
+ public FieldAccessInfoCollectionImpl() {
+ this(new IdentityHashMap<>());
+ }
+
+ public FieldAccessInfoCollectionImpl(Map<DexField, FieldAccessInfoImpl> infos) {
+ this.infos = infos;
+ }
@Override
public void destroyAccessContexts() {
@@ -25,6 +34,11 @@
infos.values().forEach(FieldAccessInfoImpl::flattenAccessContexts);
}
+ public FieldAccessInfoImpl computeIfAbsent(
+ DexField field, Function<DexField, FieldAccessInfoImpl> fn) {
+ return infos.computeIfAbsent(field, fn);
+ }
+
@Override
public boolean contains(DexField field) {
return infos.containsKey(field);
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
index 023e423..9dae761 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
@@ -71,6 +71,14 @@
return field;
}
+ public AbstractAccessContexts getReadsWithContexts() {
+ return readsWithContexts;
+ }
+
+ public void setReadsWithContexts(AbstractAccessContexts readsWithContexts) {
+ this.readsWithContexts = readsWithContexts;
+ }
+
@Override
public int getNumberOfReadContexts() {
return readsWithContexts.getNumberOfAccessContexts();
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 14a4e95..7bfa6cb 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -3,11 +3,17 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.horizontalclassmerging.ClassMerger;
import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.ir.desugar.InterfaceProcessor.InterfaceProcessorNestedGraphLens;
import com.android.tools.r8.shaking.KeepInfoCollection;
+import com.android.tools.r8.utils.Action;
import com.android.tools.r8.utils.SetUtils;
+import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableMap;
@@ -21,7 +27,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.function.Supplier;
+import java.util.concurrent.ConcurrentHashMap;
/**
* A GraphLens implements a virtual view on top of the graph, used to delay global rewrites until
@@ -41,6 +47,89 @@
*/
public abstract class GraphLens {
+ abstract static class MemberLookupResult<R extends DexMember<?, R>> {
+
+ private final R reference;
+ private final R reboundReference;
+
+ private MemberLookupResult(R reference, R reboundReference) {
+ this.reference = reference;
+ this.reboundReference = reboundReference;
+ }
+
+ public R getReference() {
+ return reference;
+ }
+
+ public R getRewrittenReference(Map<R, R> rewritings) {
+ return rewritings.getOrDefault(reference, reference);
+ }
+
+ public boolean hasReboundReference() {
+ return reboundReference != null;
+ }
+
+ public R getReboundReference() {
+ return reboundReference;
+ }
+
+ public R getRewrittenReboundReference(Map<R, R> rewritings) {
+ return rewritings.getOrDefault(reboundReference, reboundReference);
+ }
+
+ abstract static class Builder<R extends DexMember<?, R>, Self extends Builder<R, Self>> {
+
+ R reference;
+ R reboundReference;
+
+ public Self setReference(R reference) {
+ this.reference = reference;
+ return self();
+ }
+
+ public Self setReboundReference(R reboundReference) {
+ this.reboundReference = reboundReference;
+ return self();
+ }
+
+ public abstract Self self();
+ }
+ }
+
+ /**
+ * Intermediate result of a field lookup that stores the actual non-rebound reference and the
+ * rebound reference that points to the definition of the field.
+ */
+ public static class FieldLookupResult extends MemberLookupResult<DexField> {
+
+ private FieldLookupResult(DexField reference, DexField reboundReference) {
+ super(reference, reboundReference);
+ }
+
+ public static Builder builder(GraphLens lens) {
+ return new Builder(lens);
+ }
+
+ public static class Builder extends MemberLookupResult.Builder<DexField, Builder> {
+
+ private GraphLens lens;
+
+ private Builder(GraphLens lens) {
+ this.lens = lens;
+ }
+
+ @Override
+ public Builder self() {
+ return this;
+ }
+
+ public FieldLookupResult build() {
+ // TODO(b/168282032): All non-identity graph lenses should set the rebound reference.
+ return new FieldLookupResult(reference, reboundReference);
+ }
+ }
+ }
+
/**
* Result of a method lookup in a GraphLens.
*
@@ -48,21 +137,23 @@
* prototype changes that have been made to the target method and the corresponding required
* changes to the invoke arguments.
*/
- public static class GraphLensLookupResult {
+ public static class MethodLookupResult extends MemberLookupResult<DexMethod> {
- private final DexMethod method;
private final Type type;
private final RewrittenPrototypeDescription prototypeChanges;
- public GraphLensLookupResult(
- DexMethod method, Type type, RewrittenPrototypeDescription prototypeChanges) {
- this.method = method;
+ public MethodLookupResult(
+ DexMethod reference,
+ DexMethod reboundReference,
+ Type type,
+ RewrittenPrototypeDescription prototypeChanges) {
+ super(reference, reboundReference);
this.type = type;
this.prototypeChanges = prototypeChanges;
}
- public DexMethod getMethod() {
- return method;
+ public static Builder builder(GraphLens lens) {
+ return new Builder(lens);
}
public Type getType() {
@@ -72,6 +163,38 @@
public RewrittenPrototypeDescription getPrototypeChanges() {
return prototypeChanges;
}
+
+ public static class Builder extends MemberLookupResult.Builder<DexMethod, Builder> {
+
+ private final GraphLens lens;
+ private RewrittenPrototypeDescription prototypeChanges = RewrittenPrototypeDescription.none();
+ private Type type;
+
+ private Builder(GraphLens lens) {
+ this.lens = lens;
+ }
+
+ public Builder setPrototypeChanges(RewrittenPrototypeDescription prototypeChanges) {
+ this.prototypeChanges = prototypeChanges;
+ return this;
+ }
+
+ public Builder setType(Type type) {
+ this.type = type;
+ return this;
+ }
+
+ public MethodLookupResult build() {
+ assert reference != null;
+ // TODO(b/168282032): All non-identity graph lenses should set the rebound reference.
+ return new MethodLookupResult(reference, reboundReference, type, prototypeChanges);
+ }
+
+ @Override
+ public Builder self() {
+ return this;
+ }
+ }
}
public static class Builder {
@@ -141,9 +264,11 @@
}
}
- public static Builder builder() {
- return new Builder();
- }
+ /**
+ * Intentionally private. All graph lenses except for {@link IdentityGraphLens} should inherit
+ * from {@link NonIdentityGraphLens}.
+ */
+ private GraphLens() {}
public abstract DexType getOriginalType(DexType type);
@@ -191,25 +316,50 @@
public ProgramMethod mapProgramMethod(
ProgramMethod oldMethod, DexDefinitionSupplier definitions) {
DexMethod newMethod = getRenamedMethodSignature(oldMethod.getReference());
- DexProgramClass holder = definitions.definitionForHolder(newMethod).asProgramClass();
- return holder.lookupProgramMethod(newMethod);
+ DexProgramClass holder = asProgramClassOrNull(definitions.definitionForHolder(newMethod));
+ return newMethod.lookupOnProgramClass(holder);
}
+ public abstract DexType lookupClassType(DexType type);
+
public abstract DexType lookupType(DexType type);
// This overload can be used when the graph lens is known to be context insensitive.
- public DexMethod lookupMethod(DexMethod method) {
+ public final DexMethod lookupMethod(DexMethod method) {
assert verifyIsContextFreeForMethod(method);
- return lookupMethod(method, null, null).getMethod();
+ return lookupMethod(method, null, null).getReference();
}
- public abstract GraphLensLookupResult lookupMethod(
- DexMethod method, DexMethod context, Type type);
+ /** Lookup a rebound or non-rebound method reference using the current graph lens. */
+ public MethodLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
+ return internalLookupMethod(method, context, type, result -> result);
+ }
+
+ protected abstract MethodLookupResult internalLookupMethod(
+ DexMethod reference, DexMethod context, Type type, LookupMethodContinuation continuation);
+
+ interface LookupMethodContinuation {
+
+ MethodLookupResult lookupMethod(MethodLookupResult previous);
+ }
public abstract RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(
DexMethod method);
- public abstract DexField lookupField(DexField field);
+ /** Lookup a rebound or non-rebound field reference using the current graph lens. */
+ public DexField lookupField(DexField field) {
+ // Lookup the field using the graph lens and return the (non-rebound) reference from the lookup
+ // result.
+ return internalLookupField(field, FieldLookupResult::getReference);
+ }
+
+ protected abstract DexField internalLookupField(
+ DexField reference, LookupFieldContinuation continuation);
+
+ interface LookupFieldContinuation {
+
+ DexField lookupField(FieldLookupResult previous);
+ }
public DexMethod lookupGetFieldForMethod(DexField field, DexMethod context) {
return null;
@@ -254,8 +404,16 @@
return true;
}
- public final boolean isIdentityLens() {
- return this == getIdentityLens();
+ public boolean isAppliedLens() {
+ return false;
+ }
+
+ public abstract boolean isIdentityLens();
+
+ public abstract boolean isNonIdentityLens();
+
+ public NonIdentityGraphLens asNonIdentityLens() {
+ return null;
}
public boolean isInterfaceProcessorLens() {
@@ -266,17 +424,9 @@
return null;
}
- public boolean isGraphLensWithPrevious() {
- return false;
- }
-
- public GraphLensWithPrevious asGraphLensWithPrevious() {
- return null;
- }
-
- public GraphLens withCodeRewritingsApplied() {
+ public GraphLens withCodeRewritingsApplied(DexItemFactory dexItemFactory) {
if (hasCodeRewritings()) {
- return new ClearCodeRewritingGraphLens(this);
+ return new ClearCodeRewritingGraphLens(dexItemFactory, this);
}
return this;
}
@@ -317,6 +467,22 @@
return true;
}
+ public Map<DexCallSite, ProgramMethodSet> rewriteCallSites(
+ Map<DexCallSite, ProgramMethodSet> callSites, DexDefinitionSupplier definitions) {
+ Map<DexCallSite, ProgramMethodSet> result = new IdentityHashMap<>();
+ LensCodeRewriterUtils rewriter = new LensCodeRewriterUtils(definitions, this);
+ callSites.forEach(
+ (callSite, contexts) -> {
+ for (ProgramMethod context : contexts.rewrittenWithLens(definitions, this)) {
+ DexCallSite rewrittenCallSite = rewriter.rewriteCallSite(callSite, context);
+ result
+ .computeIfAbsent(rewrittenCallSite, ignore -> ProgramMethodSet.create())
+ .add(context);
+ }
+ });
+ return result;
+ }
+
public DexReference rewriteReference(DexReference reference) {
if (reference.isDexField()) {
return getRenamedFieldSignature(reference.asDexField());
@@ -432,7 +598,96 @@
return true;
}
- private static class IdentityGraphLens extends GraphLens {
+ public abstract static class NonIdentityGraphLens extends GraphLens {
+
+ private final DexItemFactory dexItemFactory;
+ private GraphLens previousLens;
+
+ private final Map<DexType, DexType> arrayTypeCache = new ConcurrentHashMap<>();
+
+ public NonIdentityGraphLens(DexItemFactory dexItemFactory, GraphLens previousLens) {
+ this.dexItemFactory = dexItemFactory;
+ this.previousLens = previousLens;
+ }
+
+ public final GraphLens getPrevious() {
+ return previousLens;
+ }
+
+ public final void withAlternativeParentLens(GraphLens lens, Action action) {
+ GraphLens oldParent = getPrevious();
+ previousLens = lens;
+ action.execute();
+ previousLens = oldParent;
+ }
+
+ @Override
+ public final DexType lookupType(DexType type) {
+ if (type.isPrimitiveType() || type.isVoidType() || type.isNullValueType()) {
+ return type;
+ }
+ if (type.isArrayType()) {
+ DexType result = arrayTypeCache.get(type);
+ if (result == null) {
+ DexType baseType = type.toBaseType(dexItemFactory);
+ DexType newType = lookupType(baseType);
+ result = baseType == newType ? type : type.replaceBaseType(newType, dexItemFactory);
+ arrayTypeCache.put(type, result);
+ }
+ return result;
+ }
+ return lookupClassType(type);
+ }
+
+ @Override
+ public final DexType lookupClassType(DexType type) {
+ assert type.isClassType() : "Expected class type, but was `" + type.toSourceString() + "`";
+ return internalDescribeLookupClassType(getPrevious().lookupClassType(type));
+ }
+
+ @Override
+ protected DexField internalLookupField(
+ DexField reference, LookupFieldContinuation continuation) {
+ return previousLens.internalLookupField(
+ reference, previous -> continuation.lookupField(internalDescribeLookupField(previous)));
+ }
+
+ @Override
+ protected MethodLookupResult internalLookupMethod(
+ DexMethod reference, DexMethod context, Type type, LookupMethodContinuation continuation) {
+ return previousLens.internalLookupMethod(
+ reference,
+ internalGetPreviousMethodSignature(context),
+ type,
+ previous -> continuation.lookupMethod(internalDescribeLookupMethod(previous, context)));
+ }
+
+ protected abstract FieldLookupResult internalDescribeLookupField(FieldLookupResult previous);
+
+ protected abstract MethodLookupResult internalDescribeLookupMethod(
+ MethodLookupResult previous, DexMethod context);
+
+ protected abstract DexType internalDescribeLookupClassType(DexType previous);
+
+ protected abstract DexMethod internalGetPreviousMethodSignature(DexMethod method);
+
+ @Override
+ public final boolean isIdentityLens() {
+ return false;
+ }
+
+ @Override
+ public final boolean isNonIdentityLens() {
+ return true;
+ }
+
+ @Override
+ public final NonIdentityGraphLens asNonIdentityLens() {
+ return this;
+ }
+ }
+
+ private static final class IdentityGraphLens extends GraphLens {
private static IdentityGraphLens INSTANCE = new IdentityGraphLens();
@@ -443,6 +698,16 @@
}
@Override
+ public boolean isIdentityLens() {
+ return true;
+ }
+
+ @Override
+ public boolean isNonIdentityLens() {
+ return false;
+ }
+
+ @Override
public DexType getOriginalType(DexType type) {
return type;
}
@@ -473,8 +738,9 @@
}
@Override
- public GraphLensLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
- return new GraphLensLookupResult(method, type, RewrittenPrototypeDescription.none());
+ public DexType lookupClassType(DexType type) {
+ assert type.isClassType();
+ return type;
}
@Override
@@ -484,8 +750,21 @@
}
@Override
- public DexField lookupField(DexField field) {
- return field;
+ protected DexField internalLookupField(
+ DexField reference, LookupFieldContinuation continuation) {
+ // Passes the field reference back to the next graph lens. The identity lens intentionally
+ // does not set the rebound field reference, since it does not know what that is.
+ return continuation.lookupField(
+ FieldLookupResult.builder(this).setReference(reference).build());
+ }
+
+ @Override
+ protected MethodLookupResult internalLookupMethod(
+ DexMethod reference, DexMethod context, Type type, LookupMethodContinuation continuation) {
+ // Passes the method reference back to the next graph lens. The identity lens intentionally
+ // does not set the rebound method reference, since it does not know what that is.
+ return continuation.lookupMethod(
+ MethodLookupResult.builder(this).setReference(reference).setType(type).build());
}
@Override
@@ -501,66 +780,82 @@
// This lens clears all code rewriting (lookup methods mimics identity lens behavior) but still
// relies on the previous lens for names (getRenamed/Original methods).
- public static class ClearCodeRewritingGraphLens extends IdentityGraphLens
- implements GraphLensWithPrevious {
+ public static class ClearCodeRewritingGraphLens extends NonIdentityGraphLens {
- private final GraphLens previous;
-
- public ClearCodeRewritingGraphLens(GraphLens previous) {
- this.previous = previous;
- }
-
- @Override
- public boolean isGraphLensWithPrevious() {
- return true;
- }
-
- @Override
- public GraphLensWithPrevious asGraphLensWithPrevious() {
- return this;
- }
-
- @Override
- public GraphLens getPrevious() {
- return previous;
+ public ClearCodeRewritingGraphLens(DexItemFactory dexItemFactory, GraphLens previousLens) {
+ super(dexItemFactory, previousLens);
}
@Override
public DexType getOriginalType(DexType type) {
- return previous.getOriginalType(type);
+ return getPrevious().getOriginalType(type);
}
@Override
public DexField getOriginalFieldSignature(DexField field) {
- return previous.getOriginalFieldSignature(field);
+ return getPrevious().getOriginalFieldSignature(field);
}
@Override
public DexMethod getOriginalMethodSignature(DexMethod method) {
- return previous.getOriginalMethodSignature(method);
+ return getPrevious().getOriginalMethodSignature(method);
}
@Override
public DexField getRenamedFieldSignature(DexField originalField) {
- return previous.getRenamedFieldSignature(originalField);
+ return getPrevious().getRenamedFieldSignature(originalField);
}
@Override
public DexMethod getRenamedMethodSignature(DexMethod originalMethod, GraphLens applied) {
return this != applied
- ? previous.getRenamedMethodSignature(originalMethod, applied)
+ ? getPrevious().getRenamedMethodSignature(originalMethod, applied)
: originalMethod;
}
@Override
- public DexType lookupType(DexType type) {
- return previous.lookupType(type);
+ public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(
+ DexMethod method) {
+ return getIdentityLens().lookupPrototypeChangesForMethodDefinition(method);
}
- }
- public interface GraphLensWithPrevious {
+ @Override
+ public final DexType internalDescribeLookupClassType(DexType previous) {
+ return previous;
+ }
- GraphLens getPrevious();
+ @Override
+ protected DexField internalLookupField(
+ DexField reference, LookupFieldContinuation continuation) {
+ return getIdentityLens().internalLookupField(reference, continuation);
+ }
+
+ @Override
+ protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
+ throw new Unreachable();
+ }
+
+ @Override
+ protected MethodLookupResult internalLookupMethod(
+ DexMethod reference, DexMethod context, Type type, LookupMethodContinuation continuation) {
+ return getIdentityLens().internalLookupMethod(reference, context, type, continuation);
+ }
+
+ @Override
+ public MethodLookupResult internalDescribeLookupMethod(
+ MethodLookupResult previous, DexMethod context) {
+ throw new Unreachable();
+ }
+
+ @Override
+ protected DexMethod internalGetPreviousMethodSignature(DexMethod method) {
+ return method;
+ }
+
+ @Override
+ public boolean isContextFreeForMethods() {
+ return getIdentityLens().isContextFreeForMethods();
+ }
}
/**
@@ -573,13 +868,11 @@
* #mapInvocationType(DexMethod, DexMethod, Type)} if the default name mapping applies, and only
* invocation type might need to change.
*/
- public static class NestedGraphLens extends GraphLens implements GraphLensWithPrevious {
+ public static class NestedGraphLens extends NonIdentityGraphLens {
- protected GraphLens previousLens;
protected final DexItemFactory dexItemFactory;
protected final Map<DexType, DexType> typeMap;
- private final Map<DexType, DexType> arrayTypeCache = new IdentityHashMap<>();
protected final Map<DexMethod, DexMethod> methodMap;
protected final Map<DexField, DexField> fieldMap;
@@ -602,6 +895,7 @@
BiMap<DexMethod, DexMethod> originalMethodSignatures,
GraphLens previousLens,
DexItemFactory dexItemFactory) {
+ super(dexItemFactory, previousLens);
assert !typeMap.isEmpty()
|| !methodMap.isEmpty()
|| !fieldMap.isEmpty()
@@ -611,36 +905,16 @@
this.fieldMap = fieldMap;
this.originalFieldSignatures = originalFieldSignatures;
this.originalMethodSignatures = originalMethodSignatures;
- this.previousLens = previousLens;
this.dexItemFactory = dexItemFactory;
}
- @Override
- public GraphLens getPrevious() {
- return previousLens;
- }
-
- public <T> T withAlternativeParentLens(GraphLens lens, Supplier<T> action) {
- GraphLens oldParent = previousLens;
- previousLens = lens;
- T result = action.get();
- previousLens = oldParent;
- return result;
- }
-
- @Override
- public boolean isGraphLensWithPrevious() {
- return true;
- }
-
- @Override
- public NestedGraphLens asGraphLensWithPrevious() {
- return this;
+ public static Builder builder() {
+ return new Builder();
}
@Override
public DexType getOriginalType(DexType type) {
- return previousLens.getOriginalType(type);
+ return getPrevious().getOriginalType(type);
}
@Override
@@ -649,7 +923,7 @@
originalFieldSignatures != null
? originalFieldSignatures.getOrDefault(field, field)
: field;
- return previousLens.getOriginalFieldSignature(originalField);
+ return getPrevious().getOriginalFieldSignature(originalField);
}
@Override
@@ -658,12 +932,12 @@
originalMethodSignatures != null
? originalMethodSignatures.getOrDefault(method, method)
: method;
- return previousLens.getOriginalMethodSignature(originalMethod);
+ return getPrevious().getOriginalMethodSignature(originalMethod);
}
@Override
public DexField getRenamedFieldSignature(DexField originalField) {
- DexField renamedField = previousLens.getRenamedFieldSignature(originalField);
+ DexField renamedField = getPrevious().getRenamedFieldSignature(originalField);
return originalFieldSignatures != null
? originalFieldSignatures.inverse().getOrDefault(renamedField, renamedField)
: renamedField;
@@ -674,49 +948,73 @@
if (this == applied) {
return originalMethod;
}
- DexMethod renamedMethod = previousLens.getRenamedMethodSignature(originalMethod, applied);
+ DexMethod renamedMethod = getPrevious().getRenamedMethodSignature(originalMethod, applied);
return originalMethodSignatures != null
? originalMethodSignatures.inverse().getOrDefault(renamedMethod, renamedMethod)
: renamedMethod;
}
@Override
- public DexType lookupType(DexType type) {
- if (type.isArrayType()) {
- synchronized (this) {
- // This block need to be synchronized due to arrayTypeCache.
- DexType result = arrayTypeCache.get(type);
- if (result == null) {
- DexType baseType = type.toBaseType(dexItemFactory);
- DexType newType = lookupType(baseType);
- if (baseType == newType) {
- result = type;
- } else {
- result = type.replaceBaseType(newType, dexItemFactory);
- }
- arrayTypeCache.put(type, result);
- }
- return result;
- }
- }
- DexType previous = previousLens.lookupType(type);
+ protected DexType internalDescribeLookupClassType(DexType previous) {
return typeMap != null ? typeMap.getOrDefault(previous, previous) : previous;
}
@Override
- public GraphLensLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
- DexMethod previousContext = internalGetPreviousMethodSignature(context);
- GraphLensLookupResult lookup = previousLens.lookupMethod(method, previousContext, type);
- DexMethod newMethod = methodMap.get(lookup.getMethod());
- if (newMethod == null) {
- return lookup;
+ protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
+ if (previous.hasReboundReference()) {
+ // Rewrite the rebound reference and then "fixup" the non-rebound reference.
+ DexField rewrittenReboundReference = previous.getRewrittenReboundReference(fieldMap);
+ return FieldLookupResult.builder(this)
+ .setReboundReference(rewrittenReboundReference)
+ .setReference(
+ rewrittenReboundReference.withHolder(
+ internalDescribeLookupClassType(previous.getReference().getHolderType()),
+ dexItemFactory))
+ .build();
+ } else {
+ // TODO(b/168282032): We should always have the rebound reference, so this should become
+ // unreachable.
+ DexField rewrittenReference = previous.getRewrittenReference(fieldMap);
+ return FieldLookupResult.builder(this).setReference(rewrittenReference).build();
}
- // TODO(sgjesse): Should we always do interface to virtual mapping? Is it a performance win
- // that only subclasses which are known to need it actually do it?
- return new GraphLensLookupResult(
- newMethod,
- mapInvocationType(newMethod, method, lookup.getType()),
- internalDescribePrototypeChanges(lookup.getPrototypeChanges(), newMethod));
+ }
+
+ @Override
+ public MethodLookupResult internalDescribeLookupMethod(
+ MethodLookupResult previous, DexMethod context) {
+ if (previous.hasReboundReference()) {
+ // TODO(sgjesse): Should we always do interface to virtual mapping? Is it a performance win
+ // that only subclasses which are known to need it actually do it?
+ DexMethod rewrittenReboundReference = previous.getRewrittenReboundReference(methodMap);
+ return MethodLookupResult.builder(this)
+ .setReboundReference(rewrittenReboundReference)
+ .setReference(
+ rewrittenReboundReference.withHolder(
+ internalDescribeLookupClassType(previous.getReference().getHolderType()),
+ dexItemFactory))
+ .setPrototypeChanges(
+ internalDescribePrototypeChanges(
+ previous.getPrototypeChanges(), rewrittenReboundReference))
+ .setType(
+ mapInvocationType(
+ rewrittenReboundReference, previous.getReference(), previous.getType()))
+ .build();
+ } else {
+ // TODO(b/168282032): We should always have the rebound reference, so this should become
+ // unreachable.
+ DexMethod newMethod = methodMap.get(previous.getReference());
+ if (newMethod == null) {
+ return previous;
+ }
+ // TODO(sgjesse): Should we always do interface to virtual mapping? Is it a performance win
+ // that only subclasses which are known to need it actually do it?
+ return MethodLookupResult.builder(this)
+ .setReference(newMethod)
+ .setPrototypeChanges(
+ internalDescribePrototypeChanges(previous.getPrototypeChanges(), newMethod))
+ .setType(mapInvocationType(newMethod, previous.getReference(), previous.getType()))
+ .build();
+ }
}
@Override
@@ -724,7 +1022,7 @@
DexMethod method) {
DexMethod previous = internalGetPreviousMethodSignature(method);
RewrittenPrototypeDescription lookup =
- previousLens.lookupPrototypeChangesForMethodDefinition(previous);
+ getPrevious().lookupPrototypeChangesForMethodDefinition(previous);
return internalDescribePrototypeChanges(lookup, method);
}
@@ -733,6 +1031,7 @@
return prototypeChanges;
}
+ @Override
protected DexMethod internalGetPreviousMethodSignature(DexMethod method) {
return originalMethodSignatures != null
? originalMethodSignatures.getOrDefault(method, method)
@@ -741,12 +1040,12 @@
@Override
public DexMethod lookupGetFieldForMethod(DexField field, DexMethod context) {
- return previousLens.lookupGetFieldForMethod(field, context);
+ return getPrevious().lookupGetFieldForMethod(field, context);
}
@Override
public DexMethod lookupPutFieldForMethod(DexField field, DexMethod context) {
- return previousLens.lookupPutFieldForMethod(field, context);
+ return getPrevious().lookupPutFieldForMethod(field, context);
}
/**
@@ -788,19 +1087,13 @@
}
@Override
- public DexField lookupField(DexField field) {
- DexField previous = previousLens.lookupField(field);
- return fieldMap.getOrDefault(previous, previous);
- }
-
- @Override
public boolean isContextFreeForMethods() {
- return previousLens.isContextFreeForMethods();
+ return getPrevious().isContextFreeForMethods();
}
@Override
public boolean verifyIsContextFreeForMethod(DexMethod method) {
- assert previousLens.verifyIsContextFreeForMethod(method);
+ assert getPrevious().verifyIsContextFreeForMethod(method);
return true;
}
@@ -821,7 +1114,7 @@
builder.append(entry.getKey().toSourceString()).append(" -> ");
builder.append(entry.getValue().toSourceString()).append(System.lineSeparator());
}
- builder.append(previousLens.toString());
+ builder.append(getPrevious().toString());
return builder.toString();
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/InnerClassAttribute.java b/src/main/java/com/android/tools/r8/graph/InnerClassAttribute.java
index 74f9880..cd3baa0 100644
--- a/src/main/java/com/android/tools/r8/graph/InnerClassAttribute.java
+++ b/src/main/java/com/android/tools/r8/graph/InnerClassAttribute.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
+import java.util.function.Consumer;
import org.objectweb.asm.ClassWriter;
/** Representation of an entry in the Java InnerClasses attribute table. */
@@ -38,6 +39,15 @@
this.innerName = innerName;
}
+ public void forEachType(Consumer<DexType> consumer) {
+ if (inner != null) {
+ consumer.accept(inner);
+ }
+ if (outer != null) {
+ consumer.accept(outer);
+ }
+ }
+
public boolean isNamed() {
return innerName != null;
}
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 7936e33..2b38f4f 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -239,7 +239,8 @@
"Malformed inner-class attribute:",
"\touterTypeInternal: " + outerName,
"\tinnerTypeInternal: " + name,
- "\tinnerName: " + innerName)));
+ "\tinnerName: " + innerName),
+ origin));
}
}
innerClasses.add(
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index b0e0312..263a21b 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -67,8 +67,10 @@
import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Deque;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
@@ -210,7 +212,8 @@
@Override
public IRCode buildIR(ProgramMethod method, AppView<?> appView, Origin origin) {
- return asCfCode().buildIR(method, appView, origin);
+ return verifyFrames(asCfCode(), method.getDefinition(), appView, origin, true)
+ .buildIR(method, appView, origin);
}
@Override
@@ -222,7 +225,12 @@
Position callerPosition,
Origin origin,
MethodProcessor methodProcessor) {
- return asCfCode()
+ return verifyFrames(
+ asCfCode(),
+ method.getDefinition(),
+ appView,
+ origin,
+ methodProcessor.shouldApplyCodeRewritings(method))
.buildInliningIR(
context,
method,
@@ -233,6 +241,18 @@
methodProcessor);
}
+ private CfCode verifyFrames(
+ CfCode cfCode,
+ DexEncodedMethod method,
+ AppView<?> appView,
+ Origin origin,
+ boolean shouldApplyCodeRewritings) {
+ if (!cfCode.verifyFrames(method, appView, origin, shouldApplyCodeRewritings)) {
+ cfCode.instructions.removeIf(CfInstruction::isFrame);
+ }
+ return cfCode;
+ }
+
@Override
public void registerCodeReferences(ProgramMethod method, UseRegistry registry) {
asCfCode().registerCodeReferences(method, registry);
@@ -387,7 +407,7 @@
int frameType, int nLocals, Object[] localTypes, int nStack, Object[] stackTypes) {
assert frameType == Opcodes.F_NEW;
Int2ReferenceSortedMap<FrameType> parsedLocals = parseLocals(nLocals, localTypes);
- List<FrameType> parsedStack = parseStack(nStack, stackTypes);
+ Deque<FrameType> parsedStack = parseStack(nStack, stackTypes);
instructions.add(new CfFrame(parsedLocals, parsedStack));
}
@@ -405,8 +425,8 @@
return types;
}
- private List<FrameType> parseStack(int nStack, Object[] stackTypes) {
- List<FrameType> dexStack = new ArrayList<>(nStack);
+ private Deque<FrameType> parseStack(int nStack, Object[] stackTypes) {
+ Deque<FrameType> dexStack = new ArrayDeque<>(nStack);
for (int i = 0; i < nStack; i++) {
dexStack.add(getFrameType(stackTypes[i]));
}
@@ -415,7 +435,7 @@
private FrameType getFrameType(Object localType) {
if (localType instanceof Label) {
- return FrameType.uninitializedNew(getLabel((Label) localType));
+ return FrameType.uninitializedNew(getLabel((Label) localType), null);
} else if (localType == Opcodes.UNINITIALIZED_THIS) {
return FrameType.uninitializedThis();
} else if (localType == null || localType == Opcodes.TOP) {
@@ -989,12 +1009,11 @@
private static DebugParsingOptions getParsingOptions(
JarApplicationReader application, boolean reachabilitySensitive) {
+ // TODO(b/166841731): We should compute our own from the compressed format.
int parsingOptions =
- (application.options.enableCfByteCodePassThrough
- || application.options.testing.readInputStackMaps)
+ application.options.testing.readInputStackMaps
? ClassReader.EXPAND_FRAMES
: ClassReader.SKIP_FRAMES;
-
ProguardConfiguration configuration = application.options.getProguardConfiguration();
if (configuration == null) {
return new DebugParsingOptions(true, true, parsingOptions);
diff --git a/src/main/java/com/android/tools/r8/graph/MethodAccessInfoCollection.java b/src/main/java/com/android/tools/r8/graph/MethodAccessInfoCollection.java
new file mode 100644
index 0000000..4b20148
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/MethodAccessInfoCollection.java
@@ -0,0 +1,185 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.utils.ConsumerUtils;
+import com.android.tools.r8.utils.MapUtils;
+import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import com.google.common.collect.Sets;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+public class MethodAccessInfoCollection {
+
+ private final Map<DexMethod, ProgramMethodSet> directInvokes;
+ private final Map<DexMethod, ProgramMethodSet> interfaceInvokes;
+ private final Map<DexMethod, ProgramMethodSet> staticInvokes;
+ private final Map<DexMethod, ProgramMethodSet> superInvokes;
+ private final Map<DexMethod, ProgramMethodSet> virtualInvokes;
+
+ private MethodAccessInfoCollection(
+ Map<DexMethod, ProgramMethodSet> directInvokes,
+ Map<DexMethod, ProgramMethodSet> interfaceInvokes,
+ Map<DexMethod, ProgramMethodSet> staticInvokes,
+ Map<DexMethod, ProgramMethodSet> superInvokes,
+ Map<DexMethod, ProgramMethodSet> virtualInvokes) {
+ this.directInvokes = directInvokes;
+ this.interfaceInvokes = interfaceInvokes;
+ this.staticInvokes = staticInvokes;
+ this.superInvokes = superInvokes;
+ this.virtualInvokes = virtualInvokes;
+ }
+
+ public static ConcurrentBuilder concurrentBuilder() {
+ return new ConcurrentBuilder();
+ }
+
+ // TODO(b/132593519): We should not need sorted maps with the new member rebinding analysis.
+ public static SortedBuilder sortedBuilder() {
+ return new SortedBuilder();
+ }
+
+ public void forEachMethodReference(Consumer<DexMethod> method) {
+ Set<DexMethod> seen = Sets.newIdentityHashSet();
+ directInvokes.keySet().forEach(ConsumerUtils.acceptIfNotSeen(method, seen));
+ interfaceInvokes.keySet().forEach(ConsumerUtils.acceptIfNotSeen(method, seen));
+ staticInvokes.keySet().forEach(ConsumerUtils.acceptIfNotSeen(method, seen));
+ superInvokes.keySet().forEach(ConsumerUtils.acceptIfNotSeen(method, seen));
+ virtualInvokes.keySet().forEach(ConsumerUtils.acceptIfNotSeen(method, seen));
+ }
+
+ public void forEachDirectInvoke(BiConsumer<DexMethod, ProgramMethodSet> consumer) {
+ directInvokes.forEach(consumer);
+ }
+
+ public void forEachInterfaceInvoke(BiConsumer<DexMethod, ProgramMethodSet> consumer) {
+ interfaceInvokes.forEach(consumer);
+ }
+
+ public void forEachStaticInvoke(BiConsumer<DexMethod, ProgramMethodSet> consumer) {
+ staticInvokes.forEach(consumer);
+ }
+
+ public void forEachSuperInvoke(BiConsumer<DexMethod, ProgramMethodSet> consumer) {
+ superInvokes.forEach(consumer);
+ }
+
+ public void forEachVirtualInvoke(BiConsumer<DexMethod, ProgramMethodSet> consumer) {
+ virtualInvokes.forEach(consumer);
+ }
+
+ public MethodAccessInfoCollection rewrittenWithLens(
+ DexDefinitionSupplier definitions, GraphLens lens) {
+ return new MethodAccessInfoCollection(
+ rewriteInvokesWithLens(directInvokes, definitions, lens),
+ rewriteInvokesWithLens(interfaceInvokes, definitions, lens),
+ rewriteInvokesWithLens(staticInvokes, definitions, lens),
+ rewriteInvokesWithLens(superInvokes, definitions, lens),
+ rewriteInvokesWithLens(virtualInvokes, definitions, lens));
+ }
+
+ private static Map<DexMethod, ProgramMethodSet> rewriteInvokesWithLens(
+ Map<DexMethod, ProgramMethodSet> invokes, DexDefinitionSupplier definitions, GraphLens lens) {
+ return MapUtils.map(
+ invokes,
+ capacity -> new TreeMap<>(DexMethod::slowCompareTo),
+ lens::getRenamedMethodSignature,
+ methods -> methods.rewrittenWithLens(definitions, lens),
+ (methods, other) -> {
+ methods.addAll(other);
+ return methods;
+ });
+ }
+
+ public abstract static class Builder<T extends Map<DexMethod, ProgramMethodSet>> {
+
+ private final T directInvokes;
+ private final T interfaceInvokes;
+ private final T staticInvokes;
+ private final T superInvokes;
+ private final T virtualInvokes;
+
+ private Builder(Supplier<T> factory) {
+ directInvokes = factory.get();
+ interfaceInvokes = factory.get();
+ staticInvokes = factory.get();
+ superInvokes = factory.get();
+ virtualInvokes = factory.get();
+ }
+
+ public T getDirectInvokes() {
+ return directInvokes;
+ }
+
+ public T getInterfaceInvokes() {
+ return interfaceInvokes;
+ }
+
+ public T getStaticInvokes() {
+ return staticInvokes;
+ }
+
+ public T getSuperInvokes() {
+ return superInvokes;
+ }
+
+ public T getVirtualInvokes() {
+ return virtualInvokes;
+ }
+
+ public boolean registerInvokeDirectInContext(DexMethod invokedMethod, ProgramMethod context) {
+ return registerInvokeMethodInContext(invokedMethod, context, directInvokes);
+ }
+
+ public boolean registerInvokeInterfaceInContext(
+ DexMethod invokedMethod, ProgramMethod context) {
+ return registerInvokeMethodInContext(invokedMethod, context, interfaceInvokes);
+ }
+
+ public boolean registerInvokeStaticInContext(DexMethod invokedMethod, ProgramMethod context) {
+ return registerInvokeMethodInContext(invokedMethod, context, staticInvokes);
+ }
+
+ public boolean registerInvokeSuperInContext(DexMethod invokedMethod, ProgramMethod context) {
+ return registerInvokeMethodInContext(invokedMethod, context, superInvokes);
+ }
+
+ public boolean registerInvokeVirtualInContext(DexMethod invokedMethod, ProgramMethod context) {
+ return registerInvokeMethodInContext(invokedMethod, context, virtualInvokes);
+ }
+
+ private static boolean registerInvokeMethodInContext(
+ DexMethod invokedMethod, ProgramMethod context, Map<DexMethod, ProgramMethodSet> invokes) {
+ return invokes
+ .computeIfAbsent(invokedMethod, ignore -> ProgramMethodSet.create())
+ .add(context);
+ }
+
+ public MethodAccessInfoCollection build() {
+ return new MethodAccessInfoCollection(
+ directInvokes, interfaceInvokes, staticInvokes, superInvokes, virtualInvokes);
+ }
+ }
+
+ public static class ConcurrentBuilder
+ extends Builder<ConcurrentHashMap<DexMethod, ProgramMethodSet>> {
+
+ private ConcurrentBuilder() {
+ super(ConcurrentHashMap::new);
+ }
+ }
+
+ public static class SortedBuilder extends Builder<TreeMap<DexMethod, ProgramMethodSet>> {
+
+ private SortedBuilder() {
+ super(() -> new TreeMap<>(DexMethod::slowCompareTo));
+ }
+ }
+}
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 9839a80..b4797a9 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
@@ -252,11 +252,11 @@
}
public int getOffsetFor(DexField field) {
- return getOffsetFor(graphLens.lookupField(field), fields);
+ return getOffsetFor(field, fields);
}
public int getOffsetFor(DexMethod method) {
- return getOffsetFor(graphLens.lookupMethod(method), methods);
+ return getOffsetFor(method, methods);
}
public int getOffsetFor(DexString string) {
@@ -264,7 +264,7 @@
}
public int getOffsetFor(DexType type) {
- return getOffsetFor(graphLens.lookupType(type), types);
+ return getOffsetFor(type, types);
}
public int getOffsetFor(DexCallSite callSite) {
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java b/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java
new file mode 100644
index 0000000..91edb06
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+public interface ProgramDefinition {
+
+ DexType getContextType();
+
+ DexDefinition getDefinition();
+}
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMember.java b/src/main/java/com/android/tools/r8/graph/ProgramMember.java
index 7dc209c..328c71f 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramMember.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramMember.java
@@ -4,8 +4,10 @@
package com.android.tools.r8.graph;
-public interface ProgramMember<D extends DexEncodedMember<D, R>, R extends DexMember<D, R>> {
+public interface ProgramMember<D extends DexEncodedMember<D, R>, R extends DexMember<D, R>>
+ extends ProgramDefinition {
+ @Override
D getDefinition();
DexType getHolderType();
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramPackage.java b/src/main/java/com/android/tools/r8/graph/ProgramPackage.java
index c92c6d4..81627e9 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramPackage.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramPackage.java
@@ -9,14 +9,21 @@
import java.util.Iterator;
import java.util.Set;
import java.util.function.Consumer;
+import java.util.function.Supplier;
public class ProgramPackage implements Iterable<DexProgramClass> {
private final String packageDescriptor;
- private final Set<DexProgramClass> classes = Sets.newIdentityHashSet();
+ private final Set<DexProgramClass> classes;
public ProgramPackage(String packageDescriptor) {
+ this(packageDescriptor, Sets::newIdentityHashSet);
+ }
+
+ protected ProgramPackage(
+ String packageDescriptor, Supplier<Set<DexProgramClass>> backingFactory) {
this.packageDescriptor = packageDescriptor;
+ this.classes = backingFactory.get();
}
public boolean add(DexProgramClass clazz) {
@@ -24,6 +31,10 @@
return classes.add(clazz);
}
+ public boolean contains(DexProgramClass clazz) {
+ return classes.contains(clazz);
+ }
+
public String getLastPackageName() {
int index = packageDescriptor.lastIndexOf('/');
if (index >= 0) {
@@ -32,6 +43,10 @@
return packageDescriptor;
}
+ public String getPackageDescriptor() {
+ return packageDescriptor;
+ }
+
public void forEachClass(Consumer<DexProgramClass> consumer) {
forEach(consumer);
}
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramPackageCollection.java b/src/main/java/com/android/tools/r8/graph/ProgramPackageCollection.java
index fe9a64c..5d53ac0 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramPackageCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramPackageCollection.java
@@ -10,9 +10,9 @@
public class ProgramPackageCollection implements Iterable<ProgramPackage> {
- private final Map<String, ProgramPackage> packages;
+ protected final Map<String, ProgramPackage> packages;
- private ProgramPackageCollection(Map<String, ProgramPackage> packages) {
+ protected ProgramPackageCollection(Map<String, ProgramPackage> packages) {
this.packages = packages;
}
diff --git a/src/main/java/com/android/tools/r8/graph/SortedProgramPackage.java b/src/main/java/com/android/tools/r8/graph/SortedProgramPackage.java
new file mode 100644
index 0000000..97afd09
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/SortedProgramPackage.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+import java.util.TreeSet;
+
+public class SortedProgramPackage extends ProgramPackage {
+
+ public SortedProgramPackage(String packageDescriptor) {
+ super(packageDescriptor, () -> new TreeSet<>((a, b) -> a.getType().slowCompareTo(b.getType())));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/SortedProgramPackageCollection.java b/src/main/java/com/android/tools/r8/graph/SortedProgramPackageCollection.java
new file mode 100644
index 0000000..fdaff05
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/SortedProgramPackageCollection.java
@@ -0,0 +1,30 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+import java.util.TreeMap;
+
+public class SortedProgramPackageCollection extends ProgramPackageCollection {
+
+ private SortedProgramPackageCollection() {
+ super(new TreeMap<>());
+ }
+
+ public static SortedProgramPackageCollection createWithAllProgramClasses(AppView<?> appView) {
+ assert !appView.appInfo().getSyntheticItems().hasPendingSyntheticClasses();
+ SortedProgramPackageCollection programPackages = new SortedProgramPackageCollection();
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ programPackages.addProgramClass(clazz);
+ }
+ return programPackages;
+ }
+
+ @Override
+ public boolean addProgramClass(DexProgramClass clazz) {
+ return packages
+ .computeIfAbsent(clazz.getType().getPackageDescriptor(), SortedProgramPackage::new)
+ .add(clazz);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
index 75e6565..5830357 100644
--- a/src/main/java/com/android/tools/r8/graph/UseRegistry.java
+++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -17,6 +17,10 @@
this.factory = factory;
}
+ public final void accept(ProgramMethod method) {
+ method.registerCodeReferences(this);
+ }
+
public abstract void registerInitClass(DexType type);
public abstract void registerInvokeVirtual(DexMethod method);
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 1f751ad..c80e363 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -21,10 +21,12 @@
import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.Iterables;
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.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -72,6 +74,19 @@
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());
+ }
+
void buildClassIdentifierMap() {
classIdentifiers.put(target.type, 0);
for (DexProgramClass toMerge : toMergeGroup) {
@@ -114,9 +129,10 @@
tryMethod -> target.lookupMethod(tryMethod) == null);
}
- void mergeConstructors() {
+ void mergeConstructors(SyntheticArgumentClass syntheticArgumentClass) {
for (ConstructorMerger merger : constructorMergers) {
- merger.merge(lensBuilder, fieldAccessChangesBuilder, classIdentifiers);
+ merger.merge(
+ lensBuilder, fieldAccessChangesBuilder, classIdentifiers, syntheticArgumentClass);
}
}
@@ -137,7 +153,7 @@
target.appendInstanceField(encodedField);
}
- public void mergeGroup() {
+ public void mergeGroup(SyntheticArgumentClass syntheticArgumentClass) {
appendClassIdField();
for (DexProgramClass clazz : toMergeGroup) {
@@ -145,7 +161,7 @@
lensBuilder.mapType(clazz.type, target.type);
}
- mergeConstructors();
+ mergeConstructors(syntheticArgumentClass);
mergeVirtualMethods();
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPoint.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPoint.java
index ff2a898..732a5d2 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPoint.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPoint.java
@@ -78,7 +78,9 @@
protected void prepareMultiConstructorInstructions() {
int typeConstructorCount = typeConstructors.size();
- int idRegister = getParamRegister(method.getArity() - 1);
+ DexMethod exampleTargetConstructor = typeConstructors.values().iterator().next();
+ // The class id register is always the first synthetic argument.
+ int idRegister = getParamRegister(exampleTargetConstructor.getArity());
addRegisterClassIdAssignment(idRegister);
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 cd2d31c..2f3c068 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
@@ -16,6 +16,9 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
+import com.android.tools.r8.ir.conversion.ExtraConstantIntParameter;
+import com.android.tools.r8.ir.conversion.ExtraParameter;
+import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
@@ -23,6 +26,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.LinkedList;
import java.util.List;
public class ConstructorMerger {
@@ -90,8 +94,8 @@
return method;
}
- private DexProto getNewConstructorProto() {
- DexEncodedMethod firstConstructor = constructors.stream().findFirst().get();
+ private DexProto getNewConstructorProto(SyntheticArgumentClass syntheticArgumentClass) {
+ DexEncodedMethod firstConstructor = constructors.iterator().next();
DexProto oldProto = firstConstructor.getProto();
if (isTrivialMerge()) {
@@ -101,10 +105,18 @@
List<DexType> parameters = new ArrayList<>();
Collections.addAll(parameters, oldProto.parameters.values);
parameters.add(dexItemFactory.intType);
+ if (syntheticArgumentClass != null) {
+ parameters.add(syntheticArgumentClass.getArgumentClass());
+ }
// TODO(b/165783587): add synthesised class to prevent constructor merge conflict
return dexItemFactory.createProto(oldProto.returnType, parameters);
}
+ private DexMethod getNewConstructorReference(SyntheticArgumentClass syntheticArgumentClass) {
+ DexProto proto = getNewConstructorProto(syntheticArgumentClass);
+ return appView.dexItemFactory().createMethod(target.type, proto, dexItemFactory.initMethodName);
+ }
+
private MethodAccessFlags getAccessFlags() {
// TODO(b/164998929): ensure this behaviour is correct, should probably calculate upper bound
return MethodAccessFlags.fromSharedAccessFlags(
@@ -115,7 +127,8 @@
void merge(
HorizontalClassMergerGraphLens.Builder lensBuilder,
FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder,
- Reference2IntMap<DexType> classIdentifiers) {
+ Reference2IntMap<DexType> classIdentifiers,
+ SyntheticArgumentClass syntheticArgumentClass) {
// Tree map as must be sorted.
Int2ReferenceSortedMap<DexMethod> typeConstructorClassMap = new Int2ReferenceAVLTreeMap<>();
@@ -130,11 +143,15 @@
classIdentifiers.getInt(constructor.getHolderType()), movedConstructor);
}
- DexProto newProto = getNewConstructorProto();
+ DexMethod newConstructorReference = getNewConstructorReference(null);
+ boolean addExtraNull = target.lookupMethod(newConstructorReference) != null;
+ if (addExtraNull) {
+ newConstructorReference = getNewConstructorReference(syntheticArgumentClass);
+ assert target.lookupMethod(newConstructorReference) == null;
+ }
- DexMethod originalConstructorReference = constructors.iterator().next().method;
- DexMethod newConstructorReference =
- appView.dexItemFactory().createMethod(target.type, newProto, dexItemFactory.initMethodName);
+ DexMethod originalConstructorReference =
+ appView.graphLens().getOriginalMethodSignature(constructors.iterator().next().method);
ConstructorEntryPointSynthesizedCode synthesizedCode =
new ConstructorEntryPointSynthesizedCode(
typeConstructorClassMap,
@@ -158,10 +175,16 @@
} else {
// Map each old constructor to the newly synthesized constructor in the graph lens.
for (DexEncodedMethod oldConstructor : constructors) {
+ int classIdentifier = classIdentifiers.getInt(oldConstructor.getHolderType());
+
+ List<ExtraParameter> extraParameters = new LinkedList<>();
+ extraParameters.add(new ExtraConstantIntParameter(classIdentifier));
+ if (addExtraNull) {
+ extraParameters.add(new ExtraUnusedNullParameter());
+ }
+
lensBuilder.mapMergedConstructor(
- oldConstructor.method,
- newConstructorReference,
- classIdentifiers.getInt(oldConstructor.getHolderType()));
+ oldConstructor.method, newConstructorReference, extraParameters);
}
}
// Map the first constructor to the newly synthesized constructor.
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 8b17c78..c993870 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -6,6 +6,8 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.horizontalclassmerging.policies.DontMergeSynchronizedClasses;
import com.android.tools.r8.horizontalclassmerging.policies.NoAnnotations;
import com.android.tools.r8.horizontalclassmerging.policies.NoFields;
import com.android.tools.r8.horizontalclassmerging.policies.NoInnerClasses;
@@ -15,6 +17,7 @@
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.PreventMergeIntoMainDex;
import com.android.tools.r8.horizontalclassmerging.policies.RespectPackageBoundaries;
import com.android.tools.r8.horizontalclassmerging.policies.SameParentClass;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -22,6 +25,7 @@
import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
import com.android.tools.r8.shaking.MainDexTracingResult;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -34,7 +38,7 @@
public HorizontalClassMerger(
AppView<AppInfoWithLiveness> appView,
- MainDexTracingResult mainDexClasses,
+ MainDexTracingResult mainDexTracingResult,
ClassMergingEnqueuerExtension classMergingEnqueuerExtension) {
this.appView = appView;
@@ -50,8 +54,10 @@
new NoKeepRules(appView),
new NoRuntimeTypeChecks(classMergingEnqueuerExtension),
new NotEntryPoint(appView.dexItemFactory()),
+ new PreventMergeIntoMainDex(appView, mainDexTracingResult),
new SameParentClass(),
- new RespectPackageBoundaries(appView)
+ new RespectPackageBoundaries(appView),
+ new DontMergeSynchronizedClasses(appView)
// TODO: add policies
);
@@ -59,7 +65,7 @@
}
// TODO(b/165577835): replace Collection<DexProgramClass> with MergeGroup
- public HorizontalClassMergerGraphLens run() {
+ public HorizontalClassMergerGraphLens run(DirectMappedDexApplication.Builder appBuilder) {
Map<FieldMultiset, Collection<DexProgramClass>> classes = new HashMap<>();
// Group classes by same field signature using the hash map.
@@ -69,6 +75,10 @@
// Run the policies on all collected classes to produce a final grouping.
Collection<Collection<DexProgramClass>> groups = policyExecutor.run(classes.values());
+ // If there are no groups, then end horizontal class merging.
+ if (groups.isEmpty()) {
+ return null;
+ }
HorizontalClassMergerGraphLens.Builder lensBuilder =
new HorizontalClassMergerGraphLens.Builder();
@@ -78,9 +88,13 @@
// Set up a class merger for each group.
Collection<ClassMerger> classMergers =
initializeClassMergers(lensBuilder, fieldAccessChangesBuilder, groups);
+ Iterable<DexProgramClass> allMergeClasses =
+ Iterables.concat(Iterables.transform(classMergers, ClassMerger::getClasses));
// Merge the classes.
- applyClassMergers(classMergers);
+ SyntheticArgumentClass syntheticArgumentClass =
+ new SyntheticArgumentClass.Builder().build(appView, appBuilder, allMergeClasses);
+ applyClassMergers(classMergers, syntheticArgumentClass);
// Generate the class lens.
return createLens(lensBuilder, fieldAccessChangesBuilder);
@@ -114,9 +128,10 @@
}
/** Merges all class groups using {@link ClassMerger}. */
- private void applyClassMergers(Collection<ClassMerger> classMergers) {
+ private void applyClassMergers(
+ Collection<ClassMerger> classMergers, SyntheticArgumentClass syntheticArgumentClass) {
for (ClassMerger merger : classMergers) {
- merger.mergeGroup();
+ merger.mergeGroup(syntheticArgumentClass);
}
}
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 b0e8d13..395409f 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
@@ -10,24 +10,24 @@
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.ir.code.Invoke.Type;
-import com.android.tools.r8.ir.conversion.ExtraConstantIntParameter;
+import com.android.tools.r8.ir.conversion.ExtraParameter;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import java.util.Collections;
import java.util.HashSet;
import java.util.IdentityHashMap;
+import java.util.List;
import java.util.Map;
import java.util.Set;
public class HorizontalClassMergerGraphLens extends NestedGraphLens {
private final AppView<?> appView;
- private final Map<DexMethod, Integer> constructorIds;
+ private final Map<DexMethod, List<ExtraParameter>> methodExtraParameters;
private final Map<DexMethod, DexMethod> originalConstructorSignatures;
private HorizontalClassMergerGraphLens(
AppView<?> appView,
- Map<DexMethod, Integer> constructorIds,
+ Map<DexMethod, List<ExtraParameter>> methodExtraParameters,
Map<DexType, DexType> typeMap,
Map<DexField, DexField> fieldMap,
Map<DexMethod, DexMethod> methodMap,
@@ -44,7 +44,7 @@
previousLens,
appView.dexItemFactory());
this.appView = appView;
- this.constructorIds = constructorIds;
+ this.methodExtraParameters = methodExtraParameters;
this.originalConstructorSignatures = originalConstructorSignatures;
}
@@ -54,7 +54,7 @@
if (originalConstructor == null) {
return super.getOriginalMethodSignature(method);
}
- return previousLens.getOriginalMethodSignature(originalConstructor);
+ return getPrevious().getOriginalMethodSignature(originalConstructor);
}
public HorizontallyMergedClasses getHorizontallyMergedClasses() {
@@ -66,20 +66,18 @@
* constructor. Otherwise return the lookup on the underlying graph lens.
*/
@Override
- public GraphLensLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
- Integer constructorId = constructorIds.get(method);
- GraphLensLookupResult lookup = super.lookupMethod(method, context, type);
- if (constructorId != null) {
- DexMethod newMethod = lookup.getMethod();
- return new GraphLensLookupResult(
- newMethod,
- mapInvocationType(newMethod, method, lookup.getType()),
- lookup
- .getPrototypeChanges()
- .withExtraParameter(new ExtraConstantIntParameter(constructorId)));
- } else {
+ public MethodLookupResult internalDescribeLookupMethod(
+ MethodLookupResult previous, DexMethod context) {
+ List<ExtraParameter> extraParameters = methodExtraParameters.get(previous.getReference());
+ MethodLookupResult lookup = super.internalDescribeLookupMethod(previous, context);
+ if (extraParameters == null) {
return lookup;
}
+ return MethodLookupResult.builder(this)
+ .setReference(lookup.getReference())
+ .setPrototypeChanges(lookup.getPrototypeChanges().withExtraParameters(extraParameters))
+ .setType(lookup.getType())
+ .build();
}
public static class Builder {
@@ -91,26 +89,25 @@
private final BiMap<DexMethod, DexMethod> originalMethodSignatures = HashBiMap.create();
private final Map<DexMethod, DexMethod> extraOriginalMethodSignatures = new IdentityHashMap<>();
- private final Map<DexMethod, Integer> constructorIds = new IdentityHashMap<>();
+ private final Map<DexMethod, List<ExtraParameter>> methodExtraParameters =
+ new IdentityHashMap<>();
Builder() {}
public HorizontalClassMergerGraphLens build(AppView<?> appView) {
- if (typeMap.isEmpty()) {
- return null;
- } else {
- BiMap<DexField, DexField> originalFieldSignatures = fieldMap.inverse();
- return new HorizontalClassMergerGraphLens(
- appView,
- constructorIds,
- typeMap,
- fieldMap,
- methodMap,
- originalFieldSignatures,
- originalMethodSignatures,
- extraOriginalMethodSignatures,
- appView.graphLens());
- }
+ assert !typeMap.isEmpty();
+
+ BiMap<DexField, DexField> originalFieldSignatures = fieldMap.inverse();
+ return new HorizontalClassMergerGraphLens(
+ appView,
+ methodExtraParameters,
+ typeMap,
+ fieldMap,
+ methodMap,
+ originalFieldSignatures,
+ originalMethodSignatures,
+ extraOriginalMethodSignatures,
+ appView.graphLens());
}
public DexType lookupType(DexType type) {
@@ -186,13 +183,11 @@
* One way mapping from one constructor to another. This is used for synthesized constructors,
* where many constructors are merged into a single constructor. The synthesized constructor
* therefore does not have a unique reverse constructor.
- *
- * @param constructorId The id that must be appended to the constructor call to ensure the
- * correct constructor is called.
*/
- public Builder mapMergedConstructor(DexMethod from, DexMethod to, int constructorId) {
+ public Builder mapMergedConstructor(
+ DexMethod from, DexMethod to, List<ExtraParameter> extraParameters) {
mapMethod(from, to);
- constructorIds.put(from, constructorId);
+ methodExtraParameters.put(from, extraParameters);
return this;
}
}
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 5c67423..829c1f3 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/MultiClassPolicy.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/MultiClassPolicy.java
@@ -10,12 +10,17 @@
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 void removeTrivialGroups(Collection<Collection<DexProgramClass>> groups) {
assert !(groups instanceof ArrayList);
- groups.removeIf(group -> group.size() < 2);
+ groups.removeIf(this::isTrivial);
}
/**
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
new file mode 100644
index 0000000..db92416
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
@@ -0,0 +1,92 @@
+// 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.dex.Constants;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ClassAccessFlags;
+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.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.origin.SynthesizedOrigin;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.Collections;
+
+/**
+ * Lets assume we are merging a class A that looks like:
+ *
+ * <pre>
+ * class A {
+ * A() { ... }
+ * A(int) { ... }
+ * }
+ * </pre>
+ *
+ * If {@code A::<init>()} is merged with other constructors, then we need to prevent a conflict with
+ * {@code A::<init>(int)}. To do this we introduce a synthetic class so that the new signature for
+ * the merged constructor is {@code A::<init>(SyntheticClass, int)}, as this is guaranteed to be
+ * unique.
+ *
+ * <p>This class generates a synthetic class in the package of the first class to be merged.
+ */
+public class SyntheticArgumentClass {
+ public static final String SYNTHETIC_CLASS_SUFFIX = "$r8$HorizontalClassMergingArgument";
+
+ private final DexType syntheticClassType;
+
+ private SyntheticArgumentClass(DexType syntheticClassType) {
+ this.syntheticClassType = syntheticClassType;
+ }
+
+ public DexType getArgumentClass() {
+ return syntheticClassType;
+ }
+
+ public static class Builder {
+
+ 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();
+ DexType syntheticClassType =
+ context.type.addSuffix(SYNTHETIC_CLASS_SUFFIX, appView.dexItemFactory());
+
+ boolean requiresMainDex = appView.appInfo().getMainDexClasses().containsAnyOf(mergeClasses);
+
+ DexProgramClass clazz =
+ new DexProgramClass(
+ syntheticClassType,
+ null,
+ new SynthesizedOrigin("horizontal class merging", HorizontalClassMerger.class),
+ ClassAccessFlags.fromCfAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC),
+ appView.dexItemFactory().objectType,
+ DexTypeList.empty(),
+ null,
+ null,
+ Collections.emptyList(),
+ null,
+ Collections.emptyList(),
+ DexAnnotationSet.empty(),
+ DexEncodedField.EMPTY_ARRAY,
+ DexEncodedField.EMPTY_ARRAY,
+ DexEncodedMethod.EMPTY_ARRAY,
+ DexEncodedMethod.EMPTY_ARRAY,
+ appView.dexItemFactory().getSkipNameValidationForTesting(),
+ DexProgramClass::checksumFromType);
+
+ appBuilder.addSynthesizedClass(clazz);
+ appView.appInfo().addSynthesizedClass(clazz, requiresMainDex);
+
+ return new SyntheticArgumentClass(clazz.type);
+ }
+ }
+}
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 087c6ef..a35db17 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -50,12 +50,8 @@
fixupFields(clazz.instanceFields(), clazz::setInstanceField);
}
HorizontalClassMergerGraphLens lens = lensBuilder.build(appView);
-
fieldAccessChangesBuilder.build(this::fixupMethod).modify(appView);
-
- if (lens != null) {
- new AnnotationFixer(lens).run(appView.appInfo().classes());
- }
+ new AnnotationFixer(lens).run(appView.appInfo().classes());
return lens;
}
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 5e20c04f..fd998a5 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -150,7 +150,8 @@
}
// Use the first of the original methods as the original method for the merged constructor.
- DexMethod originalMethodReference = methods.iterator().next().getReference();
+ DexMethod originalMethodReference =
+ appView.graphLens().getOriginalMethodSignature(methods.iterator().next().getReference());
DexMethod newMethodReference =
dexItemFactory.createMethod(
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
new file mode 100644
index 0000000..958e660
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontMergeSynchronizedClasses.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.horizontalclassmerging.policies;
+
+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.shaking.AppInfoWithLiveness;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+public class DontMergeSynchronizedClasses extends MultiClassPolicy {
+ private final AppView<AppInfoWithLiveness> appView;
+
+ public DontMergeSynchronizedClasses(AppView<AppInfoWithLiveness> appView) {
+ this.appView = appView;
+ }
+
+ private boolean isSynchronizationClass(DexProgramClass clazz) {
+ return appView.appInfo().isLockCandidate(clazz.type) || clazz.hasStaticSynchronizedMethods();
+ }
+
+ @Override
+ public Collection<Collection<DexProgramClass>> apply(Collection<DexProgramClass> group) {
+ // Gather all synchronized classes.
+ Collection<Collection<DexProgramClass>> synchronizedGroups = new LinkedList<>();
+ group.removeIf(
+ clazz -> {
+ boolean synchronizationClass = isSynchronizationClass(clazz);
+ if (synchronizationClass) {
+ Collection<DexProgramClass> synchronizedGroup = new LinkedList<>();
+ synchronizedGroup.add(clazz);
+ synchronizedGroups.add(synchronizedGroup);
+ }
+ return synchronizationClass;
+ });
+
+ if (synchronizedGroups.isEmpty()) {
+ return Collections.singletonList(group);
+ }
+
+ Iterator<Collection<DexProgramClass>> synchronizedGroupIterator = synchronizedGroups.iterator();
+ for (DexProgramClass clazz : group) {
+ if (!synchronizedGroupIterator.hasNext()) {
+ synchronizedGroupIterator = synchronizedGroups.iterator();
+ }
+ synchronizedGroupIterator.next().add(clazz);
+ }
+
+ removeTrivialGroups(synchronizedGroups);
+
+ return synchronizedGroups;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoMainDex.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoMainDex.java
new file mode 100644
index 0000000..f17e923
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoMainDex.java
@@ -0,0 +1,54 @@
+// 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.MultiClassPolicy;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.MainDexClasses;
+import com.android.tools.r8.shaking.MainDexTracingResult;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+public class PreventMergeIntoMainDex extends MultiClassPolicy {
+ private final MainDexClasses mainDexClasses;
+ private final MainDexTracingResult mainDexTracingResult;
+
+ public PreventMergeIntoMainDex(
+ AppView<AppInfoWithLiveness> appView, MainDexTracingResult mainDexTracingResult) {
+ this.mainDexClasses = appView.appInfo().getMainDexClasses();
+ this.mainDexTracingResult = mainDexTracingResult;
+ }
+
+ public boolean isMainDexClass(DexProgramClass clazz) {
+ return mainDexClasses.contains(clazz) || mainDexTracingResult.contains(clazz);
+ }
+
+ @Override
+ public Collection<Collection<DexProgramClass>> apply(Collection<DexProgramClass> group) {
+ List<DexProgramClass> mainDexMembers = new LinkedList<>();
+ Iterator<DexProgramClass> iterator = group.iterator();
+ while (iterator.hasNext()) {
+ DexProgramClass clazz = iterator.next();
+ if (isMainDexClass(clazz)) {
+ iterator.remove();
+ mainDexMembers.add(clazz);
+ }
+ }
+
+ Collection<Collection<DexProgramClass>> newGroups = new LinkedList<>();
+ if (!isTrivial(mainDexMembers)) {
+ newGroups.add(mainDexMembers);
+ }
+ if (!isTrivial(group)) {
+ newGroups.add(group);
+ }
+
+ return newGroups;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
index e71ef7d..edd1b91 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
@@ -16,6 +16,7 @@
import java.util.Collections;
import java.util.HashSet;
import java.util.IdentityHashMap;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
@@ -132,7 +133,10 @@
builder.append(" {");
Set<DexType> interfaces = getInterfaces();
if (interfaces != null) {
- builder.append(interfaces.stream().map(DexType::toString).collect(Collectors.joining(", ")));
+ List<DexType> sortedInterfaces = new ArrayList<>(interfaces);
+ sortedInterfaces.sort(DexType::slowCompareTo);
+ builder.append(
+ sortedInterfaces.stream().map(DexType::toString).collect(Collectors.joining(", ")));
}
builder.append("}");
return builder.toString();
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 020254f..6d452e1 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
@@ -166,7 +166,7 @@
if (instructionMayHaveSideEffects(appView, context)) {
return DeadInstructionResult.notDead();
}
- if (!getInvokedMethod().isInstanceInitializer(appView)) {
+ if (!getInvokedMethod().isInstanceInitializer(appView.dexItemFactory())) {
return DeadInstructionResult.deadIfOutValueIsDead();
}
// Super-constructor calls cannot be removed.
diff --git a/src/main/java/com/android/tools/r8/ir/code/Position.java b/src/main/java/com/android/tools/r8/ir/code/Position.java
index 183d352..6cc4f93 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Position.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Position.java
@@ -8,9 +8,10 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.ProgramMethod;
import com.google.common.annotations.VisibleForTesting;
+import java.util.Comparator;
import java.util.Objects;
-public class Position {
+public class Position implements Comparable<Position> {
// A no-position marker. Not having a position means the position is implicitly defined by the
// context, e.g., the marker does not materialize anything concrete.
@@ -114,18 +115,7 @@
@Override
public boolean equals(Object other) {
- if (this == other) {
- return true;
- }
- if (other instanceof Position) {
- Position o = (Position) other;
- return line == o.line
- && file == o.file
- && method == o.method
- && synthetic == o.synthetic
- && Objects.equals(callerPosition, o.callerPosition);
- }
- return false;
+ return other instanceof Position && compareTo((Position) other) == 0;
}
@Override
@@ -138,6 +128,19 @@
return result;
}
+ @Override
+ public int compareTo(Position o) {
+ if (this == o) {
+ return 0;
+ }
+ return Comparator.comparingInt((Position p) -> p.line)
+ .thenComparing(p -> p.file, Comparator.nullsFirst(DexString::slowCompareTo))
+ .thenComparing(p -> p.synthetic)
+ .thenComparing(p -> p.method, DexMethod::slowCompareTo)
+ .thenComparing(p -> p.callerPosition, Comparator.nullsFirst(Position::compareTo))
+ .compare(this, o);
+ }
+
private String toString(boolean forceMethod) {
if (isNone()) {
return "--";
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 f5a51c8..6756693 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
@@ -18,7 +18,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldAccessInfo;
import com.android.tools.r8.graph.FieldAccessInfoCollection;
-import com.android.tools.r8.graph.GraphLens.GraphLensLookupResult;
+import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
import com.android.tools.r8.graph.LookupResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ResolutionResult;
@@ -166,9 +166,9 @@
private void processInvoke(Invoke.Type originalType, DexMethod originalMethod) {
ProgramMethod context = currentMethod.getProgramMethod();
- GraphLensLookupResult result =
+ MethodLookupResult result =
appView.graphLens().lookupMethod(originalMethod, context.getReference(), originalType);
- DexMethod method = result.getMethod();
+ DexMethod method = result.getReference();
Invoke.Type type = result.getType();
if (type == Invoke.Type.INTERFACE || type == Invoke.Type.VIRTUAL) {
// For virtual and interface calls add all potential targets that could be called.
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 9055230..47db073 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
@@ -59,7 +59,6 @@
import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
import java.util.ArrayDeque;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
@@ -165,6 +164,7 @@
DexBuilder.removeRedundantDebugPositions(code);
CfCode code = buildCfCode();
assert verifyInvokeInterface(code, appView);
+ assert code.verifyFrames(method, appView, this.code.origin, false);
return code;
}
@@ -530,13 +530,14 @@
private void addFrame(BasicBlock block) {
List<TypeInfo> stack = registerAllocator.getTypesAtBlockEntry(block).stack;
- List<FrameType> stackTypes;
+ Deque<FrameType> stackTypes;
if (block.entry().isMoveException()) {
assert stack.isEmpty();
StackValue exception = (StackValue) block.entry().outValue();
- stackTypes = Collections.singletonList(getFrameType(block, exception.getTypeInfo()));
+ stackTypes = new ArrayDeque<>();
+ stackTypes.add(getFrameType(block, exception.getTypeInfo()));
} else {
- stackTypes = new ArrayList<>(stack.size());
+ stackTypes = new ArrayDeque<>(stack.size());
for (TypeInfo typeInfo : stack) {
stackTypes.add(getFrameType(block, typeInfo));
}
@@ -577,8 +578,11 @@
FrameType res;
Instruction definition;
if (typeInfo instanceof NewInstanceInfo) {
- definition = ((NewInstanceInfo) typeInfo).newInstance;
- res = FrameType.uninitializedNew(newInstanceLabels.get(definition));
+ NewInstanceInfo newInstanceInfo = (NewInstanceInfo) typeInfo;
+ definition = newInstanceInfo.newInstance;
+ res =
+ FrameType.uninitializedNew(
+ newInstanceLabels.get(definition), newInstanceInfo.getDexType());
} else if (typeInfo instanceof ThisInstanceInfo) {
definition = ((ThisInstanceInfo) typeInfo).thisArgument;
res = FrameType.uninitializedThis();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
index d4c8040..f7e8a90 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
@@ -292,7 +292,11 @@
// This is the only instruction that differ in throwing between DEX and CF. If we find more
// consider rewriting CfInstruction.canThrow to take in options.
private boolean canThrowHelper(CfInstruction instruction) {
- if (internalOutputMode.isGeneratingClassFiles()
+ return canThrowHelper(instruction, internalOutputMode.isGeneratingClassFiles());
+ }
+
+ public static boolean canThrowHelper(CfInstruction instruction, boolean isGeneratingClassFiles) {
+ if (isGeneratingClassFiles
&& (instruction.isConstString() || instruction.isDexItemBasedConstString())) {
return false;
}
@@ -605,8 +609,9 @@
for (Int2ReferenceMap.Entry<FrameType> entry : frameLocals.int2ReferenceEntrySet()) {
locals[entry.getIntKey()] = convertUninitialized(entry.getValue());
}
- for (int i = 0; i < stack.length; i++) {
- stack[i] = convertUninitialized(frame.getStack().get(i));
+ int index = 0;
+ for (FrameType frameType : frame.getStack()) {
+ stack[index++] = convertUninitialized(frameType);
}
state.setStateFromFrame(
locals, stack, getCanonicalDebugPositionAtOffset(currentInstructionIndex));
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 605f698..32da612 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
@@ -592,6 +592,9 @@
public DexApplication optimize(
AppView<AppInfoWithLiveness> appView, ExecutorService executorService)
throws ExecutionException {
+ // Lambda rewriting happens in the enqueuer.
+ assert lambdaRewriter == null;
+
DexApplication application = appView.appInfo().app();
computeReachabilitySensitivity(application);
@@ -702,9 +705,6 @@
Builder<?> builder = appView.appInfo().app().builder();
builder.setHighestSortingString(highestSortingString);
- printPhase("Lambda class synthesis");
- synthesizeLambdaClasses(builder, executorService);
-
printPhase("Interface method desugaring");
desugarInterfaceMethods(builder, IncludeAllResources, executorService);
feedback.updateVisibleOptimizationInfo();
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 576a376..8b827d3 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
@@ -42,7 +42,7 @@
import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.GraphLens.GraphLensLookupResult;
+import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.RewrittenPrototypeDescription;
import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfo;
@@ -214,9 +214,9 @@
if (invoke.isInvokeDirect()) {
checkInvokeDirect(method.getReference(), invoke.asInvokeDirect());
}
- GraphLensLookupResult lensLookup =
+ MethodLookupResult lensLookup =
graphLens.lookupMethod(invokedMethod, method.getReference(), invoke.getType());
- DexMethod actualTarget = lensLookup.getMethod();
+ DexMethod actualTarget = lensLookup.getReference();
Invoke.Type actualInvokeType = lensLookup.getType();
if (actualTarget != invokedMethod || invoke.getType() != actualInvokeType) {
RewrittenPrototypeDescription prototypeChanges = lensLookup.getPrototypeChanges();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
index 27b3064..4b44ada 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
@@ -23,7 +23,7 @@
import com.android.tools.r8.graph.DexValue.DexValueMethodType;
import com.android.tools.r8.graph.DexValue.DexValueType;
import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.GraphLens.GraphLensLookupResult;
+import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.UseRegistry.MethodHandleUse;
import java.util.ArrayList;
@@ -73,9 +73,9 @@
if (methodHandle.isMethodHandle()) {
DexMethod invokedMethod = methodHandle.asMethod();
MethodHandleType oldType = methodHandle.type;
- GraphLensLookupResult lensLookup =
+ MethodLookupResult lensLookup =
graphLens.lookupMethod(invokedMethod, context.getReference(), oldType.toInvokeType());
- DexMethod rewrittenTarget = lensLookup.getMethod();
+ DexMethod rewrittenTarget = lensLookup.getReference();
DexMethod actualTarget;
MethodHandleType newType;
if (use == ARGUMENT_TO_LAMBDA_METAFACTORY) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
index d687d19..7586210 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
@@ -19,6 +19,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.function.Consumer;
public class DesugaredLibraryConfigurationParser {
@@ -77,6 +78,12 @@
}
public DesugaredLibraryConfiguration parse(StringResource stringResource) {
+ return parse(stringResource, builder -> {});
+ }
+
+ public DesugaredLibraryConfiguration parse(
+ StringResource stringResource,
+ Consumer<DesugaredLibraryConfiguration.Builder> configurationAmender) {
origin = stringResource.getOrigin();
assert origin != null;
configurationBuilder = DesugaredLibraryConfiguration.builder(dexItemFactory, reporter, origin);
@@ -142,6 +149,7 @@
}
configurationBuilder.setExtraKeepRules(extraKeepRules);
}
+ configurationAmender.accept(configurationBuilder);
DesugaredLibraryConfiguration config = configurationBuilder.build();
configurationBuilder = null;
origin = null;
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 a792f06..67d5b2a 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
@@ -415,8 +415,8 @@
if (lens.isIdentityLens()) {
return null;
}
- if (lens.isGraphLensWithPrevious()) {
- return find(lens.asGraphLensWithPrevious().getPrevious());
+ if (lens.isNonIdentityLens()) {
+ return find(lens.asNonIdentityLens().getPrevious());
}
assert false;
return null;
@@ -452,7 +452,7 @@
? originalMethodSignatures.getOrDefault(method, method)
: method;
}
- return previousLens.getOriginalMethodSignature(originalMethod);
+ return getPrevious().getOriginalMethodSignature(originalMethod);
}
@Override
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 fad4980..2faa07a 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
@@ -354,14 +354,20 @@
for (DexProgramClass clazz : appView.appInfo().classes()) {
EnclosingMethodAttribute enclosingMethod = clazz.getEnclosingMethodAttribute();
if (enclosingMethod != null) {
- DexMethod mappedEnclosingMethod = lens.lookupMethod(enclosingMethod.getEnclosingMethod());
- if (mappedEnclosingMethod != null
- && mappedEnclosingMethod != enclosingMethod.getEnclosingMethod()) {
- clazz.setEnclosingMethodAttribute(new EnclosingMethodAttribute(mappedEnclosingMethod));
+ if (enclosingMethod.getEnclosingMethod() != null) {
+ DexMethod mappedEnclosingMethod = lens.lookupMethod(enclosingMethod.getEnclosingMethod());
+ if (mappedEnclosingMethod != enclosingMethod.getEnclosingMethod()) {
+ clazz.setEnclosingMethodAttribute(new EnclosingMethodAttribute(mappedEnclosingMethod));
+ }
+ } else {
+ assert enclosingMethod.getEnclosingClass() != null;
+ DexType mappedEnclosingClass = lens.lookupType(enclosingMethod.getEnclosingClass());
+ if (mappedEnclosingClass != enclosingMethod.getEnclosingClass()) {
+ clazz.setEnclosingMethodAttribute(new EnclosingMethodAttribute(mappedEnclosingClass));
+ }
}
}
}
- ;
// Return lens without method map (but still retaining originalMethodSignatures), as the
// generated lambdas classes are generated with the an invoke to the new method, so no
// code rewriting is required.
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 8370326..89dc96c 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
@@ -87,7 +87,7 @@
private DexEncodedMethod lookupOnHolder(
DexMethod method, DexClassAndMethod context, Invoke.Type invokeType) {
DexMethod rewritten =
- appView.graphLens().lookupMethod(method, context.getReference(), invokeType).getMethod();
+ appView.graphLens().lookupMethod(method, context.getReference(), invokeType).getReference();
return rewritten.lookupOnClass(appView.definitionForHolder(rewritten));
}
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 9e429a4..7bc5e6b 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
@@ -11,7 +11,7 @@
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.code.Invoke;
+import com.android.tools.r8.ir.code.Invoke.Type;
import com.google.common.collect.ImmutableMap;
import java.util.IdentityHashMap;
import java.util.Map;
@@ -61,13 +61,13 @@
@Override
public DexMethod lookupGetFieldForMethod(DexField field, DexMethod context) {
- assert previousLens.lookupGetFieldForMethod(field, context) == null;
+ assert getPrevious().lookupGetFieldForMethod(field, context) == null;
return lookupFieldForMethod(field, context, getFieldMap);
}
@Override
public DexMethod lookupPutFieldForMethod(DexField field, DexMethod context) {
- assert previousLens.lookupPutFieldForMethod(field, context) == null;
+ assert getPrevious().lookupPutFieldForMethod(field, context) == null;
return lookupFieldForMethod(field, context, putFieldMap);
}
@@ -78,7 +78,7 @@
@Override
public boolean verifyIsContextFreeForMethod(DexMethod method) {
- assert !methodMap.containsKey(previousLens.lookupMethod(method));
+ assert !methodMap.containsKey(getPrevious().lookupMethod(method));
return true;
}
@@ -110,24 +110,28 @@
}
@Override
- public GraphLensLookupResult lookupMethod(DexMethod method, DexMethod context, Invoke.Type type) {
+ public MethodLookupResult internalDescribeLookupMethod(
+ MethodLookupResult previous, DexMethod context) {
assert originalMethodSignatures == null;
- GraphLensLookupResult lookup = previousLens.lookupMethod(method, context, type);
- DexMethod bridge = methodMap.get(lookup.getMethod());
+ DexMethod bridge = methodMap.get(previous.getReference());
if (bridge == null) {
- return lookup;
+ return previous;
}
assert context != null : "Guaranteed by isContextFreeForMethod";
if (bridge.holder == context.holder) {
- return lookup;
+ return previous;
}
+ MethodLookupResult.Builder resultBuilder =
+ MethodLookupResult.builder(this).setReference(bridge);
if (isConstructorBridge(bridge)) {
- return new GraphLensLookupResult(
- bridge,
- Invoke.Type.DIRECT,
- internalDescribePrototypeChanges(lookup.getPrototypeChanges(), bridge));
+ resultBuilder
+ .setPrototypeChanges(
+ internalDescribePrototypeChanges(previous.getPrototypeChanges(), bridge))
+ .setType(Type.DIRECT);
+ } else {
+ resultBuilder.setPrototypeChanges(previous.getPrototypeChanges()).setType(Type.STATIC);
}
- return new GraphLensLookupResult(bridge, Invoke.Type.STATIC, lookup.getPrototypeChanges());
+ return resultBuilder.build();
}
public static Builder builder() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ServiceLoaderSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/ServiceLoaderSourceCode.java
index f0b74ea..8f0ddf7 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ServiceLoaderSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ServiceLoaderSourceCode.java
@@ -11,6 +11,8 @@
import com.android.tools.r8.cf.code.CfArrayStore;
import com.android.tools.r8.cf.code.CfConstNumber;
+import com.android.tools.r8.cf.code.CfFrame;
+import com.android.tools.r8.cf.code.CfFrame.FrameType;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.cf.code.CfLabel;
@@ -28,6 +30,8 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.utils.collections.ImmutableDeque;
+import com.android.tools.r8.utils.collections.ImmutableInt2ReferenceSortedMap;
import com.google.common.collect.ImmutableList;
import java.util.List;
@@ -85,6 +89,9 @@
CfLabel tryCatchHandler = new CfLabel();
builder.add(
tryCatchHandler,
+ new CfFrame(
+ ImmutableInt2ReferenceSortedMap.empty(),
+ ImmutableDeque.of(FrameType.initialized(factory.throwableType))),
new CfStore(ValueType.OBJECT, 0),
new CfNew(factory.serviceLoaderConfigurationErrorType),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
index d28ee6d..da56b69 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
@@ -48,6 +48,7 @@
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableList;
import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
+import java.util.ArrayDeque;
import java.util.Arrays;
public final class BackportedMethods {
@@ -133,7 +134,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 0),
new CfIf(If.Type.EQ, ValueType.INT, label2),
new CfConstNumber(1, ValueType.INT),
@@ -146,7 +147,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(-1, ValueType.INT),
label3,
new CfFrame(
@@ -156,7 +157,8 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfReturn(ValueType.INT),
label4),
ImmutableList.of(),
@@ -183,14 +185,15 @@
new Int2ReferenceAVLTreeMap<>(
new int[] {0},
new FrameType[] {FrameType.initialized(options.itemFactory.intType)}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(1237, ValueType.INT),
label2,
new CfFrame(
new Int2ReferenceAVLTreeMap<>(
new int[] {0},
new FrameType[] {FrameType.initialized(options.itemFactory.intType)}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfReturn(ValueType.INT),
label3),
ImmutableList.of(),
@@ -331,7 +334,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(0, ValueType.INT),
new CfStore(ValueType.INT, 4),
label5,
@@ -360,7 +363,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 4),
new CfLoad(ValueType.INT, 5),
new CfIfCmp(If.Type.GE, ValueType.INT, label12),
@@ -409,7 +412,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfIinc(4, 1),
new CfGoto(label6),
label12,
@@ -422,7 +425,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 2),
new CfLoad(ValueType.INT, 3),
new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
@@ -533,7 +536,7 @@
FrameType.initialized(options.itemFactory.throwableType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 1),
new CfInvoke(
182,
@@ -582,9 +585,10 @@
FrameType.initialized(options.itemFactory.throwableType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList(
- FrameType.initialized(
- options.itemFactory.createType("Ljava/lang/Exception;")))),
+ new ArrayDeque<>(
+ Arrays.asList(
+ FrameType.initialized(
+ options.itemFactory.createType("Ljava/lang/Exception;"))))),
new CfStore(ValueType.OBJECT, 2),
label6,
new CfNew(options.itemFactory.createType("Ljava/lang/AssertionError;")),
@@ -650,7 +654,8 @@
FrameType.initialized(options.itemFactory.throwableType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.throwableType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.throwableType)))),
new CfStore(ValueType.OBJECT, 2),
label8,
new CfNew(options.itemFactory.createType("Ljava/lang/AssertionError;")),
@@ -716,10 +721,11 @@
FrameType.initialized(options.itemFactory.throwableType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList(
- FrameType.initialized(
- options.itemFactory.createType(
- "Ljava/lang/reflect/InvocationTargetException;")))),
+ new ArrayDeque<>(
+ Arrays.asList(
+ FrameType.initialized(
+ options.itemFactory.createType(
+ "Ljava/lang/reflect/InvocationTargetException;"))))),
new CfStore(ValueType.OBJECT, 2),
label10,
new CfLoad(ValueType.OBJECT, 2),
@@ -739,7 +745,7 @@
FrameType.initialized(options.itemFactory.throwableType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfGoto(label16),
label12,
new CfFrame(
@@ -749,7 +755,8 @@
FrameType.initialized(options.itemFactory.throwableType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.throwableType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.throwableType)))),
new CfStore(ValueType.OBJECT, 2),
label13,
new CfLoad(ValueType.OBJECT, 0),
@@ -765,7 +772,7 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.throwableType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 2),
label15,
new CfFrame(
@@ -776,7 +783,8 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.throwableType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.throwableType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.throwableType)))),
new CfThrow(),
label16,
new CfFrame(
@@ -786,7 +794,7 @@
FrameType.initialized(options.itemFactory.throwableType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfReturnVoid(),
label17),
ImmutableList.of(
@@ -881,7 +889,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 4),
new CfLoad(ValueType.INT, 3),
new CfIfCmp(If.Type.GE, ValueType.INT, label5),
@@ -920,7 +928,7 @@
FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
FrameType.initialized(options.itemFactory.createType("Ljava/util/ArrayList;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 1),
new CfInvoke(
184,
@@ -1040,7 +1048,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 4),
new CfLoad(ValueType.INT, 3),
new CfIfCmp(If.Type.GE, ValueType.INT, label8),
@@ -1158,7 +1166,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfIinc(4, 1),
new CfGoto(label2),
label8,
@@ -1170,7 +1178,7 @@
options.itemFactory.createType("[Ljava/util/Map$Entry;")),
FrameType.initialized(options.itemFactory.createType("Ljava/util/HashMap;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 1),
new CfInvoke(
184,
@@ -1234,7 +1242,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 4),
new CfLoad(ValueType.INT, 3),
new CfIfCmp(If.Type.GE, ValueType.INT, label6),
@@ -1319,7 +1327,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfIinc(4, 1),
new CfGoto(label2),
label6,
@@ -1330,7 +1338,7 @@
FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
FrameType.initialized(options.itemFactory.createType("Ljava/util/HashSet;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 1),
new CfInvoke(
184,
@@ -1402,7 +1410,7 @@
options.itemFactory.createType("Ljava/util/ArrayList;")),
FrameType.initialized(options.itemFactory.createType("Ljava/util/Iterator;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 2),
new CfInvoke(
185,
@@ -1452,7 +1460,7 @@
options.itemFactory.createType("Ljava/util/Collection;")),
FrameType.initialized(options.itemFactory.createType("Ljava/util/ArrayList;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 1),
new CfInvoke(
184,
@@ -1533,7 +1541,7 @@
FrameType.initialized(options.itemFactory.createType("Ljava/util/HashMap;")),
FrameType.initialized(options.itemFactory.createType("Ljava/util/Iterator;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 2),
new CfInvoke(
185,
@@ -1611,7 +1619,7 @@
FrameType.initialized(options.itemFactory.createType("Ljava/util/Map;")),
FrameType.initialized(options.itemFactory.createType("Ljava/util/HashMap;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 1),
new CfInvoke(
184,
@@ -1682,7 +1690,7 @@
FrameType.initialized(options.itemFactory.createType("Ljava/util/HashSet;")),
FrameType.initialized(options.itemFactory.createType("Ljava/util/Iterator;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 2),
new CfInvoke(
185,
@@ -1732,7 +1740,7 @@
options.itemFactory.createType("Ljava/util/Collection;")),
FrameType.initialized(options.itemFactory.createType("Ljava/util/HashSet;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 1),
new CfInvoke(
184,
@@ -1910,14 +1918,15 @@
new Int2ReferenceAVLTreeMap<>(
new int[] {0},
new FrameType[] {FrameType.initialized(options.itemFactory.doubleType)}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(0, ValueType.INT),
label2,
new CfFrame(
new Int2ReferenceAVLTreeMap<>(
new int[] {0},
new FrameType[] {FrameType.initialized(options.itemFactory.doubleType)}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfReturn(ValueType.INT),
label3),
ImmutableList.of(),
@@ -1962,14 +1971,15 @@
new Int2ReferenceAVLTreeMap<>(
new int[] {0},
new FrameType[] {FrameType.initialized(options.itemFactory.floatType)}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(0, ValueType.INT),
label2,
new CfFrame(
new Int2ReferenceAVLTreeMap<>(
new int[] {0},
new FrameType[] {FrameType.initialized(options.itemFactory.floatType)}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfReturn(ValueType.INT),
label3),
ImmutableList.of(),
@@ -2001,7 +2011,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 0),
new CfLoad(ValueType.INT, 1),
new CfIfCmp(If.Type.GE, ValueType.INT, label2),
@@ -2015,7 +2025,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(1, ValueType.INT),
label3,
new CfFrame(
@@ -2025,7 +2035,8 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfReturn(ValueType.INT),
label4),
ImmutableList.of(),
@@ -2188,7 +2199,7 @@
FrameType.initialized(options.itemFactory.stringType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 0),
new CfLoad(ValueType.INT, 1),
new CfInvoke(
@@ -2292,7 +2303,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.LONG, 2),
new CfNumberConversion(NumericType.LONG, NumericType.INT),
new CfReturn(ValueType.INT),
@@ -2509,7 +2520,7 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(1, ValueType.LONG),
new CfReturn(ValueType.LONG),
label6,
@@ -2520,7 +2531,7 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.LONG, 0),
new CfConstNumber(0, ValueType.LONG),
new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
@@ -2538,7 +2549,7 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.LONG, 0),
new CfConstNumber(1, ValueType.INT),
new CfLogicalBinop(CfLogicalBinop.Opcode.Ushr, NumericType.LONG),
@@ -2584,7 +2595,8 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.longType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.longType)))),
new CfConstNumber(0, ValueType.INT),
label14,
new CfFrame(
@@ -2598,9 +2610,10 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList(
- FrameType.initialized(options.itemFactory.longType),
- FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(
+ FrameType.initialized(options.itemFactory.longType),
+ FrameType.initialized(options.itemFactory.intType)))),
new CfNumberConversion(NumericType.INT, NumericType.LONG),
new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.LONG),
new CfReturn(ValueType.LONG),
@@ -2721,7 +2734,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 1),
new CfConstNumber(2, ValueType.INT),
new CfIfCmp(If.Type.LT, ValueType.INT, label4),
@@ -2737,7 +2750,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfNew(options.itemFactory.createType("Ljava/lang/NumberFormatException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfConstString(options.itemFactory.createString("illegal radix: ")),
@@ -2776,7 +2789,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(-1, ValueType.LONG),
new CfLoad(ValueType.INT, 1),
new CfNumberConversion(NumericType.INT, NumericType.LONG),
@@ -2819,7 +2832,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(0, ValueType.INT),
label8,
new CfFrame(
@@ -2831,7 +2844,8 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfStore(ValueType.INT, 5),
label9,
new CfConstNumber(0, ValueType.LONG),
@@ -2852,7 +2866,7 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 8),
new CfLoad(ValueType.INT, 2),
new CfIfCmp(If.Type.GE, ValueType.INT, label20),
@@ -2910,7 +2924,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.LONG, 6),
new CfConstNumber(0, ValueType.LONG),
new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
@@ -2954,7 +2968,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfNew(options.itemFactory.createType("Ljava/lang/NumberFormatException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfConstString(options.itemFactory.createString("Too large for unsigned long: ")),
@@ -2990,7 +3004,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.LONG, 6),
new CfLoad(ValueType.INT, 1),
new CfNumberConversion(NumericType.INT, NumericType.LONG),
@@ -3014,7 +3028,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.LONG, 6),
new CfReturn(ValueType.LONG),
label21),
@@ -3077,7 +3091,7 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.LONG, 0),
new CfLoad(ValueType.LONG, 2),
new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.LONG),
@@ -3090,7 +3104,7 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.LONG, 0),
new CfConstNumber(0, ValueType.LONG),
new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
@@ -3108,7 +3122,7 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.LONG, 0),
new CfConstNumber(1, ValueType.INT),
new CfLogicalBinop(CfLogicalBinop.Opcode.Ushr, NumericType.LONG),
@@ -3154,7 +3168,8 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.longType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.longType)))),
new CfConstNumber(0, ValueType.LONG),
label14,
new CfFrame(
@@ -3168,9 +3183,10 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList(
- FrameType.initialized(options.itemFactory.longType),
- FrameType.initialized(options.itemFactory.longType))),
+ new ArrayDeque<>(
+ Arrays.asList(
+ FrameType.initialized(options.itemFactory.longType),
+ FrameType.initialized(options.itemFactory.longType)))),
new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.LONG),
new CfReturn(ValueType.LONG),
label15),
@@ -3255,7 +3271,7 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.LONG, 0),
new CfConstNumber(0, ValueType.LONG),
new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
@@ -3282,7 +3298,7 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 2),
new CfConstNumber(2, ValueType.INT),
new CfIfCmp(If.Type.LT, ValueType.INT, label5),
@@ -3297,7 +3313,7 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(10, ValueType.INT),
new CfStore(ValueType.INT, 2),
label6,
@@ -3308,7 +3324,7 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(64, ValueType.INT),
new CfNewArray(options.itemFactory.charArrayType),
new CfStore(ValueType.OBJECT, 3),
@@ -3351,7 +3367,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 3),
new CfIinc(4, -1),
new CfLoad(ValueType.INT, 4),
@@ -3393,7 +3409,7 @@
FrameType.initialized(options.itemFactory.charArrayType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 2),
new CfConstNumber(1, ValueType.INT),
new CfLogicalBinop(CfLogicalBinop.Opcode.And, NumericType.INT),
@@ -3420,7 +3436,7 @@
FrameType.initialized(options.itemFactory.charArrayType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.LONG, 0),
new CfLoad(ValueType.INT, 2),
new CfNumberConversion(NumericType.INT, NumericType.LONG),
@@ -3446,7 +3462,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.LONG, 0),
new CfLoad(ValueType.LONG, 5),
new CfLoad(ValueType.INT, 2),
@@ -3487,7 +3503,7 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.LONG, 0),
new CfConstNumber(0, ValueType.LONG),
new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
@@ -3530,7 +3546,7 @@
FrameType.initialized(options.itemFactory.charArrayType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfNew(options.itemFactory.stringType),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfLoad(ValueType.OBJECT, 3),
@@ -3598,7 +3614,7 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfInvoke(
@@ -3652,7 +3668,7 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(0, ValueType.INT),
label3,
new CfFrame(
@@ -3663,7 +3679,8 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfLoad(ValueType.LONG, 0),
new CfLoad(ValueType.LONG, 4),
new CfLogicalBinop(CfLogicalBinop.Opcode.Xor, NumericType.LONG),
@@ -3681,7 +3698,8 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfConstNumber(0, ValueType.INT),
label5,
new CfFrame(
@@ -3692,9 +3710,10 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList(
- FrameType.initialized(options.itemFactory.intType),
- FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(
+ FrameType.initialized(options.itemFactory.intType),
+ FrameType.initialized(options.itemFactory.intType)))),
new CfLogicalBinop(CfLogicalBinop.Opcode.Or, NumericType.INT),
new CfIf(If.Type.EQ, ValueType.INT, label7),
label6,
@@ -3709,7 +3728,7 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfInvoke(
@@ -3755,7 +3774,7 @@
new Int2ReferenceAVLTreeMap<>(
new int[] {0},
new FrameType[] {FrameType.initialized(options.itemFactory.intType)}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 0),
new CfConstNumber(1, ValueType.INT),
new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
@@ -3796,7 +3815,7 @@
new Int2ReferenceAVLTreeMap<>(
new int[] {0},
new FrameType[] {FrameType.initialized(options.itemFactory.longType)}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.LONG, 0),
new CfConstNumber(1, ValueType.LONG),
new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.LONG),
@@ -3849,7 +3868,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(1, ValueType.INT),
new CfLoad(ValueType.INT, 0),
new CfLoad(ValueType.INT, 1),
@@ -3876,7 +3895,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 2),
label7,
new CfFrame(
@@ -3889,7 +3908,8 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfReturn(ValueType.INT),
label8),
ImmutableList.of(),
@@ -3941,7 +3961,7 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(1, ValueType.LONG),
new CfLoad(ValueType.LONG, 0),
new CfLoad(ValueType.LONG, 2),
@@ -3970,7 +3990,7 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.LONG, 4),
label7,
new CfFrame(
@@ -3983,7 +4003,8 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.longType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.longType)))),
new CfReturn(ValueType.LONG),
label8),
ImmutableList.of(),
@@ -4052,7 +4073,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(1, ValueType.INT),
new CfLoad(ValueType.INT, 0),
new CfLoad(ValueType.INT, 1),
@@ -4076,7 +4097,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 2),
new CfLoad(ValueType.INT, 1),
new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
@@ -4090,7 +4111,8 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfReturn(ValueType.INT),
label7),
ImmutableList.of(),
@@ -4133,7 +4155,7 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(1, ValueType.LONG),
new CfLoad(ValueType.LONG, 0),
new CfLoad(ValueType.LONG, 2),
@@ -4159,7 +4181,7 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.LONG, 4),
new CfLoad(ValueType.LONG, 2),
new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.LONG),
@@ -4173,7 +4195,8 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.longType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.longType)))),
new CfReturn(ValueType.LONG),
label7),
ImmutableList.of(),
@@ -4239,7 +4262,7 @@
new Int2ReferenceAVLTreeMap<>(
new int[] {0},
new FrameType[] {FrameType.initialized(options.itemFactory.intType)}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 0),
new CfConstNumber(1, ValueType.INT),
new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
@@ -4280,7 +4303,7 @@
new Int2ReferenceAVLTreeMap<>(
new int[] {0},
new FrameType[] {FrameType.initialized(options.itemFactory.longType)}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.LONG, 0),
new CfConstNumber(1, ValueType.LONG),
new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.LONG),
@@ -4332,7 +4355,7 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfInvoke(
@@ -4438,7 +4461,7 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 4),
new CfConstNumber(64, ValueType.INT),
new CfIfCmp(If.Type.LT, ValueType.INT, label15),
@@ -4457,7 +4480,7 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(0, ValueType.INT),
label9,
new CfFrame(
@@ -4468,7 +4491,8 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfLoad(ValueType.LONG, 2),
new CfConstNumber(-9223372036854775808L, ValueType.LONG),
new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
@@ -4484,7 +4508,8 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfConstNumber(0, ValueType.INT),
label11,
new CfFrame(
@@ -4495,9 +4520,10 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList(
- FrameType.initialized(options.itemFactory.intType),
- FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(
+ FrameType.initialized(options.itemFactory.intType),
+ FrameType.initialized(options.itemFactory.intType)))),
new CfLogicalBinop(CfLogicalBinop.Opcode.Or, NumericType.INT),
new CfIf(If.Type.EQ, ValueType.INT, label15),
label12,
@@ -4526,7 +4552,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.LONG, 5),
new CfReturn(ValueType.LONG),
label15,
@@ -4538,7 +4564,7 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfInvoke(
@@ -4736,7 +4762,7 @@
new Int2ReferenceAVLTreeMap<>(
new int[] {0},
new FrameType[] {FrameType.initialized(options.itemFactory.intType)}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 0),
new CfNeg(NumericType.INT),
new CfReturn(ValueType.INT),
@@ -4776,7 +4802,7 @@
new Int2ReferenceAVLTreeMap<>(
new int[] {0},
new FrameType[] {FrameType.initialized(options.itemFactory.longType)}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.LONG, 0),
new CfNeg(NumericType.LONG),
new CfReturn(ValueType.LONG),
@@ -4879,7 +4905,7 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfInvoke(
@@ -4933,7 +4959,7 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(0, ValueType.INT),
label3,
new CfFrame(
@@ -4944,7 +4970,8 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfLoad(ValueType.LONG, 0),
new CfLoad(ValueType.LONG, 4),
new CfLogicalBinop(CfLogicalBinop.Opcode.Xor, NumericType.LONG),
@@ -4962,7 +4989,8 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfConstNumber(0, ValueType.INT),
label5,
new CfFrame(
@@ -4973,9 +5001,10 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList(
- FrameType.initialized(options.itemFactory.intType),
- FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(
+ FrameType.initialized(options.itemFactory.intType),
+ FrameType.initialized(options.itemFactory.intType)))),
new CfLogicalBinop(CfLogicalBinop.Opcode.Or, NumericType.INT),
new CfIf(If.Type.EQ, ValueType.INT, label7),
label6,
@@ -4990,7 +5019,7 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.longType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfInvoke(
@@ -5046,7 +5075,7 @@
FrameType.initialized(options.itemFactory.longType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 2),
new CfReturn(ValueType.INT),
label4),
@@ -5086,7 +5115,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfNew(options.itemFactory.createType("Ljava/lang/IndexOutOfBoundsException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfNew(options.itemFactory.stringBuilderType),
@@ -5195,7 +5224,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 0),
new CfReturn(ValueType.INT),
label3),
@@ -5231,7 +5260,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfNew(options.itemFactory.createType("Ljava/lang/IndexOutOfBoundsException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfNew(options.itemFactory.stringBuilderType),
@@ -5322,7 +5351,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 0),
new CfReturn(ValueType.INT),
label3),
@@ -5354,7 +5383,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfNew(options.itemFactory.createType("Ljava/lang/IndexOutOfBoundsException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfNew(options.itemFactory.stringBuilderType),
@@ -5426,7 +5455,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 0),
new CfReturn(ValueType.INT),
label3),
@@ -5460,7 +5489,7 @@
FrameType.initialized(
options.itemFactory.createType("Ljava/util/Comparator;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 2),
new CfLoad(ValueType.OBJECT, 0),
new CfLoad(ValueType.OBJECT, 1),
@@ -5484,7 +5513,8 @@
FrameType.initialized(
options.itemFactory.createType("Ljava/util/Comparator;"))
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfReturn(ValueType.INT),
label3),
ImmutableList.of(),
@@ -5551,7 +5581,7 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 0),
new CfIf(If.Type.NE, ValueType.OBJECT, label2),
new CfConstNumber(0, ValueType.INT),
@@ -5564,7 +5594,7 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceOf(options.itemFactory.booleanArrayType),
new CfIf(If.Type.EQ, ValueType.INT, label6),
@@ -5597,7 +5627,7 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(0, ValueType.INT),
label5,
new CfFrame(
@@ -5607,7 +5637,8 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfReturn(ValueType.INT),
label6,
new CfFrame(
@@ -5617,7 +5648,7 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceOf(options.itemFactory.byteArrayType),
new CfIf(If.Type.EQ, ValueType.INT, label10),
@@ -5650,7 +5681,7 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(0, ValueType.INT),
label9,
new CfFrame(
@@ -5660,7 +5691,8 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfReturn(ValueType.INT),
label10,
new CfFrame(
@@ -5670,7 +5702,7 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceOf(options.itemFactory.charArrayType),
new CfIf(If.Type.EQ, ValueType.INT, label14),
@@ -5703,7 +5735,7 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(0, ValueType.INT),
label13,
new CfFrame(
@@ -5713,7 +5745,8 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfReturn(ValueType.INT),
label14,
new CfFrame(
@@ -5723,7 +5756,7 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceOf(options.itemFactory.doubleArrayType),
new CfIf(If.Type.EQ, ValueType.INT, label18),
@@ -5756,7 +5789,7 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(0, ValueType.INT),
label17,
new CfFrame(
@@ -5766,7 +5799,8 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfReturn(ValueType.INT),
label18,
new CfFrame(
@@ -5776,7 +5810,7 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceOf(options.itemFactory.floatArrayType),
new CfIf(If.Type.EQ, ValueType.INT, label22),
@@ -5809,7 +5843,7 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(0, ValueType.INT),
label21,
new CfFrame(
@@ -5819,7 +5853,8 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfReturn(ValueType.INT),
label22,
new CfFrame(
@@ -5829,7 +5864,7 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceOf(options.itemFactory.intArrayType),
new CfIf(If.Type.EQ, ValueType.INT, label26),
@@ -5862,7 +5897,7 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(0, ValueType.INT),
label25,
new CfFrame(
@@ -5872,7 +5907,8 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfReturn(ValueType.INT),
label26,
new CfFrame(
@@ -5882,7 +5918,7 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceOf(options.itemFactory.longArrayType),
new CfIf(If.Type.EQ, ValueType.INT, label30),
@@ -5915,7 +5951,7 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(0, ValueType.INT),
label29,
new CfFrame(
@@ -5925,7 +5961,8 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfReturn(ValueType.INT),
label30,
new CfFrame(
@@ -5935,7 +5972,7 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceOf(options.itemFactory.shortArrayType),
new CfIf(If.Type.EQ, ValueType.INT, label34),
@@ -5968,7 +6005,7 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(0, ValueType.INT),
label33,
new CfFrame(
@@ -5978,7 +6015,8 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfReturn(ValueType.INT),
label34,
new CfFrame(
@@ -5988,7 +6026,7 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceOf(options.itemFactory.createType("[Ljava/lang/Object;")),
new CfIf(If.Type.EQ, ValueType.INT, label38),
@@ -6021,7 +6059,7 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(0, ValueType.INT),
label37,
new CfFrame(
@@ -6031,7 +6069,8 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfReturn(ValueType.INT),
label38,
new CfFrame(
@@ -6041,7 +6080,7 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 0),
new CfLoad(ValueType.OBJECT, 1),
new CfInvoke(
@@ -6094,7 +6133,7 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(1, ValueType.INT),
new CfGoto(label3),
label2,
@@ -6105,7 +6144,7 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(0, ValueType.INT),
label3,
new CfFrame(
@@ -6115,7 +6154,8 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfReturn(ValueType.INT),
label4),
ImmutableList.of(),
@@ -6142,7 +6182,7 @@
new Int2ReferenceAVLTreeMap<>(
new int[] {0},
new FrameType[] {FrameType.initialized(options.itemFactory.objectType)}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 0),
new CfInvoke(
182,
@@ -6156,7 +6196,8 @@
new Int2ReferenceAVLTreeMap<>(
new int[] {0},
new FrameType[] {FrameType.initialized(options.itemFactory.objectType)}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfReturn(ValueType.INT),
label3),
ImmutableList.of(),
@@ -6183,14 +6224,15 @@
new Int2ReferenceAVLTreeMap<>(
new int[] {0},
new FrameType[] {FrameType.initialized(options.itemFactory.objectType)}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(0, ValueType.INT),
label2,
new CfFrame(
new Int2ReferenceAVLTreeMap<>(
new int[] {0},
new FrameType[] {FrameType.initialized(options.itemFactory.objectType)}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfReturn(ValueType.INT),
label3),
ImmutableList.of(),
@@ -6217,14 +6259,15 @@
new Int2ReferenceAVLTreeMap<>(
new int[] {0},
new FrameType[] {FrameType.initialized(options.itemFactory.objectType)}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(0, ValueType.INT),
label2,
new CfFrame(
new Int2ReferenceAVLTreeMap<>(
new int[] {0},
new FrameType[] {FrameType.initialized(options.itemFactory.objectType)}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfReturn(ValueType.INT),
label3),
ImmutableList.of(),
@@ -6254,7 +6297,7 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 1),
new CfConstString(options.itemFactory.createString("defaultObj")),
new CfInvoke(
@@ -6298,7 +6341,7 @@
FrameType.initialized(
options.itemFactory.createType("Ljava/util/function/Supplier;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 1),
new CfConstString(options.itemFactory.createString("supplier")),
new CfInvoke(
@@ -6374,7 +6417,7 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.stringType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 0),
new CfReturn(ValueType.OBJECT),
label3),
@@ -6432,7 +6475,7 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.stringType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 0),
new CfInvoke(
182,
@@ -6449,7 +6492,8 @@
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.stringType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.stringType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.stringType)))),
new CfReturn(ValueType.OBJECT),
label3),
ImmutableList.of(),
@@ -6506,7 +6550,7 @@
options.itemFactory.createType("Ljava/util/function/Consumer;")),
FrameType.initialized(options.itemFactory.createType("Ljava/lang/Runnable;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 2),
new CfInvoke(
185,
@@ -6525,7 +6569,7 @@
options.itemFactory.createType("Ljava/util/function/Consumer;")),
FrameType.initialized(options.itemFactory.createType("Ljava/lang/Runnable;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfReturnVoid(),
label4),
ImmutableList.of(),
@@ -6584,7 +6628,7 @@
options.itemFactory.createType("Ljava/util/function/DoubleConsumer;")),
FrameType.initialized(options.itemFactory.createType("Ljava/lang/Runnable;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 2),
new CfInvoke(
185,
@@ -6604,7 +6648,7 @@
options.itemFactory.createType("Ljava/util/function/DoubleConsumer;")),
FrameType.initialized(options.itemFactory.createType("Ljava/lang/Runnable;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfReturnVoid(),
label4),
ImmutableList.of(),
@@ -6663,7 +6707,7 @@
options.itemFactory.createType("Ljava/util/function/IntConsumer;")),
FrameType.initialized(options.itemFactory.createType("Ljava/lang/Runnable;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 2),
new CfInvoke(
185,
@@ -6683,7 +6727,7 @@
options.itemFactory.createType("Ljava/util/function/IntConsumer;")),
FrameType.initialized(options.itemFactory.createType("Ljava/lang/Runnable;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfReturnVoid(),
label4),
ImmutableList.of(),
@@ -6742,7 +6786,7 @@
options.itemFactory.createType("Ljava/util/function/LongConsumer;")),
FrameType.initialized(options.itemFactory.createType("Ljava/lang/Runnable;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 2),
new CfInvoke(
185,
@@ -6762,7 +6806,7 @@
options.itemFactory.createType("Ljava/util/function/LongConsumer;")),
FrameType.initialized(options.itemFactory.createType("Ljava/lang/Runnable;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfReturnVoid(),
label4),
ImmutableList.of(),
@@ -6798,7 +6842,7 @@
new FrameType[] {
FrameType.initialized(options.itemFactory.createType("Ljava/util/Optional;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(0, ValueType.INT),
label2,
new CfFrame(
@@ -6807,7 +6851,8 @@
new FrameType[] {
FrameType.initialized(options.itemFactory.createType("Ljava/util/Optional;"))
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfReturn(ValueType.INT),
label3),
ImmutableList.of(),
@@ -6844,7 +6889,7 @@
FrameType.initialized(
options.itemFactory.createType("Ljava/util/OptionalDouble;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(0, ValueType.INT),
label2,
new CfFrame(
@@ -6854,7 +6899,8 @@
FrameType.initialized(
options.itemFactory.createType("Ljava/util/OptionalDouble;"))
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfReturn(ValueType.INT),
label3),
ImmutableList.of(),
@@ -6891,7 +6937,7 @@
FrameType.initialized(
options.itemFactory.createType("Ljava/util/OptionalInt;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(0, ValueType.INT),
label2,
new CfFrame(
@@ -6901,7 +6947,8 @@
FrameType.initialized(
options.itemFactory.createType("Ljava/util/OptionalInt;"))
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfReturn(ValueType.INT),
label3),
ImmutableList.of(),
@@ -6938,7 +6985,7 @@
FrameType.initialized(
options.itemFactory.createType("Ljava/util/OptionalLong;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(0, ValueType.INT),
label2,
new CfFrame(
@@ -6948,7 +6995,8 @@
FrameType.initialized(
options.itemFactory.createType("Ljava/util/OptionalLong;"))
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfReturn(ValueType.INT),
label3),
ImmutableList.of(),
@@ -7000,7 +7048,7 @@
FrameType.initialized(
options.itemFactory.createType("Ljava/util/function/Supplier;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 1),
new CfInvoke(
185,
@@ -7074,7 +7122,7 @@
new FrameType[] {
FrameType.initialized(options.itemFactory.createType("Ljava/util/Optional;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfInvoke(
184,
options.itemFactory.createMethod(
@@ -7136,7 +7184,7 @@
FrameType.initialized(
options.itemFactory.createType("Ljava/util/OptionalDouble;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfInvoke(
184,
options.itemFactory.createMethod(
@@ -7198,7 +7246,7 @@
FrameType.initialized(
options.itemFactory.createType("Ljava/util/OptionalInt;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfInvoke(
184,
options.itemFactory.createMethod(
@@ -7260,7 +7308,7 @@
FrameType.initialized(
options.itemFactory.createType("Ljava/util/OptionalLong;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfInvoke(
184,
options.itemFactory.createMethod(
@@ -7379,7 +7427,7 @@
new Int2ReferenceAVLTreeMap<>(
new int[] {0},
new FrameType[] {FrameType.initialized(options.itemFactory.objectType)}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 0),
new CfInvoke(
184,
@@ -7395,9 +7443,10 @@
new Int2ReferenceAVLTreeMap<>(
new int[] {0},
new FrameType[] {FrameType.initialized(options.itemFactory.objectType)}),
- Arrays.asList(
- FrameType.initialized(
- options.itemFactory.createType("Ljava/util/stream/Stream;")))),
+ new ArrayDeque<>(
+ Arrays.asList(
+ FrameType.initialized(
+ options.itemFactory.createType("Ljava/util/stream/Stream;"))))),
new CfReturn(ValueType.OBJECT),
label3),
ImmutableList.of(),
@@ -7442,7 +7491,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 1),
new CfLoad(ValueType.INT, 2),
new CfIfCmp(If.Type.GE, ValueType.INT, label8),
@@ -7482,7 +7531,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 1),
new CfLoad(ValueType.INT, 3),
new CfInvoke(
@@ -7502,7 +7551,7 @@
new Int2ReferenceAVLTreeMap<>(
new int[] {0},
new FrameType[] {FrameType.initialized(options.itemFactory.stringType)}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(1, ValueType.INT),
new CfReturn(ValueType.INT),
label9),
@@ -7551,7 +7600,7 @@
FrameType.initialized(
options.itemFactory.createType("[Ljava/lang/CharSequence;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfNew(options.itemFactory.stringBuilderType),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfInvoke(
@@ -7595,7 +7644,7 @@
FrameType.initialized(options.itemFactory.stringBuilderType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 3),
new CfLoad(ValueType.OBJECT, 1),
new CfArrayLength(),
@@ -7641,7 +7690,7 @@
options.itemFactory.createType("[Ljava/lang/CharSequence;")),
FrameType.initialized(options.itemFactory.stringBuilderType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 2),
new CfInvoke(
182,
@@ -7695,7 +7744,7 @@
FrameType.initialized(options.itemFactory.charSequenceType),
FrameType.initialized(options.itemFactory.createType("Ljava/lang/Iterable;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfNew(options.itemFactory.stringBuilderType),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfInvoke(
@@ -7758,7 +7807,7 @@
FrameType.initialized(options.itemFactory.stringBuilderType),
FrameType.initialized(options.itemFactory.createType("Ljava/util/Iterator;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 3),
new CfInvoke(
185,
@@ -7813,7 +7862,7 @@
FrameType.initialized(options.itemFactory.stringBuilderType),
FrameType.initialized(options.itemFactory.createType("Ljava/util/Iterator;"))
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 2),
new CfInvoke(
182,
@@ -7905,7 +7954,7 @@
FrameType.initialized(options.itemFactory.stringType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 0),
new CfInvoke(
182,
@@ -7929,7 +7978,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstString(options.itemFactory.createString("")),
new CfReturn(ValueType.OBJECT),
label5,
@@ -7941,7 +7990,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 1),
new CfConstNumber(1, ValueType.INT),
new CfIfCmp(If.Type.NE, ValueType.INT, label7),
@@ -7957,7 +8006,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfNew(options.itemFactory.stringBuilderType),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfLoad(ValueType.INT, 2),
@@ -7986,7 +8035,7 @@
FrameType.initialized(options.itemFactory.stringBuilderType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 4),
new CfLoad(ValueType.INT, 1),
new CfIfCmp(If.Type.GE, ValueType.INT, label12),
@@ -8015,7 +8064,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.stringBuilderType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 3),
new CfInvoke(
182,
@@ -8074,7 +8123,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 1),
new CfLoad(ValueType.INT, 2),
new CfIfCmp(If.Type.GE, ValueType.INT, label8),
@@ -8113,7 +8162,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 1),
new CfLoad(ValueType.INT, 3),
new CfInvoke(
@@ -8137,7 +8186,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 2),
new CfLoad(ValueType.INT, 1),
new CfIfCmp(If.Type.LE, ValueType.INT, label14),
@@ -8178,7 +8227,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 2),
new CfLoad(ValueType.INT, 3),
new CfInvoke(
@@ -8202,7 +8251,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 0),
new CfLoad(ValueType.INT, 1),
new CfLoad(ValueType.INT, 2),
@@ -8260,7 +8309,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 1),
new CfLoad(ValueType.INT, 2),
new CfIfCmp(If.Type.GE, ValueType.INT, label8),
@@ -8299,7 +8348,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 1),
new CfLoad(ValueType.INT, 3),
new CfInvoke(
@@ -8323,7 +8372,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 0),
new CfLoad(ValueType.INT, 1),
new CfLoad(ValueType.INT, 2),
@@ -8376,7 +8425,7 @@
FrameType.initialized(options.itemFactory.stringType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 1),
new CfIf(If.Type.LE, ValueType.INT, label7),
label2,
@@ -8415,7 +8464,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 1),
new CfLoad(ValueType.INT, 2),
new CfInvoke(
@@ -8438,7 +8487,7 @@
FrameType.initialized(options.itemFactory.stringType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 0),
new CfConstNumber(0, ValueType.INT),
new CfLoad(ValueType.INT, 1),
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 eb6bddd..b6bd724 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
@@ -99,6 +99,11 @@
it.removeOrReplaceByDebugLocalRead();
value.uniquePhiUsers().forEach(Phi::removeTrivialPhi);
}
+
+ @Override
+ public String toString() {
+ return "ExistingValue(v" + value.getNumber() + ")";
+ }
}
private class MaterializableValue implements FieldValue {
@@ -361,7 +366,7 @@
InstanceFieldInitializationInfoCollection fieldInitializationInfos =
instanceInitializerInfo.fieldInitializationInfos();
- fieldInitializationInfos.forEach(
+ fieldInitializationInfos.forEachWithDeterministicOrder(
appView,
(field, info) -> {
if (!appView.appInfo().withLiveness().mayPropagateValueFor(field.field)) {
@@ -372,13 +377,22 @@
invoke.getArgument(info.asArgumentInitializationInfo().getArgumentIndex());
Value object = invoke.getReceiver().getAliasedValue();
FieldAndObject fieldAndObject = new FieldAndObject(field.field, object);
- activeState.putNonFinalInstanceField(fieldAndObject, new ExistingValue(value));
+ if (field.isFinal()) {
+ activeState.putFinalInstanceField(fieldAndObject, new ExistingValue(value));
+ } else {
+ activeState.putNonFinalInstanceField(fieldAndObject, new ExistingValue(value));
+ }
} else if (info.isSingleValue()) {
SingleValue value = info.asSingleValue();
if (value.isMaterializableInContext(appView.withLiveness(), method)) {
Value object = invoke.getReceiver().getAliasedValue();
FieldAndObject fieldAndObject = new FieldAndObject(field.field, object);
- activeState.putNonFinalInstanceField(fieldAndObject, new MaterializableValue(value));
+ if (field.isFinal()) {
+ activeState.putFinalInstanceField(fieldAndObject, new MaterializableValue(value));
+ } else {
+ activeState.putNonFinalInstanceField(
+ fieldAndObject, new MaterializableValue(value));
+ }
}
} else {
assert info.isTypeInitializationInfo();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCfMethods.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCfMethods.java
index 922b5f0..21c8b07 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCfMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCfMethods.java
@@ -38,6 +38,7 @@
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableList;
import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
+import java.util.ArrayDeque;
import java.util.Arrays;
public final class EnumUnboxingCfMethods {
@@ -69,7 +70,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfNew(options.itemFactory.createType("Ljava/lang/NullPointerException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfInvoke(
@@ -88,7 +89,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 0),
new CfLoad(ValueType.INT, 1),
new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
@@ -132,7 +133,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 0),
new CfLoad(ValueType.INT, 1),
new CfIfCmp(If.Type.NE, ValueType.INT, label3),
@@ -146,7 +147,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfConstNumber(0, ValueType.INT),
label4,
new CfFrame(
@@ -156,7 +157,8 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList(FrameType.initialized(options.itemFactory.intType))),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initialized(options.itemFactory.intType)))),
new CfReturn(ValueType.INT),
label5),
ImmutableList.of(),
@@ -192,7 +194,7 @@
new Int2ReferenceAVLTreeMap<>(
new int[] {0},
new FrameType[] {FrameType.initialized(options.itemFactory.intType)}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 0),
new CfConstNumber(1, ValueType.INT),
new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
@@ -231,7 +233,7 @@
FrameType.initialized(options.itemFactory.intArrayType),
FrameType.initialized(options.itemFactory.intType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 2),
new CfLoad(ValueType.OBJECT, 1),
new CfArrayLength(),
@@ -254,7 +256,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.intArrayType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 1),
new CfReturn(ValueType.OBJECT),
label6),
@@ -291,7 +293,7 @@
new Int2ReferenceAVLTreeMap<>(
new int[] {0},
new FrameType[] {FrameType.initialized(options.itemFactory.intType)}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfReturnVoid(),
label3),
ImmutableList.of(),
@@ -333,7 +335,7 @@
FrameType.initialized(options.itemFactory.intType),
FrameType.initialized(options.itemFactory.stringType)
}),
- Arrays.asList()),
+ new ArrayDeque<>(Arrays.asList())),
new CfReturnVoid(),
label3),
ImmutableList.of(),
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/EmptyInstanceFieldInitializationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/EmptyInstanceFieldInitializationInfoCollection.java
index 4e45bac..9c7d07c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/EmptyInstanceFieldInitializationInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/EmptyInstanceFieldInitializationInfoCollection.java
@@ -35,6 +35,13 @@
}
@Override
+ public void forEachWithDeterministicOrder(
+ DexDefinitionSupplier definitions,
+ BiConsumer<DexEncodedField, InstanceFieldInitializationInfo> consumer) {
+ // Intentionally empty.
+ }
+
+ @Override
public InstanceFieldInitializationInfo get(DexEncodedField field) {
return UnknownInstanceFieldInitializationInfo.getInstance();
}
@@ -49,4 +56,9 @@
AppView<AppInfoWithLiveness> appView, GraphLens lens) {
return this;
}
+
+ @Override
+ public String toString() {
+ return "EmptyInstanceFieldInitializationInfoCollection";
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldArgumentInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldArgumentInitializationInfo.java
index 0b87005..0447d5d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldArgumentInitializationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldArgumentInitializationInfo.java
@@ -43,4 +43,9 @@
// initialization info.
return this;
}
+
+ @Override
+ public String toString() {
+ return "InstanceFieldArgumentInitializationInfo(argumentIndex=" + argumentIndex + ")";
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
index 5979031..775d7e9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
@@ -10,8 +10,7 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import java.util.IdentityHashMap;
-import java.util.Map;
+import java.util.TreeMap;
import java.util.function.BiConsumer;
/**
@@ -31,6 +30,10 @@
DexDefinitionSupplier definitions,
BiConsumer<DexEncodedField, InstanceFieldInitializationInfo> consumer);
+ public abstract void forEachWithDeterministicOrder(
+ DexDefinitionSupplier definitions,
+ BiConsumer<DexEncodedField, InstanceFieldInitializationInfo> consumer);
+
public abstract InstanceFieldInitializationInfo get(DexEncodedField field);
public abstract boolean isEmpty();
@@ -40,7 +43,8 @@
public static class Builder {
- Map<DexField, InstanceFieldInitializationInfo> infos = new IdentityHashMap<>();
+ TreeMap<DexField, InstanceFieldInitializationInfo> infos =
+ new TreeMap<>(DexField::slowCompareTo);
public void recordInitializationInfo(
DexEncodedField field, InstanceFieldInitializationInfo info) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java
index fe09706..7e78f27 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java
@@ -87,4 +87,9 @@
return Objects.equals(dynamicLowerBoundType, info.dynamicLowerBoundType)
&& Objects.equals(dynamicUpperBoundType, info.dynamicUpperBoundType);
}
+
+ @Override
+ public String toString() {
+ return "InstanceFieldTypeInitializationInfo";
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java
index e8daa83..6fa0363 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java
@@ -12,17 +12,20 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import java.util.Map;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.TreeMap;
import java.util.function.BiConsumer;
/** See {@link InstanceFieldArgumentInitializationInfo}. */
public class NonTrivialInstanceFieldInitializationInfoCollection
extends InstanceFieldInitializationInfoCollection {
- private final Map<DexField, InstanceFieldInitializationInfo> infos;
+ private final TreeMap<DexField, InstanceFieldInitializationInfo> infos;
NonTrivialInstanceFieldInitializationInfoCollection(
- Map<DexField, InstanceFieldInitializationInfo> infos) {
+ TreeMap<DexField, InstanceFieldInitializationInfo> infos) {
assert !infos.isEmpty();
assert infos.values().stream().noneMatch(InstanceFieldInitializationInfo::isUnknown);
this.infos = infos;
@@ -45,6 +48,14 @@
}
@Override
+ public void forEachWithDeterministicOrder(
+ DexDefinitionSupplier definitions,
+ BiConsumer<DexEncodedField, InstanceFieldInitializationInfo> consumer) {
+ // We currently use a sorted backing and can therefore simply use forEach().
+ forEach(definitions, consumer);
+ }
+
+ @Override
public InstanceFieldInitializationInfo get(DexEncodedField field) {
return infos.getOrDefault(field.field, UnknownInstanceFieldInitializationInfo.getInstance());
}
@@ -68,4 +79,13 @@
});
return builder.build();
}
+
+ @Override
+ public String toString() {
+ List<String> strings = new ArrayList<>();
+ infos.forEach((field, info) -> strings.add(field.toSourceString() + " -> " + info));
+ return "NonTrivialInstanceFieldInitializationInfoCollection("
+ + StringUtils.join(strings, "; ")
+ + ")";
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/UnknownInstanceFieldInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/UnknownInstanceFieldInitializationInfo.java
index ab4bbfe..13d4944 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/UnknownInstanceFieldInitializationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/UnknownInstanceFieldInitializationInfo.java
@@ -33,4 +33,9 @@
AppView<AppInfoWithLiveness> appView, GraphLens lens) {
return this;
}
+
+ @Override
+ public String toString() {
+ return "UnknownInstanceFieldInitializationInfo";
+ }
}
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 e4424ec..c9a7f32 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
@@ -59,4 +59,9 @@
AppView<AppInfoWithLiveness> appView, GraphLens lens) {
return this;
}
+
+ @Override
+ public String toString() {
+ return "DefaultInstanceInitializerInfo";
+ }
}
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 84ce93f..ee08c65 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
@@ -92,6 +92,11 @@
lens.getRenamedMethodSignature(parent));
}
+ @Override
+ public String toString() {
+ return "NonTrivialInstanceInitializerInfo(" + fieldInitializationInfos + ")";
+ }
+
public static class Builder {
private final InstanceFieldInitializationInfoCollection instanceFieldInitializationInfos;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
index 0f6a8ed..cc01f83 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.ir.optimize.lambda.CodeProcessor.Strategy;
import com.android.tools.r8.kotlin.Kotlin;
import com.android.tools.r8.shaking.MainDexClasses;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThrowingConsumer;
import com.google.common.collect.Lists;
import com.google.common.io.BaseEncoding;
@@ -148,7 +149,7 @@
int lastUsed = -1;
int lastSeen = -1;
for (Entry<DexType, LambdaInfo> entry : lambdas.entrySet()) {
- Integer index = entry.getValue().id;
+ int index = entry.getValue().id;
assert lastUsed <= lastSeen && lastSeen < index;
lastUsed++;
lastSeen = index;
@@ -184,11 +185,12 @@
+ getGroupSuffix()
+ createHash(lambdas)
+ ";");
- return getBuilder(appView.dexItemFactory()).synthesizeClass(appView, feedback);
+ return getBuilder(appView.dexItemFactory(), appView.options())
+ .synthesizeClass(appView, feedback);
}
protected abstract LambdaGroupClassBuilder<? extends LambdaGroup> getBuilder(
- DexItemFactory factory);
+ DexItemFactory factory, InternalOptions options);
private String createHash(List<LambdaInfo> lambdas) {
try {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
index 697638f..5ddc8dd 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
@@ -38,28 +38,26 @@
AppView<? extends AppInfoWithClassHierarchy> appView, OptimizationFeedback feedback) {
DexType groupClassType = group.getGroupClassType();
DexType superClassType = getSuperClassType();
- DexProgramClass programClass =
- new DexProgramClass(
- groupClassType,
- null,
- new SynthesizedOrigin(origin, getClass()),
- buildAccessFlags(),
- superClassType,
- buildInterfaces(),
- factory.createString(origin),
- null,
- Collections.emptyList(),
- buildEnclosingMethodAttribute(),
- buildInnerClasses(),
- buildAnnotations(),
- buildStaticFields(appView, feedback),
- buildInstanceFields(),
- buildDirectMethods(),
- buildVirtualMethods(),
- factory.getSkipNameValidationForTesting(),
- // The name of the class is based on the hash of the content.
- DexProgramClass::checksumFromType);
- return programClass;
+ return new DexProgramClass(
+ groupClassType,
+ null,
+ new SynthesizedOrigin(origin, getClass()),
+ buildAccessFlags(),
+ superClassType,
+ buildInterfaces(),
+ factory.createString(origin),
+ null,
+ Collections.emptyList(),
+ buildEnclosingMethodAttribute(),
+ buildInnerClasses(),
+ buildAnnotations(),
+ buildStaticFields(appView, feedback),
+ buildInstanceFields(),
+ buildDirectMethods(),
+ buildVirtualMethods(),
+ factory.getSkipNameValidationForTesting(),
+ // The name of the class is based on the hash of the content.
+ DexProgramClass::checksumFromType);
}
protected abstract DexType getSuperClassType();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index 3414861..3bb366d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -167,7 +167,8 @@
}
}
- assert invokesToInline.size() > 1;
+ assert invokesToInline.size() > 1
+ || appView.options().testing.verificationSizeLimitInBytesOverride > -1;
inliner.performForcedInlining(method, code, invokesToInline, provider, Timing.empty());
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
index 4aafb60..117ee3b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
import com.android.tools.r8.kotlin.Kotlin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThrowingConsumer;
import com.google.common.collect.Lists;
import java.util.List;
@@ -108,8 +109,8 @@
}
@Override
- protected ClassBuilder getBuilder(DexItemFactory factory) {
- return new ClassBuilder(factory, "java-style lambda group");
+ protected ClassBuilder getBuilder(DexItemFactory factory, InternalOptions options) {
+ return new ClassBuilder(factory, options, "java-style lambda group");
}
@Override
@@ -183,8 +184,8 @@
// Specialized class builder.
private final class ClassBuilder extends KotlinLambdaGroupClassBuilder<JStyleLambdaGroup> {
- ClassBuilder(DexItemFactory factory, String origin) {
- super(JStyleLambdaGroup.this, factory, origin);
+ ClassBuilder(DexItemFactory factory, InternalOptions options, String origin) {
+ super(JStyleLambdaGroup.this, factory, options, origin);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroupIdFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroupIdFactory.java
index e9ab954..2e4e1a9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroupIdFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroupIdFactory.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.optimize.lambda.kotlin;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexType;
@@ -27,8 +28,10 @@
assert lambda.getKotlinInfo().isSyntheticClass();
assert lambda.getKotlinInfo().asSyntheticClass().isJavaStyleLambda();
- checkAccessFlags("class access flags", lambda.accessFlags,
- PUBLIC_LAMBDA_CLASS_FLAGS, LAMBDA_CLASS_FLAGS);
+ // Ignore ACC_SUPER.
+ ClassAccessFlags copy = lambda.accessFlags.copy();
+ copy.unsetSuper();
+ checkAccessFlags("class access flags", copy, PUBLIC_LAMBDA_CLASS_FLAGS, LAMBDA_CLASS_FLAGS);
// Class and interface.
validateSuperclass(kotlin, lambda);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
index cc9061a..cfae611 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
@@ -26,6 +26,7 @@
import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
import com.android.tools.r8.kotlin.Kotlin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThrowingConsumer;
import com.google.common.collect.Lists;
import java.util.List;
@@ -114,8 +115,8 @@
}
@Override
- protected ClassBuilder getBuilder(DexItemFactory factory) {
- return new ClassBuilder(factory, "kotlin-style lambda group");
+ protected ClassBuilder getBuilder(DexItemFactory factory, InternalOptions options) {
+ return new ClassBuilder(factory, options, "kotlin-style lambda group");
}
@Override
@@ -194,8 +195,8 @@
// Specialized class builder.
private final class ClassBuilder extends KotlinLambdaGroupClassBuilder<KStyleLambdaGroup> {
- ClassBuilder(DexItemFactory factory, String origin) {
- super(KStyleLambdaGroup.this, factory, origin);
+ ClassBuilder(DexItemFactory factory, InternalOptions options, String origin) {
+ super(KStyleLambdaGroup.this, factory, options, origin);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroupIdFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroupIdFactory.java
index 495ab0b..6f7650b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroupIdFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroupIdFactory.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.optimize.lambda.kotlin;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexType;
@@ -27,8 +28,10 @@
assert lambda.getKotlinInfo().isSyntheticClass();
assert lambda.getKotlinInfo().asSyntheticClass().isKotlinStyleLambda();
- checkAccessFlags("class access flags", lambda.accessFlags,
- PUBLIC_LAMBDA_CLASS_FLAGS, LAMBDA_CLASS_FLAGS);
+ // Ignore ACC_SUPER.
+ ClassAccessFlags copy = lambda.accessFlags.copy();
+ copy.unsetSuper();
+ checkAccessFlags("class access flags", copy, PUBLIC_LAMBDA_CLASS_FLAGS, LAMBDA_CLASS_FLAGS);
// Class and interface.
validateSuperclass(kotlin, lambda);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
index dfd62d3..5b0b0b4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ClassAccessFlags;
+import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexEncodedField;
@@ -26,11 +27,16 @@
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.code.IntSwitch;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.ir.optimize.lambda.LambdaGroupClassBuilder;
import com.android.tools.r8.ir.synthetic.SynthesizedCode;
import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
+import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.IntBox;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.TriConsumer;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.Collections;
@@ -44,10 +50,13 @@
extends LambdaGroupClassBuilder<T> implements KotlinLambdaConstants {
final KotlinLambdaGroupId id;
+ final InternalOptions options;
- KotlinLambdaGroupClassBuilder(T group, DexItemFactory factory, String origin) {
+ KotlinLambdaGroupClassBuilder(
+ T group, DexItemFactory factory, InternalOptions options, String origin) {
super(group, factory, origin);
this.id = group.id();
+ this.options = options;
}
abstract SyntheticSourceCode createInstanceInitializerSourceCode(
@@ -107,34 +116,104 @@
boolean isMainMethod =
id.mainMethodName == methodName && id.mainMethodProto == methodProto;
- // For bridge methods we still use same PUBLIC FINAL as for the main method,
- // since inlining removes BRIDGE & SYNTHETIC attributes from the bridge methods
- // anyways and our new method is a product of inlining.
- MethodAccessFlags accessFlags = MAIN_METHOD_FLAGS.copy();
-
- DexMethod method = factory.createMethod(group.getGroupClassType(), methodProto, methodName);
- result.add(
- new DexEncodedMethod(
- method,
- accessFlags,
- isMainMethod ? id.mainMethodAnnotations : DexAnnotationSet.empty(),
- isMainMethod ? id.mainMethodParamAnnotations : ParameterAnnotationsList.empty(),
- new SynthesizedCode(
- callerPosition ->
- new KotlinLambdaVirtualMethodSourceCode(
- factory,
- group.getGroupClassType(),
- method,
- group.getLambdaIdField(factory),
- implMethods,
- callerPosition)),
- true));
+ // Merging lambdas can introduce methods with too many instructions for the verifier on
+ // ART to give up statically verifying the method. We therefore split up the implementation
+ // methods and chain them with fallthrough:
+ // function <method>() {
+ // switch(field.id) {
+ // case 1:
+ // case 2:
+ // ...
+ // case n:
+ // default: <method$1>()
+ // }
+ //
+ // function <method$1>() {
+ // case n + 1:
+ // case n + 2:
+ // ...
+ // case n + m:
+ // default: throw null
+ // }
+ IntBox counter = new IntBox(0);
+ Box<DexMethod> currentMethodBox =
+ new Box<>(factory.createMethod(group.getGroupClassType(), methodProto, methodName));
+ splitIntoGroupsBasedOnInstructionSize(
+ implMethods,
+ (implMethodsToAdd, methodsSoFar, methodsRemaining) -> {
+ assert currentMethodBox.isSet();
+ // For bridge methods we still use same PUBLIC FINAL as for the main method,
+ // since inlining removes BRIDGE & SYNTHETIC attributes from the bridge methods
+ // anyways and our new method is a product of inlining.
+ MethodAccessFlags accessFlags = MAIN_METHOD_FLAGS.copy();
+ DexMethod method = currentMethodBox.get();
+ DexMethod fallthrough =
+ methodsRemaining
+ ? factory.createMethod(
+ group.getGroupClassType(),
+ methodProto,
+ methodName.toString() + "$" + counter.getAndIncrement())
+ : null;
+ result.add(
+ new DexEncodedMethod(
+ method,
+ accessFlags,
+ isMainMethod ? id.mainMethodAnnotations : DexAnnotationSet.empty(),
+ isMainMethod
+ ? id.mainMethodParamAnnotations
+ : ParameterAnnotationsList.empty(),
+ new SynthesizedCode(
+ callerPosition ->
+ new KotlinLambdaVirtualMethodSourceCode(
+ factory,
+ group.getGroupClassType(),
+ method,
+ group.getLambdaIdField(factory),
+ implMethodsToAdd,
+ fallthrough,
+ methodsSoFar,
+ callerPosition)),
+ true));
+ currentMethodBox.set(fallthrough);
+ });
+ assert !currentMethodBox.isSet();
}
}
-
return result.toArray(DexEncodedMethod.EMPTY_ARRAY);
}
+ private void splitIntoGroupsBasedOnInstructionSize(
+ List<DexEncodedMethod> implMethods,
+ TriConsumer<List<DexEncodedMethod>, Integer, Boolean> consumer) {
+ List<DexEncodedMethod> methods = new ArrayList<>();
+ // Upper bound in DEX for reading the field for switching on the group id.
+ final int fieldLoadInstructionSize = 10;
+ int verificationSizeLimitInBytes = options.verificationSizeLimitInBytes();
+ int currentInstructionsSize = fieldLoadInstructionSize;
+ int implMethodsCommitted = 0;
+ for (DexEncodedMethod implMethod : implMethods) {
+ int packedSwitchPayloadSize =
+ (int)
+ (IntSwitch.basePackedSize(options.getInternalOutputMode())
+ + IntSwitch.packedPayloadSize(options.getInternalOutputMode(), methods.size()));
+ Code code = implMethod.getCode();
+ // We only do lambda merging for DEX. If we started doing lambda merging for CF, we would
+ // have to compute a size.
+ assert code.isDexCode();
+ int codeSize = code.asDexCode().codeSizeInBytes();
+ int estimatedMethodSize = currentInstructionsSize + codeSize + packedSwitchPayloadSize;
+ if (methods.size() > 0 && estimatedMethodSize > verificationSizeLimitInBytes) {
+ consumer.accept(methods, implMethodsCommitted, true);
+ currentInstructionsSize = fieldLoadInstructionSize;
+ implMethodsCommitted += methods.size();
+ methods = new ArrayList<>();
+ }
+ methods.add(implMethod);
+ currentInstructionsSize += codeSize;
+ }
+ consumer.accept(methods, implMethodsCommitted, false);
+ }
+
// Build a map of virtual methods with unique name/proto pointing to a list of methods
// from lambda classes implementing appropriate logic. The indices in the list correspond
// to lambda ids. Note that some of the slots in the lists may be empty, indicating the
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaVirtualMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaVirtualMethodSourceCode.java
index a89ee11..657a796 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaVirtualMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaVirtualMethodSourceCode.java
@@ -9,7 +9,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.code.ValueType;
@@ -22,6 +22,8 @@
private final DexItemFactory factory;
private final DexField idField;
private final List<DexEncodedMethod> implMethods;
+ private final DexMethod fallThroughMethod;
+ private final int keyStart;
KotlinLambdaVirtualMethodSourceCode(
DexItemFactory factory,
@@ -29,11 +31,15 @@
DexMethod method,
DexField idField,
List<DexEncodedMethod> implMethods,
+ DexMethod fallThroughMethod,
+ int keyStart,
Position callerPosition) {
super(groupClass, method, callerPosition);
this.factory = factory;
this.idField = idField;
this.implMethods = implMethods;
+ this.fallThroughMethod = fallThroughMethod;
+ this.keyStart = keyStart;
}
@Override
@@ -64,17 +70,21 @@
add(builder -> builder.addSwitch(idRegister, keys, fallthrough[0], offsets),
builder -> endsSwitch(builder, switchIndex, fallthrough[0], offsets));
- // Fallthrough treated as unreachable.
- fallthrough[0] = nextInstructionIndex();
- int nullRegister = nextRegister(ValueType.OBJECT);
- add(builder -> builder.addNullConst(nullRegister));
- add(builder -> builder.addThrow(nullRegister), endsBlock);
-
List<Value> arguments = new ArrayList<>(proto.parameters.values.length + 1);
+ fallthrough[0] = nextInstructionIndex();
+ if (fallThroughMethod == null) {
+ // Fallthrough treated as unreachable.
+ int nullRegister = nextRegister(ValueType.OBJECT);
+ add(builder -> builder.addNullConst(nullRegister));
+ add(builder -> builder.addThrow(nullRegister), endsBlock);
+ } else {
+ addMethodCall(fallThroughMethod, arguments, returnsValue, retRegister);
+ }
+
// Blocks for each lambda id.
for (int i = 0; i < implMethodCount; i++) {
- keys[i] = i;
+ keys[i] = keyStart + i;
DexEncodedMethod impl = implMethods.get(i);
if (impl == null) {
// Virtual method is missing in lambda class.
@@ -82,28 +92,31 @@
continue;
}
offsets[i] = nextInstructionIndex();
+ addMethodCall(impl.method, arguments, returnsValue, retRegister);
+ }
+ }
- // Emit fake call on `this` receiver.
- add(
- builder -> {
- if (arguments.isEmpty()) {
- arguments.add(builder.getReceiverValue());
- List<Value> argumentValues = builder.getArgumentValues();
- if (argumentValues != null) {
- arguments.addAll(builder.getArgumentValues());
- }
+ private void addMethodCall(
+ DexMethod method, List<Value> arguments, boolean returnsValue, int retRegister) {
+ // Emit fake call on `this` receiver.
+ add(
+ builder -> {
+ if (arguments.isEmpty()) {
+ arguments.add(builder.getReceiverValue());
+ List<Value> argumentValues = builder.getArgumentValues();
+ if (argumentValues != null) {
+ arguments.addAll(builder.getArgumentValues());
}
- builder.addInvoke(
- Type.VIRTUAL, impl.method, impl.method.proto, arguments, false /* isInterface */);
- });
-
- // Handle return value if needed.
- if (returnsValue) {
- add(builder -> builder.addMoveResult(retRegister));
- add(builder -> builder.addReturn(retRegister), endsBlock);
- } else {
- add(IRBuilder::addReturn, endsBlock);
- }
+ }
+ builder.addInvoke(
+ Invoke.Type.VIRTUAL, method, method.proto, arguments, false /* isInterface */);
+ });
+ // Handle return value if needed.
+ if (returnsValue) {
+ add(builder -> builder.addMoveResult(retRegister));
+ add(builder -> builder.addReturn(retRegister), endsBlock);
+ } else {
+ add(IRBuilder::addReturn, endsBlock);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java b/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java
index a8f3018..09790f1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java
@@ -20,6 +20,7 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.Action;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
/**
* An optimization that merges a method override (B.m()) into the method it overrides (A.m()).
@@ -41,8 +42,8 @@
private final AppView<AppInfoWithLiveness> appView;
private final IRConverter converter;
- private final ProgramMethodSet candidatesForInstanceOfOptimization =
- ProgramMethodSet.createSorted();
+ private final SortedProgramMethodSet candidatesForInstanceOfOptimization =
+ SortedProgramMethodSet.create();
public CheckCastAndInstanceOfMethodSpecialization(
AppView<AppInfoWithLiveness> appView, IRConverter converter) {
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
index 5036205..2c80105 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
@@ -32,8 +32,8 @@
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter;
+import com.android.tools.r8.utils.collections.ImmutableDeque;
import com.android.tools.r8.utils.collections.ImmutableInt2ReferenceSortedMap;
-import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.Opcodes;
@@ -250,7 +250,7 @@
instructions.add(new CfConstNull());
instructions.add(new CfReturn(ValueType.OBJECT));
instructions.add(nullDest);
- instructions.add(new CfFrame(locals, ImmutableList.of()));
+ instructions.add(new CfFrame(locals, ImmutableDeque.of()));
// if (arg instanceOf ReverseWrapper) { return ((ReverseWrapper) arg).wrapperField};
assert reverseWrapperField != null;
@@ -264,7 +264,7 @@
new CfFieldInstruction(Opcodes.GETFIELD, reverseWrapperField, reverseWrapperField));
instructions.add(new CfReturn(ValueType.fromDexType(reverseWrapperField.type)));
instructions.add(unwrapDest);
- instructions.add(new CfFrame(locals, ImmutableList.of()));
+ instructions.add(new CfFrame(locals, ImmutableDeque.of()));
// return new Wrapper(wrappedValue);
instructions.add(new CfNew(wrapperField.holder));
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/EmulateInterfaceSyntheticCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/EmulateInterfaceSyntheticCfCodeProvider.java
index e0579ed..7f26a19 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/EmulateInterfaceSyntheticCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/EmulateInterfaceSyntheticCfCodeProvider.java
@@ -22,8 +22,8 @@
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.utils.Pair;
+import com.android.tools.r8.utils.collections.ImmutableDeque;
import com.android.tools.r8.utils.collections.ImmutableInt2ReferenceSortedMap;
-import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.Opcodes;
@@ -81,7 +81,7 @@
for (Pair<DexType, DexMethod> dispatch : extraDispatchCases) {
// Type check basic block.
instructions.add(labels[nextLabel++]);
- instructions.add(new CfFrame(locals, ImmutableList.of()));
+ instructions.add(new CfFrame(locals, ImmutableDeque.of()));
instructions.add(new CfLoad(ValueType.fromDexType(interfaceType), 0));
instructions.add(new CfInstanceOf(dispatch.getFirst()));
instructions.add(new CfIf(If.Type.EQ, ValueType.INT, labels[nextLabel]));
@@ -96,7 +96,7 @@
// Branch with companion call.
instructions.add(labels[nextLabel]);
- instructions.add(new CfFrame(locals, ImmutableList.of()));
+ instructions.add(new CfFrame(locals, ImmutableDeque.of()));
instructions.add(new CfLoad(ValueType.fromDexType(interfaceType), 0));
loadExtraParameters(instructions);
instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, companionMethod, false));
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java
index 52cdb30..75358f7 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java
@@ -33,8 +33,8 @@
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldMappingData;
+import com.android.tools.r8.utils.collections.ImmutableDeque;
import com.android.tools.r8.utils.collections.ImmutableInt2ReferenceSortedMap;
-import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.Opcodes;
@@ -112,7 +112,7 @@
addCfInstructionsForAbstractValue(instructions, value, returnType);
instructions.add(new CfReturn(ValueType.fromDexType(returnType)));
instructions.add(dest);
- instructions.add(new CfFrame(locals, ImmutableList.of()));
+ instructions.add(new CfFrame(locals, ImmutableDeque.of()));
}
});
@@ -176,7 +176,7 @@
new CfInvoke(Opcodes.INVOKESPECIAL, factory.npeMethods.initWithMessage, false));
instructions.add(new CfThrow());
instructions.add(nullDest);
- instructions.add(new CfFrame(locals, ImmutableList.of()));
+ instructions.add(new CfFrame(locals, ImmutableDeque.of()));
// if (s.equals("A")) { return 1;}
// if (s.equals("B")) { return 2;}
@@ -192,7 +192,7 @@
instructions.add(new CfConstNumber(enumValueInfo.convertToInt(), ValueType.INT));
instructions.add(new CfReturn(ValueType.INT));
instructions.add(dest);
- instructions.add(new CfFrame(locals, ImmutableList.of()));
+ instructions.add(new CfFrame(locals, ImmutableDeque.of()));
});
// throw new IllegalArgumentException("No enum constant com.x.MyEnum." + s);
@@ -255,7 +255,7 @@
instructions.add(nullDest);
instructions.add(
new CfFrame(
- ImmutableInt2ReferenceSortedMap.<FrameType>builder().build(), ImmutableList.of()));
+ ImmutableInt2ReferenceSortedMap.<FrameType>builder().build(), ImmutableDeque.of()));
instructions.add(new CfFieldInstruction(Opcodes.GETSTATIC, utilityField, utilityField));
instructions.add(new CfReturn(ValueType.OBJECT));
return standardCfCodeFromInstructions(instructions);
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
index c7d94d4..3f6569e 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
@@ -24,7 +24,7 @@
protected final static Predicate<IRBuilder> doesNotEndBlock = x -> false;
protected final static Predicate<IRBuilder> endsBlock = x -> true;
- // TODO(b/146124603): Remove these fields as optimzations (e.g., merging) could invalidate them.
+ // TODO(b/146124603): Remove these fields as optimizations (e.g., merging) could invalidate them.
protected final DexType receiver;
protected final DexMethod method;
protected final DexProto proto;
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 e13331f..9ccc19c 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -163,6 +163,15 @@
String sourceDebug = getSourceDebugExtension(clazz.annotations());
writer.visitSource(clazz.sourceFile != null ? clazz.sourceFile.toString() : null, sourceDebug);
int version = getClassFileVersion(clazz);
+ if (version >= V1_8) {
+ // JDK8 and after ignore ACC_SUPER so unset it.
+ clazz.accessFlags.unsetSuper();
+ } else {
+ // In all other cases set the super bit as D8/R8 do not support targeting pre 1.0.2 JDKs.
+ if (!clazz.accessFlags.isInterface()) {
+ clazz.accessFlags.setSuper();
+ }
+ }
int access = clazz.accessFlags.getAsCfAccessFlags();
String desc = namingLens.lookupDescriptor(clazz.type).toString();
String name = namingLens.lookupInternalName(clazz.type);
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 060f171..448a8fa 100644
--- a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
@@ -441,7 +441,7 @@
// desugared lambdas this is a conservative estimate, as we don't track if the generated
// lambda classes survive into the output. As multi-interface lambda expressions are rare
// this is not a big deal.
- Set<DexCallSite> liveCallSites = appView.appInfo().callSites;
+ Set<DexCallSite> liveCallSites = appView.appInfo().callSites.keySet();
// If the input program contains a multi-interface lambda expression that implements
// interface methods with different protos, we need to make sure tha the implemented lambda
// methods are renamed to the same name.
diff --git a/src/main/java/com/android/tools/r8/optimize/BridgeHoisting.java b/src/main/java/com/android/tools/r8/optimize/BridgeHoisting.java
index c19bcd2..038d078 100644
--- a/src/main/java/com/android/tools/r8/optimize/BridgeHoisting.java
+++ b/src/main/java/com/android/tools/r8/optimize/BridgeHoisting.java
@@ -379,6 +379,11 @@
}
@Override
+ public boolean hasCodeRewritings() {
+ return getPrevious().hasCodeRewritings();
+ }
+
+ @Override
public boolean isLegitimateToHaveEmptyMappings() {
return true;
}
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 dcd003c6..b8022b9 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -16,13 +16,14 @@
import com.android.tools.r8.graph.FieldAccessInfoCollection;
import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.MethodAccessInfoCollection;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.BiForEachable;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
-import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
@@ -127,50 +128,71 @@
return appView.appInfo().unsafeResolveMethodDueToDexFormat(method).getSingleTarget();
}
+ private void computeMethodRebinding(MethodAccessInfoCollection methodAccessInfoCollection) {
+ // Virtual invokes are on classes, so use class resolution.
+ computeMethodRebinding(
+ methodAccessInfoCollection::forEachVirtualInvoke, this::classLookup, Type.VIRTUAL);
+ // Interface invokes are always on interfaces, so use interface resolution.
+ computeMethodRebinding(
+ methodAccessInfoCollection::forEachInterfaceInvoke, this::interfaceLookup, Type.INTERFACE);
+ // Super invokes can be on both kinds, decide using the holder class.
+ computeMethodRebinding(
+ methodAccessInfoCollection::forEachSuperInvoke, this::anyLookup, Type.SUPER);
+ // Direct invokes (private/constructor) can also be on both kinds.
+ computeMethodRebinding(
+ methodAccessInfoCollection::forEachDirectInvoke, this::anyLookup, Type.DIRECT);
+ // Likewise static invokes.
+ computeMethodRebinding(
+ methodAccessInfoCollection::forEachStaticInvoke, this::anyLookup, Type.STATIC);
+ }
+
private void computeMethodRebinding(
- Map<DexMethod, ProgramMethodSet> methodsWithContexts,
+ BiForEachable<DexMethod, ProgramMethodSet> methodsWithContexts,
Function<DexMethod, DexEncodedMethod> lookupTarget,
Type invokeType) {
- for (DexMethod method : methodsWithContexts.keySet()) {
- // We can safely ignore array types, as the corresponding methods are defined in a library.
- if (!method.holder.isClassType()) {
- continue;
- }
- DexClass originalClass = appView.definitionFor(method.holder);
- if (originalClass == null || originalClass.isNotProgramClass()) {
- continue;
- }
- DexEncodedMethod target = lookupTarget.apply(method);
- // TODO(b/128404854) Rebind to the lowest library class or program class. For now we allow
- // searching in library for methods, but this should be done on classpath instead.
- if (target != null && target.method != method) {
- DexClass targetClass = appView.definitionFor(target.holder());
- if (originalClass.isProgramClass()) {
- // In Java bytecode, it is only possible to target interface methods that are in one of
- // the immediate super-interfaces via a super-invocation (see IndirectSuperInterfaceTest).
- // To avoid introducing an IncompatibleClassChangeError at runtime we therefore insert a
- // bridge method when we are about to rebind to an interface method that is not the
- // original target.
- if (needsBridgeForInterfaceMethod(originalClass, targetClass, invokeType)) {
- target =
- insertBridgeForInterfaceMethod(
- method, target, originalClass.asProgramClass(), targetClass, lookupTarget);
+ methodsWithContexts.forEach(
+ (method, contexts) -> {
+ // We can safely ignore array types, as the corresponding methods are defined in a
+ // library.
+ if (!method.holder.isClassType()) {
+ return;
}
+ DexClass originalClass = appView.definitionFor(method.holder);
+ if (originalClass == null || originalClass.isNotProgramClass()) {
+ return;
+ }
+ DexEncodedMethod target = lookupTarget.apply(method);
+ // TODO(b/128404854) Rebind to the lowest library class or program class. For now we allow
+ // searching in library for methods, but this should be done on classpath instead.
+ if (target == null || target.method == method) {
+ return;
+ }
+ DexClass targetClass = appView.definitionFor(target.holder());
+ if (originalClass.isProgramClass()) {
+ // In Java bytecode, it is only possible to target interface methods that are in one of
+ // the immediate super-interfaces via a super-invocation (see
+ // IndirectSuperInterfaceTest).
+ // To avoid introducing an IncompatibleClassChangeError at runtime we therefore insert a
+ // bridge method when we are about to rebind to an interface method that is not the
+ // original target.
+ if (needsBridgeForInterfaceMethod(originalClass, targetClass, invokeType)) {
+ target =
+ insertBridgeForInterfaceMethod(
+ method, target, originalClass.asProgramClass(), targetClass, lookupTarget);
+ }
- // If the target class is not public but the targeted method is, we might run into
- // visibility problems when rebinding.
- final DexEncodedMethod finalTarget = target;
- ProgramMethodSet contexts = methodsWithContexts.get(method);
- if (contexts.stream()
- .anyMatch(context -> mayNeedBridgeForVisibility(context, finalTarget))) {
- target =
- insertBridgeForVisibilityIfNeeded(
- method, target, originalClass, targetClass, lookupTarget);
+ // If the target class is not public but the targeted method is, we might run into
+ // visibility problems when rebinding.
+ final DexEncodedMethod finalTarget = target;
+ if (contexts.stream()
+ .anyMatch(context -> mayNeedBridgeForVisibility(context, finalTarget))) {
+ target =
+ insertBridgeForVisibilityIfNeeded(
+ method, target, originalClass, targetClass, lookupTarget);
+ }
}
- }
- builder.map(method, lens.lookupMethod(validTargetFor(target.method, method)), invokeType);
- }
- }
+ builder.map(method, lens.lookupMethod(validTargetFor(target.method, method)), invokeType);
+ });
}
private boolean needsBridgeForInterfaceMethod(
@@ -337,16 +359,7 @@
public GraphLens run() {
AppInfoWithLiveness appInfo = appView.appInfo();
- // Virtual invokes are on classes, so use class resolution.
- computeMethodRebinding(appInfo.virtualInvokes, this::classLookup, Type.VIRTUAL);
- // Interface invokes are always on interfaces, so use interface resolution.
- computeMethodRebinding(appInfo.interfaceInvokes, this::interfaceLookup, Type.INTERFACE);
- // Super invokes can be on both kinds, decide using the holder class.
- computeMethodRebinding(appInfo.superInvokes, this::anyLookup, Type.SUPER);
- // Direct invokes (private/constructor) can also be on both kinds.
- computeMethodRebinding(appInfo.directInvokes, this::anyLookup, Type.DIRECT);
- // Likewise static invokes.
- computeMethodRebinding(appInfo.staticInvokes, this::anyLookup, Type.STATIC);
+ computeMethodRebinding(appInfo.getMethodAccessInfoCollection());
computeFieldRebinding();
GraphLens lens = builder.build(this.lens);
appInfo.getFieldAccessInfoCollection().flattenAccessContexts();
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
new file mode 100644
index 0000000..9e3cbce
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
@@ -0,0 +1,170 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize;
+
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.FieldAccessInfo;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
+import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+/**
+ * This lens is used to populate the rebound field and method reference during lookup, such that
+ * both the non-rebound and rebound field and method references are available to all descendants of
+ * this lens.
+ */
+public class MemberRebindingIdentityLens extends NonIdentityGraphLens {
+
+ private final Map<DexField, DexField> nonReboundFieldReferenceToDefinitionMap;
+ private final Map<DexMethod, DexMethod> nonReboundMethodReferenceToDefinitionMap;
+
+ private MemberRebindingIdentityLens(
+ Map<DexField, DexField> nonReboundFieldReferenceToDefinitionMap,
+ Map<DexMethod, DexMethod> nonReboundMethodReferenceToDefinitionMap,
+ DexItemFactory dexItemFactory,
+ GraphLens previousLens) {
+ super(dexItemFactory, previousLens);
+ assert !previousLens.hasCodeRewritings();
+ this.nonReboundFieldReferenceToDefinitionMap = nonReboundFieldReferenceToDefinitionMap;
+ this.nonReboundMethodReferenceToDefinitionMap = nonReboundMethodReferenceToDefinitionMap;
+ }
+
+ public static Builder builder(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ return new Builder(appView);
+ }
+
+ @Override
+ protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
+ assert previous.getReboundReference() == null;
+ return FieldLookupResult.builder(this)
+ .setReference(previous.getReference())
+ .setReboundReference(getReboundFieldReference(previous.getReference()))
+ .build();
+ }
+
+ private DexField getReboundFieldReference(DexField field) {
+ return nonReboundFieldReferenceToDefinitionMap.getOrDefault(field, field);
+ }
+
+ @Override
+ public MethodLookupResult internalDescribeLookupMethod(
+ MethodLookupResult previous, DexMethod context) {
+ assert previous.getReboundReference() == null;
+ return MethodLookupResult.builder(this)
+ .setReference(previous.getReference())
+ .setReboundReference(getReboundMethodReference(previous.getReference()))
+ .setPrototypeChanges(previous.getPrototypeChanges())
+ .setType(previous.getType())
+ .build();
+ }
+
+ private DexMethod getReboundMethodReference(DexMethod method) {
+ return nonReboundMethodReferenceToDefinitionMap.getOrDefault(method, method);
+ }
+
+ @Override
+ public DexType getOriginalType(DexType type) {
+ return getPrevious().getOriginalType(type);
+ }
+
+ @Override
+ public DexField getOriginalFieldSignature(DexField field) {
+ return getPrevious().getOriginalFieldSignature(field);
+ }
+
+ @Override
+ public DexMethod getOriginalMethodSignature(DexMethod method) {
+ return getPrevious().getOriginalMethodSignature(method);
+ }
+
+ @Override
+ public DexField getRenamedFieldSignature(DexField originalField) {
+ return getPrevious().getRenamedFieldSignature(originalField);
+ }
+
+ @Override
+ public DexMethod getRenamedMethodSignature(DexMethod originalMethod, GraphLens applied) {
+ return getPrevious().getRenamedMethodSignature(originalMethod, applied);
+ }
+
+ @Override
+ public final DexType internalDescribeLookupClassType(DexType previous) {
+ return previous;
+ }
+
+ @Override
+ protected DexMethod internalGetPreviousMethodSignature(DexMethod method) {
+ return method;
+ }
+
+ @Override
+ public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(DexMethod method) {
+ return getPrevious().lookupPrototypeChangesForMethodDefinition(method);
+ }
+
+ @Override
+ public boolean isContextFreeForMethods() {
+ return getPrevious().isContextFreeForMethods();
+ }
+
+ public static class Builder {
+
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
+ private final Map<DexField, DexField> nonReboundFieldReferenceToDefinitionMap =
+ new IdentityHashMap<>();
+ private final Map<DexMethod, DexMethod> nonReboundMethodReferenceToDefinitionMap =
+ new IdentityHashMap<>();
+
+ private Builder(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ this.appView = appView;
+ }
+
+ void recordNonReboundFieldAccesses(FieldAccessInfo fieldAccessInfo) {
+ fieldAccessInfo.forEachIndirectAccess(
+ nonReboundFieldReference ->
+ recordNonReboundFieldAccess(nonReboundFieldReference, fieldAccessInfo.getField()));
+ }
+
+ private void recordNonReboundFieldAccess(
+ DexField nonReboundFieldReference, DexField reboundFieldReference) {
+ nonReboundFieldReferenceToDefinitionMap.put(nonReboundFieldReference, reboundFieldReference);
+ }
+
+ void recordMethodAccess(DexMethod reference) {
+ if (reference.getHolderType().isArrayType()) {
+ return;
+ }
+ DexClass holder = appView.contextIndependentDefinitionFor(reference.getHolderType());
+ if (holder != null) {
+ SingleResolutionResult resolutionResult =
+ appView.appInfo().resolveMethodOn(holder, reference).asSingleResolution();
+ if (resolutionResult != null && resolutionResult.getResolvedHolder() != holder) {
+ nonReboundMethodReferenceToDefinitionMap.put(
+ reference, resolutionResult.getResolvedMethod().getReference());
+ }
+ }
+ }
+
+ MemberRebindingIdentityLens build() {
+ // This intentionally does not return null when the maps are empty. In this case there are no
+ // non-rebound field or method references, but the member rebinding lens is still needed to
+ // populate the rebound reference during field and method lookup.
+ return new MemberRebindingIdentityLens(
+ nonReboundFieldReferenceToDefinitionMap,
+ nonReboundMethodReferenceToDefinitionMap,
+ appView.dexItemFactory(),
+ appView.graphLens());
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
new file mode 100644
index 0000000..944af37
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
@@ -0,0 +1,249 @@
+// 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.optimize;
+
+import com.android.tools.r8.graph.AbstractAccessContexts.ConcreteAccessContexts;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.FieldAccessInfoCollection;
+import com.android.tools.r8.graph.FieldAccessInfoCollectionImpl;
+import com.android.tools.r8.graph.FieldAccessInfoImpl;
+import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
+import com.android.tools.r8.graph.MethodAccessInfoCollection;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import com.google.common.collect.Sets;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+public class MemberRebindingIdentityLensFactory {
+
+ /**
+ * In order to construct an instance of {@link MemberRebindingIdentityLens} we need a mapping from
+ * non-rebound field and method references to their definitions.
+ *
+ * <p>If shrinking or minification is enabled, we retrieve these from {@link AppInfoWithLiveness}.
+ * Otherwise we apply the {@link NonReboundMemberReferencesRegistry} below to all code objects to
+ * compute the mapping.
+ */
+ public static MemberRebindingIdentityLens create(
+ AppView<? extends AppInfoWithClassHierarchy> appView, ExecutorService executorService)
+ throws ExecutionException {
+ FieldAccessInfoCollection<?> fieldAccessInfoCollection;
+ MethodAccessInfoCollection methodAccessInfoCollection;
+ if (appView.appInfo().hasLiveness()
+ && appView.options().testing.alwaysUseExistingAccessInfoCollectionsInMemberRebinding) {
+ AppInfoWithLiveness appInfo = appView.appInfo().withLiveness();
+ fieldAccessInfoCollection = appInfo.getFieldAccessInfoCollection();
+ methodAccessInfoCollection = appInfo.getMethodAccessInfoCollection();
+ } else {
+ FieldAccessInfoCollectionImpl mutableFieldAccessInfoCollection =
+ new FieldAccessInfoCollectionImpl(new ConcurrentHashMap<>());
+ MethodAccessInfoCollection.ConcurrentBuilder methodAccessInfoCollectionBuilder =
+ MethodAccessInfoCollection.concurrentBuilder();
+ initializeMemberAccessInfoCollectionsForMemberRebinding(
+ appView,
+ mutableFieldAccessInfoCollection,
+ methodAccessInfoCollectionBuilder,
+ executorService);
+ fieldAccessInfoCollection = mutableFieldAccessInfoCollection;
+ methodAccessInfoCollection = methodAccessInfoCollectionBuilder.build();
+ }
+ return create(appView, fieldAccessInfoCollection, methodAccessInfoCollection);
+ }
+
+ public static MemberRebindingIdentityLens create(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ FieldAccessInfoCollection<?> fieldAccessInfoCollection,
+ MethodAccessInfoCollection methodAccessInfoCollection) {
+ MemberRebindingIdentityLens.Builder builder = MemberRebindingIdentityLens.builder(appView);
+ fieldAccessInfoCollection.forEach(builder::recordNonReboundFieldAccesses);
+ methodAccessInfoCollection.forEachMethodReference(builder::recordMethodAccess);
+ return builder.build();
+ }
+
+ /**
+ * Applies {@link NonReboundMemberReferencesRegistry} to all code objects to construct a mapping
+ * from non-rebound field references to their definition.
+ */
+ private static void initializeMemberAccessInfoCollectionsForMemberRebinding(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ FieldAccessInfoCollectionImpl fieldAccessInfoCollection,
+ MethodAccessInfoCollection.ConcurrentBuilder methodAccessInfoCollectionBuilder,
+ ExecutorService executorService)
+ throws ExecutionException {
+ Set<DexField> seenFieldReferences = Sets.newConcurrentHashSet();
+ Set<DexMethod> seenMethodReferences = Sets.newConcurrentHashSet();
+ ThreadUtils.processItems(
+ appView.appInfo()::forEachMethod,
+ method ->
+ new NonReboundMemberReferencesRegistry(
+ appView,
+ method,
+ fieldAccessInfoCollection,
+ methodAccessInfoCollectionBuilder,
+ seenFieldReferences,
+ seenMethodReferences)
+ .accept(method),
+ executorService);
+ }
+
+ private static class NonReboundMemberReferencesRegistry extends UseRegistry {
+
+ private final AppInfoWithClassHierarchy appInfo;
+ private final ProgramMethod context;
+ private final FieldAccessInfoCollectionImpl fieldAccessInfoCollection;
+ private final MethodAccessInfoCollection.ConcurrentBuilder methodAccessInfoCollectionBuilder;
+ private final Set<DexField> seenFieldReferences;
+ private final Set<DexMethod> seenMethodReferences;
+
+ public NonReboundMemberReferencesRegistry(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ ProgramMethod context,
+ FieldAccessInfoCollectionImpl fieldAccessInfoCollection,
+ MethodAccessInfoCollection.ConcurrentBuilder methodAccessInfoCollectionBuilder,
+ Set<DexField> seenFieldReferences,
+ Set<DexMethod> seenMethodReferences) {
+ super(appView.dexItemFactory());
+ this.appInfo = appView.appInfo();
+ this.context = context;
+ this.fieldAccessInfoCollection = fieldAccessInfoCollection;
+ this.methodAccessInfoCollectionBuilder = methodAccessInfoCollectionBuilder;
+ this.seenFieldReferences = seenFieldReferences;
+ this.seenMethodReferences = seenMethodReferences;
+ }
+
+ @Override
+ public void registerInstanceFieldRead(DexField field) {
+ registerFieldAccess(field);
+ }
+
+ @Override
+ public void registerInstanceFieldWrite(DexField field) {
+ registerFieldAccess(field);
+ }
+
+ @Override
+ public void registerStaticFieldRead(DexField field) {
+ registerFieldAccess(field);
+ }
+
+ @Override
+ public void registerStaticFieldWrite(DexField field) {
+ registerFieldAccess(field);
+ }
+
+ private void registerFieldAccess(DexField field) {
+ if (!seenFieldReferences.add(field)) {
+ return;
+ }
+ SuccessfulFieldResolutionResult resolutionResult =
+ appInfo.resolveField(field).asSuccessfulResolution();
+ if (resolutionResult == null) {
+ return;
+ }
+ DexField reboundReference = resolutionResult.getResolvedField().toReference();
+ if (field == reboundReference) {
+ // For the purpose of member rebinding, we don't care about already rebound references.
+ return;
+ }
+ FieldAccessInfoImpl fieldAccessInfo =
+ fieldAccessInfoCollection.computeIfAbsent(reboundReference, FieldAccessInfoImpl::new);
+ synchronized (fieldAccessInfo) {
+ // Record the fact that there is a non-rebound access to the given field. We don't
+ // distinguish between non-rebound reads and writes, so we just record it as a read.
+ ConcreteAccessContexts accessContexts =
+ fieldAccessInfo.getReadsWithContexts().isConcrete()
+ ? fieldAccessInfo.getReadsWithContexts().asConcrete()
+ : new ConcreteAccessContexts();
+ // For the purpose of member rebinding, we don't care about the access contexts, so we
+ // simply use the empty set.
+ accessContexts.getAccessesWithContexts().put(field, ProgramMethodSet.empty());
+ }
+ }
+
+ @Override
+ public void registerInvokeDirect(DexMethod method) {
+ registerInvokeMethod(method, methodAccessInfoCollectionBuilder.getDirectInvokes());
+ }
+
+ @Override
+ public void registerInvokeInterface(DexMethod method) {
+ registerInvokeMethod(method, methodAccessInfoCollectionBuilder.getInterfaceInvokes());
+ }
+
+ @Override
+ public void registerInvokeStatic(DexMethod method) {
+ registerInvokeMethod(method, methodAccessInfoCollectionBuilder.getStaticInvokes());
+ }
+
+ @Override
+ public void registerInvokeSuper(DexMethod method) {
+ registerInvokeMethod(method, methodAccessInfoCollectionBuilder.getSuperInvokes());
+ }
+
+ @Override
+ public void registerInvokeVirtual(DexMethod method) {
+ registerInvokeMethod(method, methodAccessInfoCollectionBuilder.getVirtualInvokes());
+ }
+
+ private void registerInvokeMethod(DexMethod method, Map<DexMethod, ProgramMethodSet> invokes) {
+ if (!seenMethodReferences.add(method)) {
+ return;
+ }
+ if (method.getHolderType().isArrayType()) {
+ return;
+ }
+ DexClass holder = appInfo.definitionFor(method.getHolderType(), context);
+ if (holder == null) {
+ return;
+ }
+ SingleResolutionResult resolutionResult =
+ appInfo.resolveMethodOn(holder, method).asSingleResolution();
+ if (resolutionResult == null) {
+ return;
+ }
+ DexMethod reboundReference = resolutionResult.getResolvedMethod().getReference();
+ if (method == reboundReference) {
+ // For the purpose of member rebinding, we don't care about already rebound references.
+ return;
+ }
+ // For the purpose of member rebinding, we don't care about the access contexts, so we
+ // simply use the empty set.
+ invokes.put(method, ProgramMethodSet.empty());
+ }
+
+ @Override
+ public void registerInitClass(DexType type) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void registerNewInstance(DexType type) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void registerTypeReference(DexType type) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void registerInstanceOf(DexType type) {
+ // Intentionally empty.
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
index 2e55916..a6a6021 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
@@ -86,17 +86,19 @@
}
@Override
- public GraphLensLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
- GraphLensLookupResult lookup = previousLens.lookupMethod(method, context, type);
- Map<DexMethod, DexMethod> methodMap = methodMaps.getOrDefault(type, Collections.emptyMap());
- DexMethod newMethod = methodMap.get(lookup.getMethod());
+ public MethodLookupResult internalDescribeLookupMethod(
+ MethodLookupResult previous, DexMethod context) {
+ Map<DexMethod, DexMethod> methodMap =
+ methodMaps.getOrDefault(previous.getType(), Collections.emptyMap());
+ DexMethod newMethod = methodMap.get(previous.getReference());
if (newMethod == null) {
- return lookup;
+ return previous;
}
- return new GraphLensLookupResult(
- newMethod,
- mapInvocationType(newMethod, method, lookup.getType()),
- lookup.getPrototypeChanges());
+ return MethodLookupResult.builder(this)
+ .setReference(newMethod)
+ .setPrototypeChanges(previous.getPrototypeChanges())
+ .setType(mapInvocationType(newMethod, previous.getReference(), previous.getType()))
+ .build();
}
@Override
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 1e8eff2..d36cbee 100644
--- a/src/main/java/com/android/tools/r8/optimize/PublicizerLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/PublicizerLens.java
@@ -40,19 +40,22 @@
}
@Override
- public GraphLensLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
- GraphLensLookupResult lookup = previousLens.lookupMethod(method, context, type);
- if (lookup.getType() == Type.DIRECT && publicizedMethods.contains(lookup.getMethod())) {
- assert publicizedMethodIsPresentOnHolder(lookup.getMethod(), context);
- return new GraphLensLookupResult(
- lookup.getMethod(), Type.VIRTUAL, lookup.getPrototypeChanges());
+ public MethodLookupResult internalDescribeLookupMethod(
+ MethodLookupResult previous, DexMethod context) {
+ if (previous.getType() == Type.DIRECT && publicizedMethods.contains(previous.getReference())) {
+ assert publicizedMethodIsPresentOnHolder(previous.getReference(), context);
+ return MethodLookupResult.builder(this)
+ .setReference(previous.getReference())
+ .setPrototypeChanges(previous.getPrototypeChanges())
+ .setType(Type.VIRTUAL)
+ .build();
}
- return lookup;
+ return previous;
}
private boolean publicizedMethodIsPresentOnHolder(DexMethod method, DexMethod context) {
- GraphLensLookupResult lookup = appView.graphLens().lookupMethod(method, context, Type.VIRTUAL);
- DexMethod signatureInCurrentWorld = lookup.getMethod();
+ MethodLookupResult lookup = appView.graphLens().lookupMethod(method, context, Type.VIRTUAL);
+ DexMethod signatureInCurrentWorld = lookup.getReference();
DexClass clazz = appView.definitionFor(signatureInCurrentWorld.holder);
assert clazz != null;
DexEncodedMethod actualEncodedTarget = clazz.lookupVirtualMethod(signatureInCurrentWorld);
diff --git a/src/main/java/com/android/tools/r8/relocator/RelocatorCommand.java b/src/main/java/com/android/tools/r8/relocator/RelocatorCommand.java
index b8355c3..7da1116 100644
--- a/src/main/java/com/android/tools/r8/relocator/RelocatorCommand.java
+++ b/src/main/java/com/android/tools/r8/relocator/RelocatorCommand.java
@@ -157,8 +157,6 @@
options.dataResourceConsumer = consumer.getDataResourceConsumer();
// Set debug to ensure that we are writing all information to the application writer.
options.debug = true;
- // We need to read stack maps since we are not processing anything.
- options.testing.readInputStackMaps = true;
return options;
}
diff --git a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
index 848008c..5868aa8 100644
--- a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
+++ b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
@@ -4,19 +4,27 @@
package com.android.tools.r8.repackaging;
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+import static com.android.tools.r8.utils.DescriptorUtils.INNER_CLASS_SEPARATOR;
+
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.ProgramPackage;
import com.android.tools.r8.graph.ProgramPackageCollection;
+import com.android.tools.r8.graph.SortedProgramPackageCollection;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.Timing;
-import java.util.IdentityHashMap;
-import java.util.Map;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -60,29 +68,92 @@
return null;
}
- // For each package, find the set of classes that can be repackaged, and move them to the
- // desired namespace.
- Map<DexType, DexType> mappings = new IdentityHashMap<>();
- for (ProgramPackage pkg : ProgramPackageCollection.createWithAllProgramClasses(appView)) {
- Iterable<DexProgramClass> classesToRepackage =
- computeClassesToRepackage(pkg, executorService);
- String newPackageDescriptor = getNewPackageDescriptor(pkg);
- for (DexProgramClass classToRepackage : classesToRepackage) {
- // TODO(b/165783399): Handle class collisions when different packages are repackaged into
- // the same package.
- DexType newType =
- classToRepackage.getType().replacePackage(newPackageDescriptor, dexItemFactory);
- mappings.put(classToRepackage.getType(), newType);
- }
- // TODO(b/165783399): Investigate if repackaging can lead to different dynamic dispatch. See,
- // for example, CrossPackageInvokeSuperToPackagePrivateMethodTest.
- }
+ BiMap<DexType, DexType> mappings = HashBiMap.create();
+ Set<String> seenPackageDescriptors = new HashSet<>();
+ ProgramPackageCollection packages =
+ SortedProgramPackageCollection.createWithAllProgramClasses(appView);
+ processPackagesInDesiredLocation(packages, mappings, seenPackageDescriptors);
+ processRemainingPackages(packages, mappings, seenPackageDescriptors, executorService);
+ mappings.entrySet().removeIf(entry -> entry.getKey() == entry.getValue());
if (mappings.isEmpty()) {
return null;
}
return new RepackagingTreeFixer(appBuilder, appView, mappings).run();
}
+ private void processPackagesInDesiredLocation(
+ ProgramPackageCollection packages,
+ BiMap<DexType, DexType> mappings,
+ Set<String> seenPackageDescriptors) {
+ // For each package that is already in the desired location, record all the classes from the
+ // package in the mapping for collision detection.
+ Iterator<ProgramPackage> iterator = packages.iterator();
+ while (iterator.hasNext()) {
+ ProgramPackage pkg = iterator.next();
+ String newPackageDescriptor = getNewPackageDescriptor(pkg, seenPackageDescriptors);
+ if (pkg.getPackageDescriptor().equals(newPackageDescriptor)) {
+ for (DexProgramClass alreadyRepackagedClass : pkg) {
+ mappings.put(alreadyRepackagedClass.getType(), alreadyRepackagedClass.getType());
+ }
+ seenPackageDescriptors.add(newPackageDescriptor);
+ iterator.remove();
+ }
+ }
+ }
+
+ private void processRemainingPackages(
+ ProgramPackageCollection packages,
+ BiMap<DexType, DexType> mappings,
+ Set<String> seenPackageDescriptors,
+ ExecutorService executorService)
+ throws ExecutionException {
+ // For each package, find the set of classes that can be repackaged, and move them to the
+ // desired package.
+ for (ProgramPackage pkg : packages) {
+ // Already processed packages should have been removed.
+ String newPackageDescriptor = getNewPackageDescriptor(pkg, seenPackageDescriptors);
+ assert !pkg.getPackageDescriptor().equals(newPackageDescriptor);
+
+ Iterable<DexProgramClass> classesToRepackage =
+ computeClassesToRepackage(pkg, executorService);
+ for (DexProgramClass classToRepackage : classesToRepackage) {
+ processClass(classToRepackage, pkg, newPackageDescriptor, mappings);
+ }
+
+ seenPackageDescriptors.add(newPackageDescriptor);
+ // TODO(b/165783399): Investigate if repackaging can lead to different dynamic dispatch. See,
+ // for example, CrossPackageInvokeSuperToPackagePrivateMethodTest.
+ }
+ }
+
+ private void processClass(
+ DexProgramClass classToRepackage,
+ ProgramPackage pkg,
+ String newPackageDescriptor,
+ BiMap<DexType, DexType> mappings) {
+ // Check if the class has already been processed.
+ if (mappings.containsKey(classToRepackage.getType())) {
+ return;
+ }
+
+ // Always repackage outer classes first, if any.
+ InnerClassAttribute innerClassAttribute = classToRepackage.getInnerClassAttributeForThisClass();
+ DexProgramClass outerClass = null;
+ if (innerClassAttribute != null && innerClassAttribute.getOuter() != null) {
+ outerClass = asProgramClassOrNull(appView.definitionFor(innerClassAttribute.getOuter()));
+ if (outerClass != null) {
+ if (pkg.contains(outerClass)) {
+ processClass(outerClass, pkg, newPackageDescriptor, mappings);
+ } else {
+ outerClass = null;
+ }
+ }
+ }
+ mappings.put(
+ classToRepackage.getType(),
+ getRepackagedType(classToRepackage, outerClass, newPackageDescriptor, mappings));
+ }
+
private Iterable<DexProgramClass> computeClassesToRepackage(
ProgramPackage pkg, ExecutorService executorService) throws ExecutionException {
RepackagingConstraintGraph constraintGraph = new RepackagingConstraintGraph(appView, pkg);
@@ -94,13 +165,46 @@
return constraintGraph.computeClassesToRepackage();
}
- private String getNewPackageDescriptor(ProgramPackage pkg) {
+ private String getNewPackageDescriptor(ProgramPackage pkg, Set<String> seenPackageDescriptors) {
String newPackageDescriptor =
StringUtils.replaceAll(proguardConfiguration.getPackagePrefix(), ".", "/");
- if (proguardConfiguration.getPackageObfuscationMode().isFlattenPackageHierarchy()) {
- // TODO(b/165783399): Handle collisions among package names.
- newPackageDescriptor += "/" + pkg.getLastPackageName();
+ if (proguardConfiguration.getPackageObfuscationMode().isRepackageClasses()) {
+ return newPackageDescriptor;
}
- return newPackageDescriptor;
+ if (!newPackageDescriptor.isEmpty()) {
+ newPackageDescriptor += "/";
+ }
+ newPackageDescriptor += pkg.getLastPackageName();
+ String finalPackageDescriptor = newPackageDescriptor;
+ for (int i = 1; seenPackageDescriptors.contains(finalPackageDescriptor); i++) {
+ finalPackageDescriptor = newPackageDescriptor + INNER_CLASS_SEPARATOR + i;
+ }
+ return finalPackageDescriptor;
+ }
+
+ private DexType getRepackagedType(
+ DexProgramClass clazz,
+ DexProgramClass outerClass,
+ String newPackageDescriptor,
+ BiMap<DexType, DexType> mappings) {
+ DexType repackagedDexType =
+ clazz.getType().replacePackage(newPackageDescriptor, dexItemFactory);
+ if (outerClass != null) {
+ String simpleName = clazz.getType().getSimpleName();
+ String outerClassSimpleName = outerClass.getType().getSimpleName();
+ if (simpleName.startsWith(outerClassSimpleName + INNER_CLASS_SEPARATOR)) {
+ String newSimpleName =
+ mappings.get(outerClass.getType()).getSimpleName()
+ + simpleName.substring(outerClassSimpleName.length());
+ repackagedDexType = repackagedDexType.withSimpleName(newSimpleName, dexItemFactory);
+ }
+ }
+ DexType finalRepackagedDexType = repackagedDexType;
+ for (int i = 1; mappings.inverse().containsKey(finalRepackagedDexType); i++) {
+ finalRepackagedDexType =
+ repackagedDexType.addSuffix(
+ Character.toString(INNER_CLASS_SEPARATOR) + i, dexItemFactory);
+ }
+ return finalRepackagedDexType;
}
}
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingAnnotationTracer.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingAnnotationTracer.java
new file mode 100644
index 0000000..f52b8b7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingAnnotationTracer.java
@@ -0,0 +1,108 @@
+// 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.repackaging;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationElement;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexEncodedAnnotation;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.ParameterAnnotationsList;
+
+public class RepackagingAnnotationTracer {
+
+ private final AppInfoWithClassHierarchy appInfo;
+ private final RepackagingUseRegistry registry;
+
+ public RepackagingAnnotationTracer(
+ AppView<? extends AppInfoWithClassHierarchy> appView, RepackagingUseRegistry registry) {
+ this.appInfo = appView.appInfo();
+ this.registry = registry;
+ }
+
+ public void trace(DexAnnotationSet annotations) {
+ annotations.forEach(this::traceAnnotation);
+ }
+
+ public void trace(ParameterAnnotationsList annotations) {
+ annotations.forEachAnnotation(this::traceAnnotation);
+ }
+
+ private void traceAnnotation(DexAnnotation annotation) {
+ traceEncodedAnnotation(annotation.annotation);
+ }
+
+ private void traceEncodedAnnotation(DexEncodedAnnotation annotation) {
+ registry.registerTypeReference(annotation.type);
+ annotation.forEachElement(this::traceAnnotationElement);
+ }
+
+ private void traceAnnotationElement(DexAnnotationElement element) {
+ traceDexValue(element.value);
+ }
+
+ private void traceDexValue(DexValue value) {
+ switch (value.getValueKind()) {
+ case BOOLEAN:
+ case BYTE:
+ case CHAR:
+ case DOUBLE:
+ case FLOAT:
+ case INT:
+ case LONG:
+ case NULL:
+ case SHORT:
+ case STRING:
+ break;
+
+ case ANNOTATION:
+ traceEncodedAnnotation(value.asDexValueAnnotation().getValue());
+ break;
+
+ case ARRAY:
+ value.asDexValueArray().forEachElement(this::traceDexValue);
+ break;
+
+ case ENUM:
+ registry.registerFieldAccess(value.asDexValueEnum().getValue());
+ break;
+
+ case FIELD:
+ registry.registerFieldAccess(value.asDexValueField().getValue());
+ break;
+
+ case METHOD:
+ registry.registerMethodReference(value.asDexValueMethod().getValue());
+ break;
+
+ case METHOD_HANDLE:
+ {
+ DexMethodHandle handle = value.asDexValueMethodHandle().getValue();
+ if (handle.isFieldHandle()) {
+ registry.registerFieldAccess(handle.asField());
+ } else {
+ assert handle.isMethodHandle();
+ registry.registerMethodReference(handle.asMethod());
+ }
+ }
+ break;
+
+ case METHOD_TYPE:
+ value.asDexValueMethodType().getValue().forEachType(registry::registerTypeReference);
+ break;
+
+ case TYPE:
+ registry.registerTypeReference(value.asDexValueType().getValue());
+ break;
+
+ default:
+ throw new Unreachable();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
index 33bbc11..b50015b 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.graph.DexEncodedMember;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.EnclosingMethodAttribute;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ProgramPackage;
@@ -68,7 +69,7 @@
}
private Node createNode(DexDefinition definition) {
- Node node = new Node(definition);
+ Node node = new Node();
nodes.put(definition, node);
return node;
}
@@ -89,23 +90,60 @@
}
private void registerReferencesFromClass(DexProgramClass clazz) {
- // TODO(b/165783399): Trace the references to the immediate super types.
- // TODO(b/165783399): Maybe trace the references in the nest host and/or members.
- // TODO(b/165783399): Maybe trace the references to the inner classes.
- // TODO(b/165783399): Maybe trace the references in @kotlin.Metadata.
+ RepackagingUseRegistry registry = new RepackagingUseRegistry(appView, this, clazz);
+
+ // Trace the references to the immediate super types.
+ registry.registerTypeReference(clazz.getSuperType());
+ clazz.interfaces.forEach(registry::registerTypeReference);
+
+ // Trace the references from the class annotations.
+ new RepackagingAnnotationTracer(appView, registry).trace(clazz.annotations());
+
+ // Trace the references in the nest host and/or members.
+ if (clazz.isInANest()) {
+ if (clazz.isNestHost()) {
+ clazz.forEachNestMember(registry::registerTypeReference);
+ } else {
+ assert clazz.isNestMember();
+ registry.registerTypeReference(clazz.getNestHost());
+ }
+ }
+
+ // Trace the references to the inner and outer classes.
+ clazz.getInnerClasses().forEach(registry::registerInnerClassAttribute);
+
+ // Trace the references from the enclosing method attribute.
+ EnclosingMethodAttribute attr = clazz.getEnclosingMethodAttribute();
+ if (attr != null) {
+ registry.registerEnclosingMethodAttribute(attr);
+ }
}
private void registerReferencesFromField(ProgramField field) {
- // TODO(b/165783399): Trace the type of the field.
- // TODO(b/165783399): Trace the references in the field annotations.
+ RepackagingUseRegistry registry = new RepackagingUseRegistry(appView, this, field);
+
+ // Trace the type of the field.
+ registry.registerTypeReference(field.getReference().getType());
+
+ // Trace the references in the field annotations.
+ new RepackagingAnnotationTracer(appView, registry).trace(field.getDefinition().annotations());
}
private void registerReferencesFromMethod(ProgramMethod method) {
- // TODO(b/165783399): Trace the type references in the method signature.
- // TODO(b/165783399): Trace the references in the method and method parameter annotations.
DexEncodedMethod definition = method.getDefinition();
+ RepackagingUseRegistry registry = new RepackagingUseRegistry(appView, this, method);
+
+ // Trace the type references in the method signature.
+ definition.getProto().forEachType(registry::registerTypeReference);
+
+ // Trace the references in the method and method parameter annotations.
+ RepackagingAnnotationTracer annotationTracer =
+ new RepackagingAnnotationTracer(appView, registry);
+ annotationTracer.trace(definition.annotations());
+ annotationTracer.trace(definition.getParameterAnnotations());
+
+ // Trace the references from the code.
if (definition.hasCode()) {
- RepackagingUseRegistry registry = new RepackagingUseRegistry(appView, this, method);
definition.getCode().registerCodeReferences(method, registry);
}
}
@@ -127,14 +165,8 @@
static class Node {
- private final DexDefinition definition;
-
private final Set<Node> neighbors = Sets.newConcurrentHashSet();
- private Node(DexDefinition definition) {
- this.definition = definition;
- }
-
public void addNeighbor(Node neighbor) {
neighbors.add(neighbor);
neighbor.neighbors.add(this);
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 7c19b0b..a0f6645 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingLens.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingLens.java
@@ -36,7 +36,7 @@
@Override
public DexType getOriginalType(DexType type) {
DexType previous = originalTypes.getOrDefault(type, type);
- return previousLens.getOriginalType(previous);
+ return getPrevious().getOriginalType(previous);
}
public static class Builder {
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java
index 3591ff7..f519f01 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java
@@ -154,9 +154,9 @@
List<InnerClassAttribute> newInnerClassAttributes = new ArrayList<>();
for (InnerClassAttribute innerClassAttribute : innerClassAttributes) {
DexType innerClassType = innerClassAttribute.getInner();
- DexType newInnerClassType = fixupType(innerClassType);
+ DexType newInnerClassType = fixupTypeOrNull(innerClassType);
DexType outerClassType = innerClassAttribute.getOuter();
- DexType newOuterClassType = fixupType(outerClassType);
+ DexType newOuterClassType = fixupTypeOrNull(outerClassType);
newInnerClassAttributes.add(
new InnerClassAttribute(
innerClassAttribute.getAccess(),
@@ -247,6 +247,10 @@
return changed ? newSynthesizedFrom : synthesizedFrom;
}
+ private DexType fixupTypeOrNull(DexType type) {
+ return type != null ? fixupType(type) : null;
+ }
+
private DexType fixupType(DexType type) {
if (type.isArrayType()) {
DexType base = type.toBaseType(dexItemFactory);
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
index 531e6c8..b27182d 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.repackaging;
+import static com.google.common.base.Predicates.alwaysTrue;
+
import com.android.tools.r8.graph.AccessFlags;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ClassAccessFlags;
@@ -12,24 +14,30 @@
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.EnclosingMethodAttribute;
+import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.MemberResolutionResult;
+import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramMember;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.graph.SuccessfulMemberResolutionResult;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
public class RepackagingUseRegistry extends UseRegistry {
private final AppInfoWithLiveness appInfo;
private final RepackagingConstraintGraph constraintGraph;
- private final ProgramMethod context;
+ private final ProgramDefinition context;
private final RepackagingConstraintGraph.Node node;
public RepackagingUseRegistry(
AppView<AppInfoWithLiveness> appView,
RepackagingConstraintGraph constraintGraph,
- ProgramMethod context) {
+ ProgramDefinition context) {
super(appView.dexItemFactory());
this.appInfo = appView.appInfo();
this.constraintGraph = constraintGraph;
@@ -43,7 +51,7 @@
return true;
}
if (accessFlags.isProtected()
- && !appInfo.isSubtype(context.getHolderType(), referencedClass.getType())) {
+ && !appInfo.isSubtype(context.getContextType(), referencedClass.getType())) {
return true;
}
return false;
@@ -55,13 +63,25 @@
return true;
}
if (accessFlags.isProtected()
- && !appInfo.isSubtype(context.getHolderType(), member.getHolderType())) {
+ && !appInfo.isSubtype(context.getContextType(), member.getHolderType())) {
return true;
}
return false;
}
- private void registerMemberAccess(MemberResolutionResult<?, ?> resolutionResult) {
+ public void registerFieldAccess(DexField field) {
+ registerMemberAccess(appInfo.resolveField(field));
+ }
+
+ public ProgramMethod registerMethodReference(DexMethod method) {
+ ResolutionResult resolutionResult = appInfo.unsafeResolveMethodDueToDexFormat(method);
+ registerMemberAccess(resolutionResult);
+ return resolutionResult.isSingleResolution()
+ ? resolutionResult.asSingleResolution().getResolvedProgramMethod()
+ : null;
+ }
+
+ public void registerMemberAccess(MemberResolutionResult<?, ?> resolutionResult) {
SuccessfulMemberResolutionResult<?, ?> successfulResolutionResult =
resolutionResult.asSuccessfulMemberResolutionResult();
if (successfulResolutionResult == null) {
@@ -88,28 +108,36 @@
}
private void registerTypeAccess(DexType type) {
+ registerTypeAccess(type, this::registerTypeAccess);
+ }
+
+ private void registerTypeAccess(DexType type, Consumer<DexClass> consumer) {
if (type.isArrayType()) {
- registerTypeAccess(type.toBaseType(appInfo.dexItemFactory()));
+ registerTypeAccess(type.toBaseType(appInfo.dexItemFactory()), consumer);
return;
}
- if (type.isPrimitiveType()) {
+ if (type.isPrimitiveType() || type.isVoidType()) {
return;
}
assert type.isClassType();
DexClass clazz = appInfo.definitionFor(type);
if (clazz != null) {
- registerTypeAccess(clazz);
+ consumer.accept(clazz);
}
}
private void registerTypeAccess(DexClass clazz) {
+ registerTypeAccess(clazz, this::isOnlyAccessibleFromSamePackage);
+ }
+
+ private void registerTypeAccess(DexClass clazz, Predicate<DexProgramClass> predicate) {
// We only want to connect the current method node to the class node if the access requires the
// two nodes to be in the same package. Therefore, we ignore accesses to non-program classes
// and program classes outside the current package.
DexProgramClass programClass = clazz.asProgramClass();
if (programClass != null) {
RepackagingConstraintGraph.Node classNode = constraintGraph.getNode(programClass);
- if (classNode != null && isOnlyAccessibleFromSamePackage(programClass)) {
+ if (classNode != null && predicate.test(programClass)) {
node.addNeighbor(classNode);
}
}
@@ -147,12 +175,12 @@
@Override
public void registerInstanceFieldRead(DexField field) {
- registerMemberAccess(appInfo.resolveField(field));
+ registerFieldAccess(field);
}
@Override
public void registerInstanceFieldWrite(DexField field) {
- registerMemberAccess(appInfo.resolveField(field));
+ registerFieldAccess(field);
}
@Override
@@ -162,12 +190,12 @@
@Override
public void registerStaticFieldRead(DexField field) {
- registerMemberAccess(appInfo.resolveField(field));
+ registerFieldAccess(field);
}
@Override
public void registerStaticFieldWrite(DexField field) {
- registerMemberAccess(appInfo.resolveField(field));
+ registerFieldAccess(field);
}
@Override
@@ -179,4 +207,29 @@
public void registerInstanceOf(DexType type) {
registerTypeAccess(type);
}
+
+ public void registerEnclosingMethodAttribute(EnclosingMethodAttribute enclosingMethodAttribute) {
+ // For references in enclosing method attributes we add an edge from the context to the
+ // referenced item even if the item would be accessible from another package, to make sure that
+ // we don't split such classes into different packages.
+ if (enclosingMethodAttribute.getEnclosingClass() != null) {
+ registerTypeAccess(
+ enclosingMethodAttribute.getEnclosingClass(),
+ clazz -> registerTypeAccess(clazz, alwaysTrue()));
+ }
+ if (enclosingMethodAttribute.getEnclosingMethod() != null) {
+ ProgramMethod method = registerMethodReference(enclosingMethodAttribute.getEnclosingMethod());
+ if (method != null) {
+ registerTypeAccess(method.getHolder(), alwaysTrue());
+ }
+ }
+ }
+
+ public void registerInnerClassAttribute(InnerClassAttribute innerClassAttribute) {
+ // For references in inner class attributes we add an edge from the context to the referenced
+ // class even if the referenced class would be accessible from another package, to make sure
+ // that we don't split such classes into different packages.
+ innerClassAttribute.forEachType(
+ type -> registerTypeAccess(type, clazz -> registerTypeAccess(clazz, alwaysTrue())));
+ }
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java
index f984045..e13c832 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java
@@ -26,15 +26,18 @@
private final ClassReference obfuscatedReference;
private final ClassNamingForNameMapper mapper;
+ private final RetraceApi retracer;
- private RetraceClassResult(ClassReference obfuscatedReference, ClassNamingForNameMapper mapper) {
+ private RetraceClassResult(
+ ClassReference obfuscatedReference, ClassNamingForNameMapper mapper, RetraceApi retracer) {
this.obfuscatedReference = obfuscatedReference;
this.mapper = mapper;
+ this.retracer = retracer;
}
static RetraceClassResult create(
- ClassReference obfuscatedReference, ClassNamingForNameMapper mapper) {
- return new RetraceClassResult(obfuscatedReference, mapper);
+ ClassReference obfuscatedReference, ClassNamingForNameMapper mapper, RetraceApi retracer) {
+ return new RetraceClassResult(obfuscatedReference, mapper, retracer);
}
public RetraceFieldResult lookupField(String fieldName) {
@@ -75,12 +78,12 @@
if (element.mapper != null) {
mappedRangesForT = lookupFunction.apply(element.mapper, name);
}
- elementBox.set(constructor.create(element, mappedRangesForT, name));
+ elementBox.set(constructor.create(element, mappedRangesForT, name, retracer));
});
return elementBox.get();
}
- private boolean hasRetraceResult() {
+ boolean hasRetraceResult() {
return mapper != null;
}
@@ -101,7 +104,7 @@
}
private interface ResultConstructor<T, R> {
- R create(Element element, T mappings, String obfuscatedName);
+ R create(Element element, T mappings, String obfuscatedName, RetraceApi retraceApi);
}
public boolean isAmbiguous() {
@@ -186,7 +189,10 @@
BiFunction<ClassNamingForNameMapper, String, T> lookupFunction,
ResultConstructor<T, R> constructor) {
return constructor.create(
- this, mapper != null ? lookupFunction.apply(mapper, name) : null, name);
+ this,
+ mapper != null ? lookupFunction.apply(mapper, name) : null,
+ name,
+ classResult.retracer);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java
index 57b5354..5477eaa 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java
@@ -23,14 +23,17 @@
private final RetraceClassResult.Element classElement;
private final List<MemberNaming> memberNamings;
private final String obfuscatedName;
+ private final RetraceApi retracer;
RetraceFieldResult(
RetraceClassResult.Element classElement,
List<MemberNaming> memberNamings,
- String obfuscatedName) {
+ String obfuscatedName,
+ RetraceApi retracer) {
this.classElement = classElement;
this.memberNamings = memberNamings;
this.obfuscatedName = obfuscatedName;
+ this.retracer = retracer;
assert classElement != null;
assert memberNamings == null
|| (!memberNamings.isEmpty() && memberNamings.stream().allMatch(Objects::nonNull));
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceMethodResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceMethodResult.java
index ff1af91..7977858 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceMethodResult.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceMethodResult.java
@@ -26,15 +26,18 @@
private final String obfuscatedName;
private final RetraceClassResult.Element classElement;
private final MappedRangesOfName mappedRanges;
+ private final RetraceApi retracer;
private Boolean isAmbiguousCached = null;
RetraceMethodResult(
RetraceClassResult.Element classElement,
MappedRangesOfName mappedRanges,
- String obfuscatedName) {
+ String obfuscatedName,
+ RetraceApi retracer) {
this.classElement = classElement;
this.mappedRanges = mappedRanges;
this.obfuscatedName = obfuscatedName;
+ this.retracer = retracer;
assert classElement != null;
}
@@ -87,7 +90,7 @@
}
}
return new RetraceMethodResult(
- classElement, new MappedRangesOfName(narrowedRanges), obfuscatedName);
+ classElement, new MappedRangesOfName(narrowedRanges), obfuscatedName, retracer);
}
@Override
@@ -182,7 +185,8 @@
}
public RetraceSourceFileResult retraceSourceFile(String sourceFile) {
- return RetraceUtils.getSourceFile(classElement, methodReference.getHolderClass(), sourceFile);
+ return RetraceUtils.getSourceFile(
+ classElement, methodReference.getHolderClass(), sourceFile, retraceMethodResult.retracer);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java b/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java
index 15479f9..40b3f41 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java
@@ -838,7 +838,8 @@
: RetraceUtils.getSourceFile(
retraceString.getClassContext(),
retraceString.context.qualifiedContext,
- fileName);
+ fileName,
+ retracer);
retracedStrings.add(
retraceString
.updateContext(context -> context.withSource(sourceFileResult.getFilename()))
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceUtils.java b/src/main/java/com/android/tools/r8/retrace/RetraceUtils.java
index 8e862d0..77aff7d 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceUtils.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceUtils.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.utils.Box;
import com.google.common.collect.Sets;
import com.google.common.io.Files;
import java.util.Set;
@@ -14,7 +15,7 @@
public class RetraceUtils {
private static final Set<String> UNKNOWN_SOURCEFILE_NAMES =
- Sets.newHashSet("", "SourceFile", "Unknown", "Unknown Source");
+ Sets.newHashSet("", "SourceFile", "Unknown", "Unknown Source", "PG");
public static String methodDescriptionFromMethodReference(
MethodReference methodReference, boolean appendHolder, boolean verbose) {
@@ -61,21 +62,33 @@
}
static RetraceSourceFileResult getSourceFile(
- RetraceClassResult.Element classElement, ClassReference context, String sourceFile) {
- // For inline frames we do not have the class element associated with it.
+ RetraceClassResult.Element classElement,
+ ClassReference context,
+ String sourceFile,
+ RetraceApi retracer) {
+ // If no context is specified always retrace using the found class element.
if (context == null) {
return classElement.retraceSourceFile(sourceFile);
}
if (context.equals(classElement.getClassReference())) {
return classElement.retraceSourceFile(sourceFile);
} else {
- return new RetraceSourceFileResult(
- synthesizeFileName(
- context.getTypeName(),
- classElement.getClassReference().getTypeName(),
- sourceFile,
- true),
- true);
+ RetraceClassResult contextClassResult = retracer.retrace(context);
+ assert !contextClassResult.isAmbiguous();
+ if (contextClassResult.hasRetraceResult()) {
+ Box<RetraceSourceFileResult> retraceSourceFile = new Box<>();
+ contextClassResult.forEach(
+ element -> retraceSourceFile.set(element.retraceSourceFile(sourceFile)));
+ return retraceSourceFile.get();
+ } else {
+ return new RetraceSourceFileResult(
+ synthesizeFileName(
+ context.getTypeName(),
+ classElement.getClassReference().getTypeName(),
+ sourceFile,
+ true),
+ true);
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/Retracer.java b/src/main/java/com/android/tools/r8/retrace/Retracer.java
index b2dad89..0f7d547 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retracer.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retracer.java
@@ -38,7 +38,7 @@
@Override
public RetraceClassResult retrace(ClassReference classReference) {
return RetraceClassResult.create(
- classReference, classNameMapper.getClassNaming(classReference.getTypeName()));
+ classReference, classNameMapper.getClassNaming(classReference.getTypeName()), this);
}
@Override
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 627d84f..311e10d 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -31,10 +31,11 @@
import com.android.tools.r8.graph.FieldAccessInfoCollectionImpl;
import com.android.tools.r8.graph.FieldResolutionResult;
import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
+import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.graph.InstantiatedSubTypeInfo;
import com.android.tools.r8.graph.LookupResult.LookupResultSuccess;
import com.android.tools.r8.graph.LookupTarget;
+import com.android.tools.r8.graph.MethodAccessInfoCollection;
import com.android.tools.r8.graph.ObjectAllocationInfoCollection;
import com.android.tools.r8.graph.ObjectAllocationInfoCollectionImpl;
import com.android.tools.r8.graph.PresortedComparable;
@@ -118,23 +119,15 @@
* each field. The latter is used, for example, during member rebinding.
*/
private FieldAccessInfoCollectionImpl fieldAccessInfoCollection;
+ /** Set of all methods referenced in invokes along with their calling contexts. */
+ private final MethodAccessInfoCollection methodAccessInfoCollection;
/** Information about instantiated classes and their allocation sites. */
private final ObjectAllocationInfoCollectionImpl objectAllocationInfoCollection;
- /** Set of all methods referenced in virtual invokes, along with calling context. */
- public final SortedMap<DexMethod, ProgramMethodSet> virtualInvokes;
- /** Set of all methods referenced in interface invokes, along with calling context. */
- public final SortedMap<DexMethod, ProgramMethodSet> interfaceInvokes;
- /** Set of all methods referenced in super invokes, along with calling context. */
- public final SortedMap<DexMethod, ProgramMethodSet> superInvokes;
- /** Set of all methods referenced in direct invokes, along with calling context. */
- public final SortedMap<DexMethod, ProgramMethodSet> directInvokes;
- /** Set of all methods referenced in static invokes, along with calling context. */
- public final SortedMap<DexMethod, ProgramMethodSet> staticInvokes;
/**
* Set of live call sites in the code. Note that if desugaring has taken place call site objects
* will have been removed from the code.
*/
- public final Set<DexCallSite> callSites;
+ public final Map<DexCallSite, ProgramMethodSet> callSites;
/** Collection of keep requirements for the program. */
private final KeepInfoCollection keepInfo;
/** All items with assumemayhavesideeffects rule. */
@@ -212,13 +205,9 @@
SortedSet<DexMethod> virtualMethodsTargetedByInvokeDirect,
SortedSet<DexMethod> liveMethods,
FieldAccessInfoCollectionImpl fieldAccessInfoCollection,
+ MethodAccessInfoCollection methodAccessInfoCollection,
ObjectAllocationInfoCollectionImpl objectAllocationInfoCollection,
- SortedMap<DexMethod, ProgramMethodSet> virtualInvokes,
- SortedMap<DexMethod, ProgramMethodSet> interfaceInvokes,
- SortedMap<DexMethod, ProgramMethodSet> superInvokes,
- SortedMap<DexMethod, ProgramMethodSet> directInvokes,
- SortedMap<DexMethod, ProgramMethodSet> staticInvokes,
- Set<DexCallSite> callSites,
+ Map<DexCallSite, ProgramMethodSet> callSites,
KeepInfoCollection keepInfo,
Map<DexReference, ProguardMemberRule> mayHaveSideEffects,
Map<DexReference, ProguardMemberRule> noSideEffects,
@@ -255,16 +244,12 @@
this.virtualMethodsTargetedByInvokeDirect = virtualMethodsTargetedByInvokeDirect;
this.liveMethods = liveMethods;
this.fieldAccessInfoCollection = fieldAccessInfoCollection;
+ this.methodAccessInfoCollection = methodAccessInfoCollection;
this.objectAllocationInfoCollection = objectAllocationInfoCollection;
this.keepInfo = keepInfo;
this.mayHaveSideEffects = mayHaveSideEffects;
this.noSideEffects = noSideEffects;
this.assumedValues = assumedValues;
- this.virtualInvokes = virtualInvokes;
- this.interfaceInvokes = interfaceInvokes;
- this.superInvokes = superInvokes;
- this.directInvokes = directInvokes;
- this.staticInvokes = staticInvokes;
this.callSites = callSites;
this.alwaysInline = alwaysInline;
this.forceInline = forceInline;
@@ -301,13 +286,9 @@
SortedSet<DexMethod> virtualMethodsTargetedByInvokeDirect,
SortedSet<DexMethod> liveMethods,
FieldAccessInfoCollectionImpl fieldAccessInfoCollection,
+ MethodAccessInfoCollection methodAccessInfoCollection,
ObjectAllocationInfoCollectionImpl objectAllocationInfoCollection,
- SortedMap<DexMethod, ProgramMethodSet> virtualInvokes,
- SortedMap<DexMethod, ProgramMethodSet> interfaceInvokes,
- SortedMap<DexMethod, ProgramMethodSet> superInvokes,
- SortedMap<DexMethod, ProgramMethodSet> directInvokes,
- SortedMap<DexMethod, ProgramMethodSet> staticInvokes,
- Set<DexCallSite> callSites,
+ Map<DexCallSite, ProgramMethodSet> callSites,
KeepInfoCollection keepInfo,
Map<DexReference, ProguardMemberRule> mayHaveSideEffects,
Map<DexReference, ProguardMemberRule> noSideEffects,
@@ -347,16 +328,12 @@
this.virtualMethodsTargetedByInvokeDirect = virtualMethodsTargetedByInvokeDirect;
this.liveMethods = liveMethods;
this.fieldAccessInfoCollection = fieldAccessInfoCollection;
+ this.methodAccessInfoCollection = methodAccessInfoCollection;
this.objectAllocationInfoCollection = objectAllocationInfoCollection;
this.keepInfo = keepInfo;
this.mayHaveSideEffects = mayHaveSideEffects;
this.noSideEffects = noSideEffects;
this.assumedValues = assumedValues;
- this.virtualInvokes = virtualInvokes;
- this.interfaceInvokes = interfaceInvokes;
- this.superInvokes = superInvokes;
- this.directInvokes = directInvokes;
- this.staticInvokes = staticInvokes;
this.callSites = callSites;
this.alwaysInline = alwaysInline;
this.forceInline = forceInline;
@@ -398,12 +375,8 @@
previous.virtualMethodsTargetedByInvokeDirect,
previous.liveMethods,
previous.fieldAccessInfoCollection,
+ previous.methodAccessInfoCollection,
previous.objectAllocationInfoCollection,
- previous.virtualInvokes,
- previous.interfaceInvokes,
- previous.superInvokes,
- previous.directInvokes,
- previous.staticInvokes,
previous.callSites,
previous.keepInfo,
previous.mayHaveSideEffects,
@@ -453,12 +426,8 @@
previous.virtualMethodsTargetedByInvokeDirect,
previous.liveMethods,
previous.fieldAccessInfoCollection,
+ previous.methodAccessInfoCollection,
previous.objectAllocationInfoCollection,
- previous.virtualInvokes,
- previous.interfaceInvokes,
- previous.superInvokes,
- previous.directInvokes,
- previous.staticInvokes,
previous.callSites,
extendPinnedItems(previous, additionalPinnedItems),
previous.mayHaveSideEffects,
@@ -545,16 +514,12 @@
this.virtualMethodsTargetedByInvokeDirect = previous.virtualMethodsTargetedByInvokeDirect;
this.liveMethods = previous.liveMethods;
this.fieldAccessInfoCollection = previous.fieldAccessInfoCollection;
+ this.methodAccessInfoCollection = previous.methodAccessInfoCollection;
this.objectAllocationInfoCollection = previous.objectAllocationInfoCollection;
this.keepInfo = previous.keepInfo;
this.mayHaveSideEffects = previous.mayHaveSideEffects;
this.noSideEffects = previous.noSideEffects;
this.assumedValues = previous.assumedValues;
- this.virtualInvokes = previous.virtualInvokes;
- this.interfaceInvokes = previous.interfaceInvokes;
- this.superInvokes = previous.superInvokes;
- this.directInvokes = previous.directInvokes;
- this.staticInvokes = previous.staticInvokes;
this.callSites = previous.callSites;
this.alwaysInline = previous.alwaysInline;
this.forceInline = previous.forceInline;
@@ -653,7 +618,7 @@
for (DexProgramClass clazz : classes()) {
worklist.add(clazz.type);
}
- for (DexCallSite callSite : callSites) {
+ for (DexCallSite callSite : callSites.keySet()) {
for (DexEncodedMethod method : lookupLambdaImplementedMethods(callSite)) {
worklist.add(method.holder());
}
@@ -779,6 +744,11 @@
return fieldAccessInfoCollection;
}
+ /** This method provides immutable access to `methodAccessInfoCollection`. */
+ public MethodAccessInfoCollection getMethodAccessInfoCollection() {
+ return methodAccessInfoCollection;
+ }
+
/** This method provides immutable access to `objectAllocationInfoCollection`. */
public ObjectAllocationInfoCollection getObjectAllocationInfoCollection() {
return objectAllocationInfoCollection;
@@ -993,17 +963,9 @@
}
public AppInfoWithLiveness rewrittenWithLens(
- DirectMappedDexApplication application, NestedGraphLens lens) {
+ DirectMappedDexApplication application, NonIdentityGraphLens lens) {
assert checkIfObsolete();
- // The application has already been rewritten with all of lens' parent lenses. Therefore, we
- // temporarily replace lens' parent lens with an identity lens to avoid the overhead of
- // traversing the entire lens chain upon each lookup during the rewriting.
- return lens.withAlternativeParentLens(
- GraphLens.getIdentityLens(), () -> createRewrittenAppInfoWithLiveness(application, lens));
- }
- private AppInfoWithLiveness createRewrittenAppInfoWithLiveness(
- DirectMappedDexApplication application, NestedGraphLens lens) {
// Switchmap classes should never be affected by renaming.
assert lens.assertDefinitionsNotModified(
switchMaps.keySet().stream()
@@ -1028,18 +990,10 @@
lens.rewriteMethods(methodsTargetedByInvokeDynamic),
lens.rewriteMethods(virtualMethodsTargetedByInvokeDirect),
lens.rewriteMethods(liveMethods),
- fieldAccessInfoCollection != null
- ? fieldAccessInfoCollection.rewrittenWithLens(definitionSupplier, lens)
- : null,
+ fieldAccessInfoCollection.rewrittenWithLens(definitionSupplier, lens),
+ methodAccessInfoCollection.rewrittenWithLens(definitionSupplier, lens),
objectAllocationInfoCollection.rewrittenWithLens(definitionSupplier, lens),
- rewriteInvokesWithContexts(virtualInvokes, lens),
- rewriteInvokesWithContexts(interfaceInvokes, lens),
- rewriteInvokesWithContexts(superInvokes, lens),
- rewriteInvokesWithContexts(directInvokes, lens),
- rewriteInvokesWithContexts(staticInvokes, lens),
- // TODO(sgjesse): Rewrite call sites as well? Right now they are only used by minification
- // after second tree shaking.
- callSites,
+ lens.rewriteCallSites(callSites, definitionSupplier),
keepInfo.rewrite(lens, application.options),
lens.rewriteReferenceKeys(mayHaveSideEffects),
lens.rewriteReferenceKeys(noSideEffects),
@@ -1300,7 +1254,7 @@
static void forEachTypeInHierarchyOfLiveProgramClasses(
Consumer<DexClass> fn,
Collection<DexProgramClass> liveProgramClasses,
- Set<DexCallSite> callSites,
+ Map<DexCallSite, ProgramMethodSet> callSites,
AppInfoWithClassHierarchy appInfo) {
Set<DexType> seen = Sets.newIdentityHashSet();
Deque<DexType> worklist = new ArrayDeque<>();
@@ -1317,7 +1271,7 @@
}
}
}
- for (DexCallSite callSite : callSites) {
+ for (DexCallSite callSite : callSites.keySet()) {
List<DexType> interfaces = LambdaDescriptor.getInterfaces(callSite, appInfo);
if (interfaces != null) {
for (DexType iface : interfaces) {
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 98def17..0afccd0 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -8,7 +8,6 @@
import static com.android.tools.r8.naming.IdentifierNameStringUtils.identifyIdentifier;
import static com.android.tools.r8.naming.IdentifierNameStringUtils.isReflectionMethod;
import static com.android.tools.r8.shaking.AnnotationRemover.shouldKeepAnnotation;
-import static com.android.tools.r8.shaking.EnqueuerUtils.toImmutableSortedMap;
import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.cf.code.CfFieldInstruction;
@@ -61,6 +60,7 @@
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.LookupLambdaTarget;
import com.android.tools.r8.graph.LookupTarget;
+import com.android.tools.r8.graph.MethodAccessInfoCollection;
import com.android.tools.r8.graph.ObjectAllocationInfoCollectionImpl;
import com.android.tools.r8.graph.PresortedComparable;
import com.android.tools.r8.graph.ProgramField;
@@ -141,6 +141,7 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.BiConsumer;
+import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -200,15 +201,12 @@
private AnnotationRemover.Builder annotationRemoverBuilder;
private final EnqueuerDefinitionSupplier enqueuerDefinitionSupplier;
- private final Map<DexMethod, ProgramMethodSet> virtualInvokes = new IdentityHashMap<>();
- private final Map<DexMethod, ProgramMethodSet> interfaceInvokes = new IdentityHashMap<>();
- private final Map<DexMethod, ProgramMethodSet> superInvokes = new IdentityHashMap<>();
- private final Map<DexMethod, ProgramMethodSet> directInvokes = new IdentityHashMap<>();
- private final Map<DexMethod, ProgramMethodSet> staticInvokes = new IdentityHashMap<>();
private final FieldAccessInfoCollectionImpl fieldAccessInfoCollection =
new FieldAccessInfoCollectionImpl();
+ private final MethodAccessInfoCollection.SortedBuilder methodAccessInfoCollection =
+ MethodAccessInfoCollection.sortedBuilder();
private final ObjectAllocationInfoCollectionImpl.Builder objectAllocationInfoCollection;
- private final Set<DexCallSite> callSites = Sets.newIdentityHashSet();
+ private final Map<DexCallSite, ProgramMethodSet> callSites = new IdentityHashMap<>();
private final Set<DexReference> identifierNameStrings = Sets.newIdentityHashSet();
@@ -784,11 +782,11 @@
//
private boolean registerMethodWithTargetAndContext(
- Map<DexMethod, ProgramMethodSet> seen, DexMethod method, ProgramMethod context) {
+ BiPredicate<DexMethod, ProgramMethod> registration, DexMethod method, ProgramMethod context) {
DexType baseHolder = method.holder.toBaseType(appView.dexItemFactory());
if (baseHolder.isClassType()) {
markTypeAsLive(baseHolder, clazz -> graphReporter.reportClassReferencedFrom(clazz, context));
- return seen.computeIfAbsent(method, ignore -> ProgramMethodSet.create()).add(context);
+ return registration.test(method, context);
}
return false;
}
@@ -883,7 +881,7 @@
} else {
markLambdaAsInstantiated(descriptor, context);
transitionMethodsForInstantiatedLambda(descriptor);
- callSites.add(callSite);
+ callSites.computeIfAbsent(callSite, ignore -> ProgramMethodSet.create()).add(context);
}
// For call sites representing a lambda, we link the targeted method
@@ -1078,7 +1076,8 @@
private void traceInvokeDirect(
DexMethod invokedMethod, ProgramMethod context, KeepReason reason) {
- if (!registerMethodWithTargetAndContext(directInvokes, invokedMethod, context)) {
+ if (!registerMethodWithTargetAndContext(
+ methodAccessInfoCollection::registerInvokeDirectInContext, invokedMethod, context)) {
return;
}
if (Log.ENABLED) {
@@ -1098,7 +1097,8 @@
private void traceInvokeInterface(
DexMethod method, ProgramMethod context, KeepReason keepReason) {
- if (!registerMethodWithTargetAndContext(interfaceInvokes, method, context)) {
+ if (!registerMethodWithTargetAndContext(
+ methodAccessInfoCollection::registerInvokeInterfaceInContext, method, context)) {
return;
}
if (Log.ENABLED) {
@@ -1137,7 +1137,8 @@
if (invokedMethod == dexItemFactory.proxyMethods.newProxyInstance) {
pendingReflectiveUses.add(context);
}
- if (!registerMethodWithTargetAndContext(staticInvokes, invokedMethod, context)) {
+ if (!registerMethodWithTargetAndContext(
+ methodAccessInfoCollection::registerInvokeStaticInContext, invokedMethod, context)) {
return;
}
if (Log.ENABLED) {
@@ -1151,7 +1152,8 @@
// We have to revisit super invokes based on the context they are found in. The same
// method descriptor will hit different targets, depending on the context it is used in.
DexMethod actualTarget = getInvokeSuperTarget(invokedMethod, context);
- if (!registerMethodWithTargetAndContext(superInvokes, invokedMethod, context)) {
+ if (!registerMethodWithTargetAndContext(
+ methodAccessInfoCollection::registerInvokeSuperInContext, invokedMethod, context)) {
return;
}
if (Log.ENABLED) {
@@ -1180,7 +1182,8 @@
// Revisit the current method to implicitly add -keep rule for items with reflective access.
pendingReflectiveUses.add(context);
}
- if (!registerMethodWithTargetAndContext(virtualInvokes, invokedMethod, context)) {
+ if (!registerMethodWithTargetAndContext(
+ methodAccessInfoCollection::registerInvokeVirtualInContext, invokedMethod, context)) {
return;
}
if (Log.ENABLED) {
@@ -3037,13 +3040,8 @@
toSortedDescriptorSet(liveMethods.getItems()),
// Filter out library fields and pinned fields, because these are read by default.
fieldAccessInfoCollection,
+ methodAccessInfoCollection.build(),
objectAllocationInfoCollection.build(appInfo),
- // TODO(b/132593519): Do we require these sets to be sorted for determinism?
- toImmutableSortedMap(virtualInvokes, PresortedComparable::slowCompare),
- toImmutableSortedMap(interfaceInvokes, PresortedComparable::slowCompare),
- toImmutableSortedMap(superInvokes, PresortedComparable::slowCompare),
- toImmutableSortedMap(directInvokes, PresortedComparable::slowCompare),
- toImmutableSortedMap(staticInvokes, PresortedComparable::slowCompare),
callSites,
keepInfo,
rootSet.mayHaveSideEffects,
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 efc610a..d2cbf66 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
@@ -16,7 +16,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
+import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.shaking.KeepFieldInfo.Joiner;
@@ -162,7 +162,7 @@
@Deprecated
public abstract void forEachPinnedField(Consumer<DexField> consumer);
- public abstract KeepInfoCollection rewrite(NestedGraphLens lens, InternalOptions options);
+ public abstract KeepInfoCollection rewrite(NonIdentityGraphLens lens, InternalOptions options);
public abstract KeepInfoCollection mutate(Consumer<MutableKeepInfoCollection> mutator);
@@ -198,7 +198,7 @@
}
@Override
- public KeepInfoCollection rewrite(NestedGraphLens lens, InternalOptions options) {
+ public KeepInfoCollection rewrite(NonIdentityGraphLens lens, InternalOptions options) {
Map<DexType, KeepClassInfo> newClassInfo = new IdentityHashMap<>(keepClassInfo.size());
keepClassInfo.forEach(
(type, info) -> {
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexTracingResult.java b/src/main/java/com/android/tools/r8/shaking/MainDexTracingResult.java
index d333211..76b6d8a 100644
--- a/src/main/java/com/android/tools/r8/shaking/MainDexTracingResult.java
+++ b/src/main/java/com/android/tools/r8/shaking/MainDexTracingResult.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
@@ -92,6 +93,14 @@
return classes;
}
+ public boolean contains(DexProgramClass clazz) {
+ return contains(clazz.type);
+ }
+
+ public boolean contains(DexType type) {
+ return getClasses().contains(type);
+ }
+
private void collectTypesMatching(
Set<DexType> types, Predicate<DexType> predicate, Consumer<DexType> consumer) {
types.forEach(
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 5948028..953779d 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -28,7 +28,8 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.GraphLens.GraphLensLookupResult;
+import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
+import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.graph.LookupResult.LookupResultSuccess;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ObjectAllocationInfoCollection;
@@ -1139,7 +1140,7 @@
// resolve to a method on an interface never hit an implementation below that interface.
deferredRenamings.mapVirtualMethodToDirectInType(
oldTarget,
- prototypeChanges -> new GraphLensLookupResult(newTarget, STATIC, prototypeChanges),
+ prototypeChanges -> new MethodLookupResult(newTarget, null, STATIC, prototypeChanges),
target.type);
} else {
// If we merge class B into class C, and class C contains an invocation super.m(), then it
@@ -1160,7 +1161,8 @@
if (resolutionSucceeds) {
deferredRenamings.mapVirtualMethodToDirectInType(
signatureInHolder,
- prototypeChanges -> new GraphLensLookupResult(newTarget, DIRECT, prototypeChanges),
+ prototypeChanges ->
+ new MethodLookupResult(newTarget, null, DIRECT, prototypeChanges),
target.type);
} else {
break;
@@ -1185,7 +1187,7 @@
deferredRenamings.mapVirtualMethodToDirectInType(
signatureInType,
prototypeChanges ->
- new GraphLensLookupResult(newTarget, DIRECT, prototypeChanges),
+ new MethodLookupResult(newTarget, null, DIRECT, prototypeChanges),
target.type);
}
}
@@ -1693,12 +1695,13 @@
return AbortReason.UNSAFE_INLINING;
}
- private class SingleTypeMapperGraphLens extends GraphLens {
+ private class SingleTypeMapperGraphLens extends NonIdentityGraphLens {
private final DexType source;
private final DexProgramClass target;
public SingleTypeMapperGraphLens(DexType source, DexProgramClass target) {
+ super(appView.dexItemFactory(), GraphLens.getIdentityLens());
this.source = source;
this.target = target;
}
@@ -1729,30 +1732,47 @@
}
@Override
- public DexType lookupType(DexType type) {
- return type == source ? target.type : mergedClasses.getOrDefault(type, type);
+ public final DexType internalDescribeLookupClassType(DexType previous) {
+ return previous == source ? target.type : mergedClasses.getOrDefault(previous, previous);
}
@Override
- public GraphLensLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
+ protected DexMethod internalGetPreviousMethodSignature(DexMethod method) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public MethodLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
// First look up the method using the existing graph lens (for example, the type will have
// changed if the method was publicized by ClassAndMemberPublicizer).
- GraphLensLookupResult lookup = appView.graphLens().lookupMethod(method, context, type);
+ MethodLookupResult lookup = appView.graphLens().lookupMethod(method, context, type);
// Then check if there is a renaming due to the vertical class merger.
- DexMethod newMethod = renamedMembersLens.methodMap.get(lookup.getMethod());
+ DexMethod newMethod = renamedMembersLens.methodMap.get(lookup.getReference());
if (newMethod == null) {
return lookup;
}
+ MethodLookupResult.Builder methodLookupResultBuilder =
+ MethodLookupResult.builder(this)
+ .setReference(newMethod)
+ .setPrototypeChanges(lookup.getPrototypeChanges())
+ .setType(lookup.getType());
if (lookup.getType() == Type.INTERFACE) {
// If an interface has been merged into a class, invoke-interface needs to be translated
// to invoke-virtual.
DexClass clazz = appInfo.definitionFor(newMethod.holder);
if (clazz != null && !clazz.accessFlags.isInterface()) {
assert appInfo.definitionFor(method.holder).accessFlags.isInterface();
- return new GraphLensLookupResult(newMethod, Type.VIRTUAL, lookup.getPrototypeChanges());
+ methodLookupResultBuilder.setType(Type.VIRTUAL);
}
}
- return new GraphLensLookupResult(newMethod, lookup.getType(), lookup.getPrototypeChanges());
+ return methodLookupResultBuilder.build();
+ }
+
+ @Override
+ protected MethodLookupResult internalDescribeLookupMethod(
+ MethodLookupResult previous, DexMethod context) {
+ // This is unreachable since we override the implementation of lookupMethod() above.
+ throw new Unreachable();
}
@Override
@@ -1767,6 +1787,12 @@
}
@Override
+ protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
+ // This is unreachable since we override the implementation of lookupField() above.
+ throw new Unreachable();
+ }
+
+ @Override
public boolean isContextFreeForMethods() {
return true;
}
@@ -1855,41 +1881,41 @@
@Override
public void registerInvokeVirtual(DexMethod method) {
assert context != null;
- GraphLensLookupResult lookup =
+ MethodLookupResult lookup =
appView.graphLens().lookupMethod(method, context.getReference(), Type.VIRTUAL);
- checkMethodReference(lookup.getMethod(), OptionalBool.FALSE);
+ checkMethodReference(lookup.getReference(), OptionalBool.FALSE);
}
@Override
public void registerInvokeDirect(DexMethod method) {
assert context != null;
- GraphLensLookupResult lookup =
+ MethodLookupResult lookup =
appView.graphLens().lookupMethod(method, context.getReference(), Type.DIRECT);
- checkMethodReference(lookup.getMethod(), OptionalBool.UNKNOWN);
+ checkMethodReference(lookup.getReference(), OptionalBool.UNKNOWN);
}
@Override
public void registerInvokeStatic(DexMethod method) {
assert context != null;
- GraphLensLookupResult lookup =
+ MethodLookupResult lookup =
appView.graphLens().lookupMethod(method, context.getReference(), Type.STATIC);
- checkMethodReference(lookup.getMethod(), OptionalBool.UNKNOWN);
+ checkMethodReference(lookup.getReference(), OptionalBool.UNKNOWN);
}
@Override
public void registerInvokeInterface(DexMethod method) {
assert context != null;
- GraphLensLookupResult lookup =
+ MethodLookupResult lookup =
appView.graphLens().lookupMethod(method, context.getReference(), Type.INTERFACE);
- checkMethodReference(lookup.getMethod(), OptionalBool.TRUE);
+ checkMethodReference(lookup.getReference(), OptionalBool.TRUE);
}
@Override
public void registerInvokeSuper(DexMethod method) {
assert context != null;
- GraphLensLookupResult lookup =
+ MethodLookupResult lookup =
appView.graphLens().lookupMethod(method, context.getReference(), Type.SUPER);
- checkMethodReference(lookup.getMethod(), OptionalBool.UNKNOWN);
+ checkMethodReference(lookup.getReference(), OptionalBool.UNKNOWN);
}
@Override
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 683f1c9..2848d3f 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
@@ -50,7 +50,7 @@
interface GraphLensLookupResultProvider {
- abstract GraphLensLookupResult get(RewrittenPrototypeDescription prototypeChanges);
+ abstract MethodLookupResult get(RewrittenPrototypeDescription prototypeChanges);
}
private final AppView<?> appView;
@@ -88,7 +88,7 @@
@Override
public DexType getOriginalType(DexType type) {
- return previousLens.getOriginalType(type);
+ return getPrevious().getOriginalType(type);
}
@Override
@@ -98,19 +98,16 @@
}
@Override
- public GraphLensLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
- assert context != null || verifyIsContextFreeForMethod(method);
- assert context == null || type != null;
- DexMethod previousContext =
- originalMethodSignaturesForBridges.containsKey(context)
- ? originalMethodSignaturesForBridges.get(context)
- : originalMethodSignatures.getOrDefault(context, context);
- GraphLensLookupResult lookup = previousLens.lookupMethod(method, previousContext, type);
- if (lookup.getType() == Type.SUPER && !mergedMethods.contains(context)) {
+ public MethodLookupResult internalDescribeLookupMethod(
+ MethodLookupResult previous, DexMethod context) {
+ assert context != null || verifyIsContextFreeForMethod(previous.getReference());
+ assert context == null || previous.getType() != null;
+ if (previous.getType() == Type.SUPER && !mergedMethods.contains(context)) {
Map<DexMethod, GraphLensLookupResultProvider> virtualToDirectMethodMap =
- contextualVirtualToDirectMethodMaps.get(context.holder);
+ contextualVirtualToDirectMethodMaps.get(context.getHolderType());
if (virtualToDirectMethodMap != null) {
- GraphLensLookupResultProvider result = virtualToDirectMethodMap.get(lookup.getMethod());
+ GraphLensLookupResultProvider result =
+ virtualToDirectMethodMap.get(previous.getReference());
if (result != null) {
// If the super class A of the enclosing class B (i.e., context.holder())
// has been merged into B during vertical class merging, and this invoke-super instruction
@@ -118,18 +115,20 @@
// method and moved into B, so that we need to use an invoke-direct instruction instead of
// invoke-super (or invoke-static, if the method was originally a default interface
// method).
- return result.get(lookup.getPrototypeChanges());
+ return result.get(previous.getPrototypeChanges());
}
}
}
- DexMethod newMethod = methodMap.get(lookup.getMethod());
+ DexMethod newMethod = methodMap.get(previous.getReference());
if (newMethod == null) {
- return lookup;
+ return previous;
}
- return new GraphLensLookupResult(
- newMethod,
- mapInvocationType(newMethod, lookup.getMethod(), lookup.getType()),
- internalDescribePrototypeChanges(lookup.getPrototypeChanges(), newMethod));
+ return MethodLookupResult.builder(this)
+ .setReference(newMethod)
+ .setPrototypeChanges(
+ internalDescribePrototypeChanges(previous.getPrototypeChanges(), newMethod))
+ .setType(mapInvocationType(newMethod, previous.getReference(), previous.getType()))
+ .build();
}
@Override
@@ -139,13 +138,13 @@
@Override
public boolean isContextFreeForMethods() {
- return contextualVirtualToDirectMethodMaps.isEmpty() && previousLens.isContextFreeForMethods();
+ return contextualVirtualToDirectMethodMaps.isEmpty() && getPrevious().isContextFreeForMethods();
}
@Override
public boolean verifyIsContextFreeForMethod(DexMethod method) {
- assert previousLens.verifyIsContextFreeForMethod(method);
- DexMethod previous = previousLens.lookupMethod(method);
+ assert getPrevious().verifyIsContextFreeForMethod(method);
+ DexMethod previous = getPrevious().lookupMethod(method);
assert contextualVirtualToDirectMethodMaps.values().stream()
.noneMatch(virtualToDirectMethodMap -> virtualToDirectMethodMap.containsKey(previous));
return true;
@@ -194,14 +193,14 @@
for (Map.Entry<DexMethod, GraphLensLookupResultProvider> innerEntry :
entry.getValue().entrySet()) {
DexMethod from = innerEntry.getKey();
- GraphLensLookupResult rewriting =
+ MethodLookupResult rewriting =
innerEntry.getValue().get(RewrittenPrototypeDescription.none());
DexMethod to =
- builder.getMethodSignatureAfterClassMerging(rewriting.getMethod(), mergedClasses);
+ builder.getMethodSignatureAfterClassMerging(rewriting.getReference(), mergedClasses);
newBuilder.mapVirtualMethodToDirectInType(
from,
prototypeChanges ->
- new GraphLensLookupResult(to, rewriting.getType(), prototypeChanges),
+ new MethodLookupResult(to, null, rewriting.getType(), prototypeChanges),
context);
}
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
index c03022e..380c2dc 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.synthesis;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
+import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.origin.Origin;
/**
@@ -29,7 +29,7 @@
this.origin = origin;
}
- SynthesizingContext rewrite(NestedGraphLens lens) {
+ SynthesizingContext rewrite(NonIdentityGraphLens lens) {
DexType rewritten = lens.lookupType(type);
return rewritten == type ? this : new SynthesizingContext(type, origin);
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index a2dd07e..2996794 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.GraphLens.Builder;
+import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
import com.android.tools.r8.shaking.MainDexClasses;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -99,7 +100,7 @@
Map<DexType, EquivalenceGroup<SyntheticMethodDefinition>> equivalences =
computeActualEquivalences(potentialEquivalences, options.itemFactory);
- Builder lensBuilder = GraphLens.builder();
+ Builder lensBuilder = NestedGraphLens.builder();
List<DexProgramClass> newProgramClasses = new ArrayList<>();
List<DexProgramClass> finalSyntheticClasses = new ArrayList<>();
buildLensAndProgram(
@@ -144,11 +145,12 @@
DexApplication application,
InternalOptions options,
MainDexClasses mainDexClasses) {
- if (options.intermediate) {
+ boolean includeSynthesizedClassMappingInOutput = options.intermediate && !options.cfToCfDesugar;
+ if (includeSynthesizedClassMappingInOutput) {
updateSynthesizedClassMapping(application, finalSyntheticClasses);
}
updateMainDexListWithSynthesizedClassMap(application, mainDexClasses);
- if (!options.intermediate) {
+ if (!includeSynthesizedClassMappingInOutput) {
clearSynthesizedClassMapping(application);
}
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index b3c179f..ef45975 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -10,7 +10,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
+import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.synthesis.SyntheticFinalization.Result;
import com.google.common.collect.ImmutableList;
@@ -233,7 +233,8 @@
nextSyntheticId);
}
- public CommittedItems commitRewrittenWithLens(DexApplication application, NestedGraphLens lens) {
+ public CommittedItems commitRewrittenWithLens(
+ DexApplication application, NonIdentityGraphLens lens) {
// Rewrite the previously committed synthetic types.
ImmutableSet<DexType> rewrittenLegacyTypes = lens.rewriteTypes(this.legacySyntheticTypes);
ImmutableMap.Builder<DexType, SyntheticReference> rewrittenItems = ImmutableMap.builder();
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java
index 326eee0..6070e49 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java
@@ -6,7 +6,7 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
+import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.graph.ProgramMethod;
import java.util.function.Function;
@@ -40,7 +40,7 @@
}
@Override
- SyntheticReference rewrite(NestedGraphLens lens) {
+ SyntheticReference rewrite(NonIdentityGraphLens lens) {
SynthesizingContext context = getContext().rewrite(lens);
DexMethod rewritten = lens.lookupMethod(method);
return context == getContext() && rewritten == method
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticReference.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticReference.java
index 9981624..9b95337 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticReference.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticReference.java
@@ -5,7 +5,7 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
+import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.origin.Origin;
import java.util.function.Function;
@@ -37,5 +37,5 @@
abstract DexType getHolder();
- abstract SyntheticReference rewrite(NestedGraphLens lens);
+ abstract SyntheticReference rewrite(NonIdentityGraphLens lens);
}
diff --git a/src/main/java/com/android/tools/r8/utils/BiForEachable.java b/src/main/java/com/android/tools/r8/utils/BiForEachable.java
new file mode 100644
index 0000000..f1dd413
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/BiForEachable.java
@@ -0,0 +1,11 @@
+// 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;
+
+import java.util.function.BiConsumer;
+
+public interface BiForEachable<S, T> {
+
+ void forEach(BiConsumer<S, T> consumer);
+}
diff --git a/src/main/java/com/android/tools/r8/utils/ComparatorUtils.java b/src/main/java/com/android/tools/r8/utils/ComparatorUtils.java
new file mode 100644
index 0000000..a42fd3a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ComparatorUtils.java
@@ -0,0 +1,64 @@
+// 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;
+
+import java.util.Comparator;
+import java.util.List;
+
+public class ComparatorUtils {
+
+ public static <T extends Comparable<T>> Comparator<List<T>> listComparator() {
+ return listComparator(T::compareTo);
+ }
+
+ public static <T> Comparator<List<T>> listComparator(Comparator<T> comparator) {
+ return (List<T> xs, List<T> ys) -> {
+ int diff = Integer.compare(xs.size(), ys.size());
+ for (int i = 0; i < xs.size() && diff == 0; i++) {
+ diff = comparator.compare(xs.get(i), ys.get(i));
+ }
+ return diff;
+ };
+ }
+
+ // Compare pair-wise integers in sequenced order, i.e., (A1, A2), (B1, B2), (C1, C2), ...
+ public static int compareInts(int... ints) {
+ assert ints.length % 2 == 0;
+ int diff = 0;
+ for (int i = 0; i < ints.length && diff == 0; ) {
+ diff = Integer.compare(ints[i++], ints[i++]);
+ }
+ return diff;
+ }
+
+ public static int compareIntArray(int[] xs, int[] ys) {
+ int diff = Integer.compare(xs.length, ys.length);
+ for (int i = 0; i < xs.length && diff == 0; i++) {
+ diff = Integer.compare(xs[i], ys[i]);
+ }
+ return diff;
+ }
+
+ public static int compareShortArray(short[] xs, short[] ys) {
+ int diff = Integer.compare(xs.length, ys.length);
+ for (int i = 0; i < xs.length && diff == 0; i++) {
+ diff = Short.compare(xs[i], ys[i]);
+ }
+ return diff;
+ }
+
+ public static <T extends Comparable<T>> Comparator<T[]> arrayComparator() {
+ return arrayComparator(T::compareTo);
+ }
+
+ public static <T> Comparator<T[]> arrayComparator(Comparator<T> comparator) {
+ return (T[] xs, T[] ys) -> {
+ int diff = Integer.compare(xs.length, ys.length);
+ for (int i = 0; i < xs.length && diff == 0; i++) {
+ diff = comparator.compare(xs[i], ys[i]);
+ }
+ return diff;
+ };
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java b/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java
index 2079af2..c1c7d5a 100644
--- a/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java
@@ -4,10 +4,19 @@
package com.android.tools.r8.utils;
+import java.util.Set;
import java.util.function.Consumer;
public class ConsumerUtils {
+ public static <T> Consumer<T> acceptIfNotSeen(Consumer<T> consumer, Set<T> seen) {
+ return element -> {
+ if (seen.add(element)) {
+ consumer.accept(element);
+ }
+ };
+ }
+
public static <T> Consumer<T> emptyConsumer() {
return ignore -> {};
}
diff --git a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
index 8c070a5..95b0fde 100644
--- a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
@@ -291,6 +291,42 @@
.replace(DESCRIPTOR_PACKAGE_SEPARATOR, JAVA_PACKAGE_SEPARATOR);
}
+ /**
+ * Get the simple class name from its descriptor.
+ *
+ * @param classDescriptor a class descriptor i.e. "Ljava/lang/Object;"
+ * @return simple class name i.e. "Object"
+ */
+ public static String getSimpleClassNameFromDescriptor(String classDescriptor) {
+ return classDescriptor.substring(
+ getSimpleClassNameIndex(classDescriptor), classDescriptor.length() - 1);
+ }
+
+ /**
+ * Replace the simple class name from its descriptor with a new simple name.
+ *
+ * @param classDescriptor a class descriptor i.e. "Ljava/lang/Object;"
+ * @param newSimpleName a new simple name e.g. "NewObject"
+ * @return updated class descriptor i.e. "Ljava/lang/NewObject;"
+ */
+ public static String replaceSimpleClassNameInDescriptor(
+ String classDescriptor, String newSimpleName) {
+ return "L"
+ + classDescriptor.substring(1, getSimpleClassNameIndex(classDescriptor))
+ + newSimpleName
+ + ";";
+ }
+
+ /**
+ * Finds the index of the simple class name in its descriptor.
+ *
+ * @param classDescriptor a class descriptor i.e. "Ljava/lang/Object;"
+ * @return the index of the simple name i.e. 11.
+ */
+ private static int getSimpleClassNameIndex(String classDescriptor) {
+ return Integer.max(classDescriptor.lastIndexOf("/"), 0) + 1;
+ }
+
/**
* Get canonical class name from its descriptor.
*
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 cf31dec..1a53b74 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -253,6 +253,15 @@
public int callGraphLikelySpuriousCallEdgeThreshold = 50;
+ public int verificationSizeLimitInBytes() {
+ if (testing.verificationSizeLimitInBytesOverride > -1) {
+ return testing.verificationSizeLimitInBytesOverride;
+ }
+ // For CF we use the defined limit in the spec. For DEX we use the limit of the static verifier
+ // https://android.googlesource.com/platform/art/+/android10-release/compiler/compiler.cc#48
+ return isGeneratingClassFiles() ? 65534 : 16383;
+ }
+
// TODO(b/141719453): The inlining limit at least should be consistent with normal inlining.
public int classInliningInstructionLimit = 10;
public int classInliningInstructionAllowance = 50;
@@ -1218,6 +1227,7 @@
public boolean allowClassInlinerGracefulExit =
System.getProperty("com.android.tools.r8.disallowClassInlinerGracefulExit") == null;
public boolean reportUnusedProguardConfigurationRules = false;
+ public boolean alwaysUseExistingAccessInfoCollectionsInMemberRebinding = true;
public boolean alwaysUsePessimisticRegisterAllocation = false;
public boolean enableCheckCastAndInstanceOfRemoval = true;
public boolean enableDeadSwitchCaseElimination = true;
@@ -1245,6 +1255,7 @@
public boolean enumUnboxingRewriteJavaCGeneratedMethod = false;
public boolean assertConsistentRenamingOfSignature = false;
public boolean allowStaticInterfaceMethodsForPreNApiLevel = false;
+ public int verificationSizeLimitInBytesOverride = -1;
// Flag to allow processing of resources in D8. A data resource consumer still needs to be
// specified.
@@ -1257,12 +1268,13 @@
public boolean enableForceNestBasedAccessDesugaringForTest = false;
public boolean verifyKeptGraphInfo = false;
+ public boolean readInputStackMaps = true;
+ public boolean disableStackMapVerification = false;
+
// Force each call of application read to dump its inputs to a file, which is subsequently
// deleted. Useful to check that our dump functionality does not cause compilation failure.
public boolean dumpAll = false;
- public boolean readInputStackMaps = false;
-
// Option for testing outlining with interface array arguments, see b/132420510.
public boolean allowOutlinerInterfaceArrayArguments = false;
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 3929e87..c899a75 100644
--- a/src/main/java/com/android/tools/r8/utils/MapUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MapUtils.java
@@ -4,11 +4,39 @@
package com.android.tools.r8.utils;
+import com.android.tools.r8.utils.StringUtils.BraceType;
import java.util.Map;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.function.IntFunction;
public class MapUtils {
+ public static <K, V> Map<K, V> map(
+ Map<K, V> map,
+ IntFunction<Map<K, V>> factory,
+ Function<K, K> keyMapping,
+ Function<V, V> valueMapping,
+ BiFunction<V, V, V> valueMerger) {
+ Map<K, V> result = factory.apply(map.size());
+ map.forEach(
+ (key, value) -> {
+ K newKey = keyMapping.apply(key);
+ V newValue = valueMapping.apply(value);
+ V existingValue = result.put(newKey, newValue);
+ if (existingValue != null) {
+ result.put(newKey, valueMerger.apply(existingValue, newValue));
+ }
+ });
+ return result;
+ }
+
public static <T> void removeIdentityMappings(Map<T, T> map) {
map.entrySet().removeIf(entry -> entry.getKey() == entry.getValue());
}
+
+ public static String toString(Map<?, ?> map) {
+ return StringUtils.join(
+ map.entrySet(), ",", BraceType.TUBORG, entry -> entry.getKey() + ":" + entry.getValue());
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java
new file mode 100644
index 0000000..ad65701
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java
@@ -0,0 +1,30 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.collections;
+
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class BidirectionalManyToOneMap<K, V> {
+
+ private final Map<K, V> backing = new IdentityHashMap<>();
+ private final Map<V, Set<K>> inverse = new IdentityHashMap<>();
+
+ public V getOrDefault(K key, V value) {
+ return backing.getOrDefault(key, value);
+ }
+
+ public Set<K> getKeys(V value) {
+ return inverse.getOrDefault(value, Collections.emptySet());
+ }
+
+ public void put(K key, V value) {
+ backing.put(key, value);
+ inverse.computeIfAbsent(value, ignore -> new LinkedHashSet<>()).add(key);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/ImmutableDeque.java b/src/main/java/com/android/tools/r8/utils/collections/ImmutableDeque.java
new file mode 100644
index 0000000..17c5e4a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/collections/ImmutableDeque.java
@@ -0,0 +1,115 @@
+// 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.errors.Unreachable;
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.function.Predicate;
+
+@SuppressWarnings("NullableProblems")
+public class ImmutableDeque<T> extends ArrayDeque<T> {
+
+ private boolean isClosed = false;
+
+ private ImmutableDeque(Collection<T> items) {
+ super(items);
+ }
+
+ private void close() {
+ isClosed = true;
+ }
+
+ @Override
+ public void push(T t) {
+ throw new Unreachable("Modification not allowed on immutable structure");
+ }
+
+ @Override
+ public T pop() {
+ throw new Unreachable("Modification not allowed on immutable structure");
+ }
+
+ @Override
+ public void addFirst(T t) {
+ throw new Unreachable("Modification not allowed on immutable structure");
+ }
+
+ @Override
+ public void addLast(T t) {
+ if (isClosed) {
+ throw new Unreachable("Modification not allowed on immutable structure");
+ } else {
+ super.addLast(t);
+ }
+ }
+
+ @Override
+ public boolean removeFirstOccurrence(Object o) {
+ throw new Unreachable("Modification not allowed on immutable structure");
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ throw new Unreachable("Modification not allowed on immutable structure");
+ }
+
+ @Override
+ public T removeFirst() {
+ throw new Unreachable("Modification not allowed on immutable structure");
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> c) {
+ throw new Unreachable("Modification not allowed on immutable structure");
+ }
+
+ @Override
+ public boolean removeIf(Predicate<? super T> filter) {
+ throw new Unreachable("Modification not allowed on immutable structure");
+ }
+
+ @Override
+ public boolean removeLastOccurrence(Object o) {
+ throw new Unreachable("Modification not allowed on immutable structure");
+ }
+
+ @Override
+ public T removeLast() {
+ throw new Unreachable("Modification not allowed on immutable structure");
+ }
+
+ @Override
+ public T remove() {
+ throw new Unreachable("Modification not allowed on immutable structure");
+ }
+
+ @Override
+ public boolean add(T t) {
+ if (isClosed) {
+ throw new Unreachable("Modification not allowed on immutable structure");
+ } else {
+ return super.add(t);
+ }
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends T> c) {
+ if (isClosed) {
+ throw new Unreachable("Modification not allowed on immutable structure");
+ } else {
+ return super.addAll(c);
+ }
+ }
+
+ @SafeVarargs
+ public static <T> Deque<T> of(T... items) {
+ ImmutableDeque<T> deque = new ImmutableDeque<>(Arrays.asList(items));
+ deque.close();
+ return deque;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/ImmutableInt2ReferenceSortedMap.java b/src/main/java/com/android/tools/r8/utils/collections/ImmutableInt2ReferenceSortedMap.java
index af41bf1..a4cb27e 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/ImmutableInt2ReferenceSortedMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/ImmutableInt2ReferenceSortedMap.java
@@ -13,7 +13,6 @@
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
-import org.jetbrains.annotations.Nullable;
public class ImmutableInt2ReferenceSortedMap<V> extends Int2ReferenceSortedMaps.EmptySortedMap<V> {
@@ -31,6 +30,10 @@
return new ImmutableInt2ReferenceSortedMap<>(new Int2ReferenceAVLTreeMap<>(keys, values));
}
+ public static <V> ImmutableInt2ReferenceSortedMap<V> empty() {
+ return new ImmutableInt2ReferenceSortedMap<>(new Int2ReferenceAVLTreeMap<>());
+ }
+
public static <V> Builder<V> builder() {
return new Builder<>();
}
@@ -50,6 +53,21 @@
}
@Override
+ public V get(int k) {
+ return sortedMap.get(k);
+ }
+
+ @Override
+ public V get(Object ok) {
+ return sortedMap.get(ok);
+ }
+
+ @Override
+ public V getOrDefault(Object key, V defaultValue) {
+ return sortedMap.getOrDefault(key, defaultValue);
+ }
+
+ @Override
public int size() {
return sortedMap.size();
}
@@ -150,7 +168,6 @@
throw new Unreachable("Should not modify an immutable structure");
}
- @Nullable
@Override
public V putIfAbsent(Integer key, V value) {
throw new Unreachable("Should not modify an immutable structure");
diff --git a/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java b/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java
index 0732bcb..2412cfd 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java
@@ -4,9 +4,11 @@
package com.android.tools.r8.utils.collections;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.ProgramMethod;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
@@ -15,26 +17,34 @@
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
-import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Supplier;
import java.util.stream.Stream;
public class ProgramMethodSet implements Iterable<ProgramMethod> {
- private static final ProgramMethodSet EMPTY = new ProgramMethodSet(ImmutableMap.of());
+ private static final ProgramMethodSet EMPTY = new ProgramMethodSet(ImmutableMap::of);
- private Map<DexMethod, ProgramMethod> backing;
+ private final Map<DexMethod, ProgramMethod> backing;
+ private final Supplier<? extends Map<DexMethod, ProgramMethod>> backingFactory;
- ProgramMethodSet(Map<DexMethod, ProgramMethod> backing) {
+ protected ProgramMethodSet(Supplier<? extends Map<DexMethod, ProgramMethod>> backingFactory) {
+ this(backingFactory, backingFactory.get());
+ }
+
+ protected ProgramMethodSet(
+ Supplier<? extends Map<DexMethod, ProgramMethod>> backingFactory,
+ Map<DexMethod, ProgramMethod> backing) {
this.backing = backing;
+ this.backingFactory = backingFactory;
}
public static ProgramMethodSet create() {
- return new ProgramMethodSet(new IdentityHashMap<>());
+ return new ProgramMethodSet(IdentityHashMap::new);
}
public static ProgramMethodSet create(int capacity) {
- return new ProgramMethodSet(new IdentityHashMap<>(capacity));
+ return new ProgramMethodSet(IdentityHashMap::new, new IdentityHashMap<>(capacity));
}
public static ProgramMethodSet create(ProgramMethod element) {
@@ -44,15 +54,11 @@
}
public static ProgramMethodSet createConcurrent() {
- return new ProgramMethodSet(new ConcurrentHashMap<>());
+ return new ProgramMethodSet(ConcurrentHashMap::new);
}
public static ProgramMethodSet createLinked() {
- return new ProgramMethodSet(new LinkedHashMap<>());
- }
-
- public static ProgramMethodSet createSorted() {
- return new ProgramMethodSet(new TreeMap<>(DexMethod::slowCompareTo));
+ return new ProgramMethodSet(LinkedHashMap::new);
}
public static ProgramMethodSet empty() {
@@ -107,6 +113,18 @@
return remove(method.getReference());
}
+ public ProgramMethodSet rewrittenWithLens(DexDefinitionSupplier definitions, GraphLens lens) {
+ ProgramMethodSet rewritten = new ProgramMethodSet(backingFactory);
+ forEach(
+ method -> {
+ ProgramMethod newMethod = lens.mapProgramMethod(method, definitions);
+ if (newMethod != null) {
+ rewritten.add(newMethod);
+ }
+ });
+ return rewritten;
+ }
+
public int size() {
return backing.size();
}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/SortedProgramMethodSet.java b/src/main/java/com/android/tools/r8/utils/collections/SortedProgramMethodSet.java
index 4c6db49..6f6d1a5 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/SortedProgramMethodSet.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/SortedProgramMethodSet.java
@@ -4,8 +4,10 @@
package com.android.tools.r8.utils.collections;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.utils.ForEachable;
import com.android.tools.r8.utils.ForEachableUtils;
@@ -13,11 +15,12 @@
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
+import java.util.function.Supplier;
public class SortedProgramMethodSet extends ProgramMethodSet {
- private SortedProgramMethodSet(TreeMap<DexMethod, ProgramMethod> backing) {
- super(backing);
+ private SortedProgramMethodSet(Supplier<TreeMap<DexMethod, ProgramMethod>> backingFactory) {
+ super(backingFactory);
}
public static SortedProgramMethodSet create() {
@@ -32,12 +35,19 @@
public static SortedProgramMethodSet create(ForEachable<ProgramMethod> methods) {
SortedProgramMethodSet result =
- new SortedProgramMethodSet(new TreeMap<>(DexMethod::slowCompareTo));
+ new SortedProgramMethodSet(() -> new TreeMap<>(DexMethod::slowCompareTo));
methods.forEach(result::add);
return result;
}
@Override
+ public SortedProgramMethodSet rewrittenWithLens(
+ DexDefinitionSupplier definitions, GraphLens lens) {
+ return create(
+ consumer -> forEach(method -> consumer.accept(lens.mapProgramMethod(method, definitions))));
+ }
+
+ @Override
public Set<DexEncodedMethod> toDefinitionSet() {
Comparator<DexEncodedMethod> comparator =
(x, y) -> x.getReference().slowCompareTo(y.getReference());
diff --git a/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java b/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java
index 2ae20c9..cea34f9 100644
--- a/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java
@@ -310,12 +310,7 @@
.addProguardConfiguration(ImmutableList.of("-keepattributes *"), Origin.unknown())
.setOutput(outputJar, OutputMode.ClassFile)
.build();
- ToolHelper.runR8(
- command,
- options -> {
- options.skipIR = true;
- options.testing.readInputStackMaps = true;
- });
+ ToolHelper.runR8(command, options -> options.skipIR = true);
ArchiveClassFileProvider expected = new ArchiveClassFileProvider(inputJar);
ArchiveClassFileProvider actual = new ArchiveClassFileProvider(outputJar);
assertEquals(getSortedDescriptorList(expected), getSortedDescriptorList(actual));
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index eddf1ee..d9ed43f 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -1193,6 +1193,7 @@
private static Map<String, Consumer<InternalOptions>> configurations =
ImmutableMap.of(
// Has a new-instance instruction that attempts to instantiate an interface.
+ "162-method-resolution", options -> options.testing.disableStackMapVerification = true,
"435-new-instance", options -> options.testing.allowTypeErrors = true);
private static List<String> failuresToTriage = ImmutableList.of(
@@ -1790,7 +1791,7 @@
builder
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.getDefault()));
}
- D8.run(builder.build());
+ ToolHelper.runD8(builder, compilationOptions::accept);
break;
}
case R8:
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index 8b19ae3..4e8d99e 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -106,7 +106,7 @@
.withOptionConsumer(opts -> opts.enableClassInlining = false)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 103, "lambdadesugaring"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 102, "lambdadesugaring"))
.run();
test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
@@ -146,7 +146,7 @@
.withOptionConsumer(opts -> opts.enableClassInlining = false)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 103, "lambdadesugaring"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 102, "lambdadesugaring"))
.run();
test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
index ab5027b..3935988 100644
--- a/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
@@ -282,6 +282,7 @@
return assertDiagnosticThatMatches(getInfos(), "info", matcher);
}
+ @Override
public TestDiagnosticMessages assertWarningThatMatches(Matcher<Diagnostic> matcher) {
return assertDiagnosticThatMatches(getWarnings(), "warning", matcher);
}
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 98881e9..69a42a4 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -261,6 +261,11 @@
return addKeepRules("-keepattributes " + String.join(",", attributes));
}
+ public T addKeepAttributeInnerClassesAndEnclosingMethod() {
+ return addKeepAttributes(
+ ProguardKeepAttributes.INNER_CLASSES, ProguardKeepAttributes.ENCLOSING_METHOD);
+ }
+
public T addKeepAttributeLineNumberTable() {
return addKeepAttributes(ProguardKeepAttributes.LINE_NUMBER_TABLE);
}
@@ -273,6 +278,10 @@
return addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS);
}
+ public T addKeepRuntimeVisibleParameterAnnotations() {
+ return addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS);
+ }
+
public T addKeepAllAttributes() {
return addKeepAttributes("*");
}
diff --git a/src/test/java/com/android/tools/r8/cf/stackmap/StackMapVerificationNoFrameForHandlerTest.java b/src/test/java/com/android/tools/r8/cf/stackmap/StackMapVerificationNoFrameForHandlerTest.java
new file mode 100644
index 0000000..625a3dc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/stackmap/StackMapVerificationNoFrameForHandlerTest.java
@@ -0,0 +1,262 @@
+// 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.cf.stackmap;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.JvmTestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class StackMapVerificationNoFrameForHandlerTest extends TestBase {
+
+ private final TestParameters parameters;
+ private final boolean includeFrameInHandler;
+ private final String EXPECTED_OUTPUT = "Hello World!";
+ private final String EXPECTED_VERIFY_ERROR =
+ "Expected stack map table for method with non-linear control flow";
+ private final String EXPECTED_JVM_ERROR =
+ "java.lang.VerifyError: Expecting a stackmap frame at branch target";
+
+ @Parameters(name = "{0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+ }
+
+ public StackMapVerificationNoFrameForHandlerTest(
+ TestParameters parameters, boolean includeFrameInHandler) {
+ this.parameters = parameters;
+ this.includeFrameInHandler = includeFrameInHandler;
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ JvmTestRunResult mainResult =
+ testForJvm()
+ .addProgramClassFileData(
+ includeFrameInHandler
+ ? MainDump.dump()
+ : transformer(MainDump.dump(), Reference.classFromClass(Main.class))
+ .stripFrames("main")
+ .transform())
+ .run(parameters.getRuntime(), Main.class);
+ if (includeFrameInHandler) {
+ mainResult.assertSuccessWithOutputLines(EXPECTED_OUTPUT);
+ } else {
+ mainResult.assertFailureWithErrorThatMatches(containsString(EXPECTED_JVM_ERROR));
+ }
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8()
+ .addProgramClassFileData(
+ includeFrameInHandler
+ ? MainDump.dump()
+ : transformer(MainDump.dump(), Reference.classFromClass(Main.class))
+ .stripFrames("main")
+ .transform())
+ .setMinApi(parameters.getApiLevel())
+ .compileWithExpectedDiagnostics(this::verifyWarningsRegardingStackMap)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED_OUTPUT);
+ }
+
+ @Test
+ public void testHandlerR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(
+ includeFrameInHandler
+ ? MainDump.dump()
+ : transformer(MainDump.dump(), Reference.classFromClass(Main.class))
+ .stripFrames("main")
+ .transform())
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .allowDiagnosticWarningMessages(!includeFrameInHandler)
+ .compileWithExpectedDiagnostics(this::verifyWarningsRegardingStackMap)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED_OUTPUT);
+ }
+
+ private void verifyWarningsRegardingStackMap(TestDiagnosticMessages diagnostics) {
+ if (includeFrameInHandler) {
+ diagnostics.assertNoMessages();
+ } else {
+ diagnostics.assertOnlyWarnings();
+ diagnostics.assertWarningsMatch(diagnosticMessage(containsString(EXPECTED_VERIFY_ERROR)));
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ try {
+ getThrowable(new Throwable());
+ } catch (Throwable e) {
+ }
+ System.out.println("Hello World!");
+ }
+
+ public static Throwable getThrowable(Throwable throwable) {
+ if (System.currentTimeMillis() > 0) {
+ return new RuntimeException(throwable);
+ } else {
+ throw new ClassCastException();
+ }
+ }
+ }
+
+ /**
+ * The dump is mostly the code obtained from the Main class above, however, some instructions are
+ * removed to have the frames being the same with linear flow:
+ *
+ * <pre>
+ * try {
+ * getThrowable(new Throwable());
+ * pop
+ * goto lbl3
+ * } catch (Throwable e) {
+ * astore(1);
+ * goto lbl3
+ * }
+ * lbl3
+ * System.out.println("Hello World!");
+ * </pre>
+ *
+ * becomes:
+ *
+ * <pre>
+ * try {
+ * getThrowable(new Throwable());
+ * } catch (Throwable e) {
+ * pop;
+ * }
+ * lbl3
+ * System.out.println("Hello World!");
+ * </pre>
+ */
+ public static class MainDump implements Opcodes {
+
+ public static byte[] dump() {
+
+ ClassWriter classWriter = new ClassWriter(0);
+ MethodVisitor methodVisitor;
+
+ classWriter.visit(
+ V1_8,
+ ACC_PUBLIC | ACC_SUPER,
+ "com/android/tools/r8/cf/stackmap/StackMapVerificationNoFrameForHandlerTest$Main",
+ null,
+ "java/lang/Object",
+ null);
+
+ classWriter.visitInnerClass(
+ "com/android/tools/r8/cf/stackmap/StackMapVerificationNoFrameForHandlerTest$Main",
+ "com/android/tools/r8/cf/stackmap/StackMapVerificationNoFrameForHandlerTest",
+ "Main",
+ ACC_PUBLIC | ACC_STATIC);
+
+ {
+ methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+ methodVisitor.visitCode();
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+ methodVisitor.visitInsn(RETURN);
+ methodVisitor.visitMaxs(1, 1);
+ methodVisitor.visitEnd();
+ }
+ {
+ methodVisitor =
+ classWriter.visitMethod(
+ ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ Label label1 = new Label();
+ Label label2 = new Label();
+ methodVisitor.visitTryCatchBlock(label0, label1, label2, "java/lang/Throwable");
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitTypeInsn(NEW, "java/lang/Throwable");
+ methodVisitor.visitInsn(DUP);
+ methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Throwable", "<init>", "()V", false);
+ methodVisitor.visitMethodInsn(
+ INVOKESTATIC,
+ "com/android/tools/r8/cf/stackmap/StackMapVerificationNoFrameForHandlerTest$Main",
+ "getThrowable",
+ "(Ljava/lang/Throwable;)Ljava/lang/Throwable;",
+ false);
+ methodVisitor.visitLabel(label1);
+ methodVisitor.visitLabel(label2);
+ methodVisitor.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/lang/Throwable"});
+ methodVisitor.visitInsn(POP);
+ Label label3 = new Label();
+ methodVisitor.visitLabel(label3);
+ methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+ methodVisitor.visitLdcInsn("Hello World!");
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
+ methodVisitor.visitInsn(RETURN);
+ methodVisitor.visitMaxs(2, 2);
+ methodVisitor.visitEnd();
+ }
+ {
+ methodVisitor =
+ classWriter.visitMethod(
+ ACC_PUBLIC | ACC_STATIC,
+ "getThrowable",
+ "(Ljava/lang/Throwable;)Ljava/lang/Throwable;",
+ null,
+ null);
+ methodVisitor.visitCode();
+ methodVisitor.visitMethodInsn(
+ INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
+ methodVisitor.visitInsn(LCONST_0);
+ methodVisitor.visitInsn(LCMP);
+ Label label0 = new Label();
+ methodVisitor.visitJumpInsn(IFLE, label0);
+ methodVisitor.visitTypeInsn(NEW, "java/lang/RuntimeException");
+ methodVisitor.visitInsn(DUP);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitMethodInsn(
+ INVOKESPECIAL,
+ "java/lang/RuntimeException",
+ "<init>",
+ "(Ljava/lang/Throwable;)V",
+ false);
+ methodVisitor.visitInsn(ARETURN);
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ methodVisitor.visitTypeInsn(NEW, "java/lang/ClassCastException");
+ methodVisitor.visitInsn(DUP);
+ methodVisitor.visitMethodInsn(
+ INVOKESPECIAL, "java/lang/ClassCastException", "<init>", "()V", false);
+ methodVisitor.visitInsn(ATHROW);
+ methodVisitor.visitMaxs(4, 1);
+ methodVisitor.visitEnd();
+ }
+ classWriter.visitEnd();
+
+ return classWriter.toByteArray();
+ }
+ }
+}
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 e190074..164259f 100644
--- a/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
+++ b/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
@@ -28,7 +28,6 @@
import java.util.Calendar;
import java.util.List;
import java.util.TreeSet;
-import java.util.function.Consumer;
public abstract class MethodGenerationBase extends TestBase {
@@ -75,18 +74,17 @@
}
// Running this method will regenerate / overwrite the content of the generated class.
- protected void generateMethodsAndWriteThemToFile(Consumer<InternalOptions> optionsConsumer)
- throws IOException {
- FileUtils.writeToFile(getGeneratedFile(), null, generateMethods(optionsConsumer).getBytes());
+ protected void generateMethodsAndWriteThemToFile() throws IOException {
+ FileUtils.writeToFile(getGeneratedFile(), null, generateMethods().getBytes());
}
// Running this method generate the content of the generated class but does not overwrite it.
- protected String generateMethods(Consumer<InternalOptions> optionsConsumer) throws IOException {
+ protected String generateMethods() throws IOException {
CfCodePrinter codePrinter = new CfCodePrinter();
File tempFile = File.createTempFile("output-", ".java");
- readMethodTemplatesInto(codePrinter, optionsConsumer);
+ readMethodTemplatesInto(codePrinter);
generateRawOutput(codePrinter, tempFile.toPath());
String result = formatRawOutput(tempFile.toPath());
@@ -94,10 +92,8 @@
return result;
}
- private void readMethodTemplatesInto(
- CfCodePrinter codePrinter, Consumer<InternalOptions> optionsConsumer) throws IOException {
+ private void readMethodTemplatesInto(CfCodePrinter codePrinter) throws IOException {
InternalOptions options = new InternalOptions();
- optionsConsumer.accept(options);
JarClassFileReader reader =
new JarClassFileReader(
new JarApplicationReader(options),
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java
new file mode 100644
index 0000000..316d547
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java
@@ -0,0 +1,115 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.readsInstanceField;
+import static com.android.tools.r8.utils.codeinspector.Matchers.writesInstanceField;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsNot.not;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.horizontalclassmerging.ClassMerger;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+
+public class ConstructorMergingOverlapTest extends HorizontalClassMergingTestBase {
+
+ public ConstructorMergingOverlapTest(
+ 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)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("42", "13", "7", "print a", "print b")
+ .inspect(
+ codeInspector -> {
+ if (enableHorizontalClassMerging) {
+ ClassSubject aClassSubject = codeInspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ FieldSubject classIdFieldSubject =
+ aClassSubject.uniqueFieldWithName(ClassMerger.CLASS_ID_FIELD_NAME);
+ assertThat(classIdFieldSubject, isPresent());
+
+ MethodSubject firstInitSubject = aClassSubject.init("int");
+ assertThat(firstInitSubject, isPresent());
+ assertThat(
+ firstInitSubject, writesInstanceField(classIdFieldSubject.getFieldReference()));
+
+ ClassSubject synthesizedClass = getSynthesizedArgumentClassSubject(codeInspector);
+
+ MethodSubject otherInitSubject =
+ aClassSubject.init("int", synthesizedClass.getFinalName());
+ assertThat(otherInitSubject, isPresent());
+ assertThat(
+ otherInitSubject, writesInstanceField(classIdFieldSubject.getFieldReference()));
+
+ MethodSubject printSubject = aClassSubject.method("void", "print");
+ assertThat(printSubject, isPresent());
+ assertThat(
+ printSubject, readsInstanceField(classIdFieldSubject.getFieldReference()));
+
+ assertThat(codeInspector.clazz(B.class), not(isPresent()));
+
+ // TODO(b/165517236): Explicitly check classes have been merged.
+ } else {
+ assertThat(codeInspector.clazz(A.class), isPresent());
+ assertThat(codeInspector.clazz(B.class), isPresent());
+ }
+ });
+ }
+
+ @NeverClassInline
+ public static class A {
+ public A() {
+ this(42);
+ }
+
+ public A(int x) {
+ System.out.println(x);
+ }
+
+ @NeverInline
+ public void print() {
+ System.out.println("print a");
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+ public B() {
+ System.out.println(7);
+ }
+
+ @NeverInline
+ public void print() {
+ System.out.println("print b");
+ }
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ A a = new A();
+ a = new A(13);
+ B b = new B();
+ a.print();
+ b.print();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
new file mode 100644
index 0000000..b7d3a2d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
@@ -0,0 +1,139 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.readsInstanceField;
+import static com.android.tools.r8.utils.codeinspector.Matchers.writesInstanceField;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsNot.not;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.horizontalclassmerging.ClassMerger;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+
+public class ConstructorMergingPreoptimizedTest extends HorizontalClassMergingTestBase {
+
+ public ConstructorMergingPreoptimizedTest(
+ 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)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(
+ "changed", "13", "42", "foo", "7", "foo", "print a", "print b")
+ .inspect(
+ codeInspector -> {
+ if (enableHorizontalClassMerging) {
+ ClassSubject changedClassSubject = codeInspector.clazz(Changed.class);
+ assertThat(changedClassSubject, isPresent());
+
+ ClassSubject aClassSubject = codeInspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ FieldSubject classIdFieldSubject =
+ aClassSubject.uniqueFieldWithName(ClassMerger.CLASS_ID_FIELD_NAME);
+ assertThat(classIdFieldSubject, isPresent());
+
+ MethodSubject firstInitSubject = aClassSubject.init("int");
+ assertThat(firstInitSubject, isPresent());
+ assertThat(
+ firstInitSubject, writesInstanceField(classIdFieldSubject.getFieldReference()));
+
+ MethodSubject otherInitSubject =
+ aClassSubject.init(changedClassSubject.getFinalName(), "int");
+ assertThat(otherInitSubject, isPresent());
+ assertThat(
+ otherInitSubject, writesInstanceField(classIdFieldSubject.getFieldReference()));
+
+ MethodSubject printSubject = aClassSubject.method("void", "print");
+ assertThat(printSubject, isPresent());
+ assertThat(
+ printSubject, readsInstanceField(classIdFieldSubject.getFieldReference()));
+
+ assertThat(codeInspector.clazz(B.class), not(isPresent()));
+
+ // TODO(b/165517236): Explicitly check classes have been merged.
+ } else {
+ assertThat(codeInspector.clazz(A.class), isPresent());
+ assertThat(codeInspector.clazz(B.class), isPresent());
+ }
+ });
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ public static class Parent {
+ @NeverInline
+ public void foo() {
+ System.out.println("foo");
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ public static class Changed extends Parent {
+ public Changed() {
+ System.out.println("changed");
+ }
+ }
+
+ @NeverClassInline
+ public static class A {
+ public A(Parent p) {
+ System.out.println(42);
+ p.foo();
+ }
+
+ public A(int x) {
+ System.out.println(x);
+ }
+
+ @NeverInline
+ public void print() {
+ System.out.println("print a");
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+ public B(Parent p) {
+ System.out.println(7);
+ p.foo();
+ }
+
+ @NeverInline
+ public void print() {
+ System.out.println("print b");
+ }
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ Parent p = new Changed();
+ A a = new A(13);
+ a = new A(p);
+ B b = new B(p);
+ a.print();
+ b.print();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingWithArgumentsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingWithArgumentsTest.java
index a1b2d1a..a4a0fd9 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingWithArgumentsTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingWithArgumentsTest.java
@@ -10,6 +10,8 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
import org.junit.Test;
public class ConstructorMergingWithArgumentsTest extends HorizontalClassMergingTestBase {
@@ -32,8 +34,13 @@
.inspect(
codeInspector -> {
if (enableHorizontalClassMerging) {
- assertThat(codeInspector.clazz(A.class), isPresent());
+ ClassSubject aClassSubject = codeInspector.clazz(A.class);
+
+ assertThat(aClassSubject, isPresent());
assertThat(codeInspector.clazz(B.class), not(isPresent()));
+
+ MethodSubject initSubject = aClassSubject.init(String.class.getName(), "int");
+ assertThat(initSubject, isPresent());
// TODO(b/165517236): Explicitly check classes have been merged.
} else {
assertThat(codeInspector.clazz(A.class), isPresent());
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingTestBase.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingTestBase.java
index 72e016b..d84c164 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingTestBase.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingTestBase.java
@@ -6,7 +6,10 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.horizontalclassmerging.SyntheticArgumentClass;
import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.util.List;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -27,4 +30,16 @@
return buildParameters(
getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
}
+
+ protected ClassSubject getSynthesizedArgumentClassSubject(CodeInspector codeInspector) {
+ return codeInspector.allClasses().stream()
+ .filter(
+ clazz ->
+ clazz.isSynthetic()
+ && clazz
+ .getOriginalName()
+ .endsWith(SyntheticArgumentClass.SYNTHETIC_CLASS_SUFFIX))
+ .findFirst()
+ .get();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PreventMergeMainDexListTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PreventMergeMainDexListTest.java
new file mode 100644
index 0000000..4a50200
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PreventMergeMainDexListTest.java
@@ -0,0 +1,104 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsNot.not;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.classmerging.horizontal.EmptyClassTest.A;
+import com.android.tools.r8.classmerging.horizontal.EmptyClassTest.B;
+import com.android.tools.r8.classmerging.horizontal.EmptyClassTest.Main;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class PreventMergeMainDexListTest extends HorizontalClassMergingTestBase {
+ public PreventMergeMainDexListTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Parameterized.Parameters(name = "{0}, horizontalClassMerging:{1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters()
+ .withDexRuntimes()
+ .withApiLevelsEndingAtExcluding(apiLevelWithNativeMultiDexSupport())
+ .build(),
+ BooleanUtils.values());
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepClassAndMembersRules(Main.class)
+ .addMainDexListClasses(A.class, Main.class)
+ .addOptionsModification(
+ options -> {
+ options.enableHorizontalClassMerging = enableHorizontalClassMerging;
+ options.minimalMainDex = true;
+ })
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .apply(this::checkCompileResult)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("main dex");
+ }
+
+ private void checkCompileResult(R8TestCompileResult compileResult) throws Exception {
+ Path out = temp.newFolder().toPath();
+ compileResult.app.writeToDirectory(out, OutputMode.DexIndexed);
+ Path classes = out.resolve("classes.dex");
+ Path classes2 = out.resolve("classes2.dex");
+ inspectMainDex(new CodeInspector(classes, compileResult.getProguardMap()));
+ inspectSecondaryDex(new CodeInspector(classes2, compileResult.getProguardMap()));
+ }
+
+ private void inspectMainDex(CodeInspector inspector) {
+ assertThat(inspector.clazz(A.class), isPresent());
+ assertThat(inspector.clazz(B.class), not(isPresent()));
+ }
+
+ private void inspectSecondaryDex(CodeInspector inspector) {
+ assertThat(inspector.clazz(A.class), not(isPresent()));
+ assertThat(inspector.clazz(B.class), isPresent());
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ A a = new A();
+ }
+
+ public static void otherDex() {
+ B b = new B();
+ }
+ }
+
+ @NeverClassInline
+ public static class A {
+ public A() {
+ System.out.println("main dex");
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+ public B() {
+ System.out.println("not main dex");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PreventMergeMainDexTracingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PreventMergeMainDexTracingTest.java
new file mode 100644
index 0000000..9e81bc6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PreventMergeMainDexTracingTest.java
@@ -0,0 +1,108 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsNot.not;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.classmerging.horizontal.PreventMergeMainDexListTest.Main;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class PreventMergeMainDexTracingTest extends HorizontalClassMergingTestBase {
+ public PreventMergeMainDexTracingTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Parameterized.Parameters(name = "{0}, horizontalClassMerging:{1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters()
+ .withDexRuntimes()
+ .withApiLevelsEndingAtExcluding(apiLevelWithNativeMultiDexSupport())
+ .build(),
+ BooleanUtils.values());
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addKeepClassAndMembersRules(Other.class)
+ .addMainDexClassRules(Main.class)
+ .addOptionsModification(
+ options -> {
+ options.enableHorizontalClassMerging = enableHorizontalClassMerging;
+ options.minimalMainDex = true;
+ })
+ .enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .apply(this::checkCompileResult)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("main dex");
+ }
+
+ private void checkCompileResult(R8TestCompileResult compileResult) throws Exception {
+ Path out = temp.newFolder().toPath();
+ compileResult.app.writeToDirectory(out, OutputMode.DexIndexed);
+ Path classes = out.resolve("classes.dex");
+ Path classes2 = out.resolve("classes2.dex");
+ inspectMainDex(new CodeInspector(classes, compileResult.getProguardMap()));
+ inspectSecondaryDex(new CodeInspector(classes2, compileResult.getProguardMap()));
+ }
+
+ private void inspectMainDex(CodeInspector inspector) {
+ assertThat(inspector.clazz(A.class), isPresent());
+ assertThat(inspector.clazz(B.class), not(isPresent()));
+ }
+
+ private void inspectSecondaryDex(CodeInspector inspector) {
+ assertThat(inspector.clazz(A.class), not(isPresent()));
+ assertThat(inspector.clazz(B.class), isPresent());
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ A a = new A();
+ }
+ }
+
+ @NoHorizontalClassMerging
+ public static class Other {
+ public static void otherDex() {
+ B b = new B();
+ }
+ }
+
+ @NeverClassInline
+ public static class A {
+ public A() {
+ System.out.println("main dex");
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+ public B() {
+ System.out.println("not main dex");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/SynchronizedClassesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/SynchronizedClassesTest.java
new file mode 100644
index 0000000..e5c77a3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/SynchronizedClassesTest.java
@@ -0,0 +1,103 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsNot.not;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import org.junit.Test;
+
+public class SynchronizedClassesTest extends HorizontalClassMergingTestBase {
+ public SynchronizedClassesTest(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;
+ })
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("foo", "b", "bar", "1", "true")
+ .inspect(
+ codeInspector -> {
+ assertThat(codeInspector.clazz(A.class), isPresent());
+ assertThat(codeInspector.clazz(B.class), isPresent());
+ if (enableHorizontalClassMerging) {
+ // C has been merged into A.
+ assertThat(codeInspector.clazz(C.class), not(isPresent()));
+ assertThat(codeInspector.clazz(A.class).init("long"), isPresent());
+
+ // D has been merged into B.
+ assertThat(codeInspector.clazz(D.class), not(isPresent()));
+ ClassSubject bClassSubject = codeInspector.clazz(B.class);
+ assertThat(bClassSubject.init("boolean"), isPresent());
+ } else {
+ assertThat(codeInspector.clazz(A.class), isPresent());
+ assertThat(codeInspector.clazz(B.class), isPresent());
+ }
+ });
+ }
+
+ @NeverClassInline
+ public static class A {
+ @NeverInline
+ static synchronized void foo() {
+ System.out.println("foo");
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+ public B(String foo) {
+ System.out.println(foo);
+ }
+
+ @NeverInline
+ void bar() {
+ synchronized (B.class) {
+ System.out.println("bar");
+ }
+ }
+ }
+
+ @NeverClassInline
+ public static class C {
+ public C(long v) {
+ System.out.println(v);
+ }
+ }
+
+ @NeverClassInline
+ public static class D {
+ public D(boolean v) {
+ System.out.println(v);
+ }
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ A a = new A();
+ A.foo();
+ B b = new B("b");
+ b.bar();
+ C c = new C(1);
+ D d = new D(true);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/code/PassThroughTest.java b/src/test/java/com/android/tools/r8/code/PassThroughTest.java
index 673f3a2..cb93c83 100644
--- a/src/test/java/com/android/tools/r8/code/PassThroughTest.java
+++ b/src/test/java/com/android/tools/r8/code/PassThroughTest.java
@@ -91,7 +91,6 @@
internalOptions -> {
internalOptions.testing.cfByteCodePassThrough =
method -> !method.name.toString().equals("<init>");
- internalOptions.testing.readInputStackMaps = true;
})
.compile()
.writeToZip(outputJar)
diff --git a/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java b/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java
index 20c7e0e..6a6e8d7 100644
--- a/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java
@@ -69,7 +69,7 @@
private Path toCompile;
private List<Path> classpath;
- public BasicTestDependenciesDesugaringTest(String name, String toCompile, String classpath) {
+ public BasicTestDependenciesDesugaringTest(String name, String toCompile, String classpath) {
this.name = name;
this.toCompile = Paths.get(toCompile);
this.classpath = Arrays.asList(classpath.split(CLASSPATH_SEPARATOR)).stream()
@@ -87,7 +87,10 @@
.addProgramFiles(toCompile)
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.K))
.setMinApiLevel(AndroidApiLevel.K.getLevel()),
- options -> options.interfaceMethodDesugaring = OffOrAuto.Auto);
+ options -> {
+ options.interfaceMethodDesugaring = OffOrAuto.Auto;
+ options.testing.disableStackMapVerification = name.equals("espresso-core-3.0.0.jar");
+ });
}
@Test
@@ -101,6 +104,9 @@
.addProgramFiles(toCompile)
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.K))
.setMinApiLevel(AndroidApiLevel.K.getLevel()),
- options -> options.interfaceMethodDesugaring = OffOrAuto.Off);
+ options -> {
+ options.interfaceMethodDesugaring = OffOrAuto.Off;
+ options.testing.disableStackMapVerification = name.equals("espresso-core-3.0.0.jar");
+ });
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
new file mode 100644
index 0000000..99b1316
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
@@ -0,0 +1,250 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.desugaredlibrary;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.L8Command;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.io.BufferedReader;
+import java.io.StringReader;
+import java.io.UncheckedIOException;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.Assume;
+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 BufferedReaderTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+
+ @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(),
+ getTestParameters()
+ .withAllRuntimes()
+ .withAllApiLevelsAlsoForCf()
+ .withApiLevel(AndroidApiLevel.N)
+ .build());
+ }
+
+ public BufferedReaderTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
+ private String expectedOutput() {
+ return StringUtils.lines(
+ "Hello",
+ "Larry",
+ "Page",
+ parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)
+ ? "Caught java.io.UncheckedIOException"
+ : "Caught j$.io.UncheckedIOException");
+ }
+
+ DesugaredLibraryConfiguration configurationWithBufferedReader(
+ InternalOptions options, boolean libraryCompilation, TestParameters parameters) {
+ // Parse the current configuration and amend the configuration for BufferedReader.lines. The
+ // configuration is the same for both program and library.
+ return new DesugaredLibraryConfigurationParser(
+ options.dexItemFactory(),
+ options.reporter,
+ libraryCompilation,
+ parameters.getApiLevel().getLevel())
+ .parse(
+ StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING),
+ builder -> {
+ if (parameters.getApiLevel().isLessThan(AndroidApiLevel.N)) {
+ builder.putRewritePrefix(
+ "java.io.DesugarBufferedReader", "j$.io.DesugarBufferedReader");
+ builder.putRewritePrefix(
+ "java.io.UncheckedIOException", "j$.io.UncheckedIOException");
+ builder.putRetargetCoreLibMember(
+ "java.io.BufferedReader#lines", "java.io.DesugarBufferedReader");
+ }
+ });
+ }
+
+ private void configurationForProgramCompilation(InternalOptions options) {
+ options.desugaredLibraryConfiguration =
+ configurationWithBufferedReader(options, false, parameters);
+ }
+
+ private void configurationForLibraryCompilation(InternalOptions options) {
+ options.desugaredLibraryConfiguration =
+ configurationWithBufferedReader(options, true, parameters);
+ }
+
+ @Test
+ public void testBufferedReaderD8Cf() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ // Use D8 to desugar with Java classfile output.
+ Path jar =
+ testForD8(Backend.CF)
+ .addOptionsModification(this::configurationForProgramCompilation)
+ .addInnerClasses(BufferedReaderTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ // .inspect(this::checkRewrittenInvokes)
+ .writeToZip();
+
+ if (parameters.getRuntime().isDex()) {
+ // Collection keep rules is only implemented in the DEX writer.
+ String desugaredLibraryKeepRules = keepRuleConsumer.get();
+ if (desugaredLibraryKeepRules != null) {
+ assertEquals(0, desugaredLibraryKeepRules.length());
+ desugaredLibraryKeepRules = "-keep class * { *; }";
+ }
+
+ // Convert to DEX without desugaring and run.
+ testForD8()
+ .addProgramFiles(jar)
+ .setMinApi(parameters.getApiLevel())
+ .disableDesugaring()
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ (apiLevel, keepRules, shrink) ->
+ buildDesugaredLibrary(
+ apiLevel,
+ keepRules,
+ shrink,
+ ImmutableList.of(),
+ this::configurationForLibraryCompilation),
+ parameters.getApiLevel(),
+ desugaredLibraryKeepRules,
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(expectedOutput());
+ } else {
+ // Build the desugared library in class file format.
+ Path desugaredLib = temp.newFolder().toPath().resolve("desugar_jdk_libs.jar");
+ L8Command.Builder l8Builder =
+ L8Command.builder()
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .addProgramFiles(ToolHelper.getDesugarJDKLibs())
+ .addProgramFiles(ToolHelper.DESUGAR_LIB_CONVERSIONS)
+ .setMode(CompilationMode.DEBUG)
+ .addDesugaredLibraryConfiguration(
+ StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
+ .setMinApiLevel(parameters.getApiLevel().getLevel())
+ .setOutput(desugaredLib, OutputMode.ClassFile);
+ ToolHelper.runL8(l8Builder.build(), this::configurationForLibraryCompilation);
+
+ // Run on the JVM with desuagred library on classpath.
+ testForJvm()
+ .addProgramFiles(jar)
+ .addRunClasspathFiles(desugaredLib)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(expectedOutput());
+ }
+ }
+
+ @Test
+ public void testBufferedReaderD8() throws Exception {
+ Assume.assumeTrue(parameters.getRuntime().isDex());
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForD8()
+ .addOptionsModification(
+ options ->
+ options.desugaredLibraryConfiguration =
+ configurationWithBufferedReader(options, false, parameters))
+ .addInnerClasses(BufferedReaderTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ (apiLevel, keepRules, shrink) ->
+ buildDesugaredLibrary(
+ apiLevel,
+ keepRules,
+ shrink,
+ ImmutableList.of(),
+ this::configurationForLibraryCompilation),
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(expectedOutput());
+ }
+
+ @Test
+ public void testBufferedReaderR8() throws Exception {
+ Assume.assumeTrue(parameters.getRuntime().isDex());
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForR8(parameters.getBackend())
+ .addOptionsModification(
+ options ->
+ options.desugaredLibraryConfiguration =
+ configurationWithBufferedReader(options, false, parameters))
+ .addInnerClasses(BufferedReaderTest.class)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .enableInliningAnnotations()
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ (apiLevel, keepRules, shrink) ->
+ buildDesugaredLibrary(
+ apiLevel,
+ keepRules,
+ shrink,
+ ImmutableList.of(),
+ this::configurationForLibraryCompilation),
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(expectedOutput());
+ }
+
+ static class TestClass {
+
+ @NeverInline
+ public static void testBufferedReaderLines() throws Exception {
+ try (BufferedReader reader = new BufferedReader(new StringReader("Hello\nLarry\nPage"))) {
+ reader.lines().forEach(System.out::println);
+ }
+ }
+
+ @NeverInline
+ public static void testBufferedReaderLines_uncheckedIoException() throws Exception {
+ BufferedReader reader = new BufferedReader(new StringReader(""));
+ reader.close();
+ try {
+ reader.lines().count();
+ System.out.println("UncheckedIOException expected");
+ } catch (UncheckedIOException expected) {
+ System.out.println("Caught " + expected.getClass().getName());
+ } catch (Throwable t) {
+ System.out.println("Caught unexpected" + t.getClass().getName());
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ testBufferedReaderLines();
+ testBufferedReaderLines_uncheckedIoException();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
index b68997a..e520016 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.file.Files;
@@ -29,6 +30,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.function.Consumer;
public class DesugaredLibraryTestBase extends TestBase {
@@ -69,7 +71,7 @@
}
protected Path buildDesugaredLibrary(AndroidApiLevel apiLevel, String keepRules, boolean shrink) {
- return buildDesugaredLibrary(apiLevel, keepRules, shrink, ImmutableList.of());
+ return buildDesugaredLibrary(apiLevel, keepRules, shrink, ImmutableList.of(), options -> {});
}
protected Path buildDesugaredLibrary(
@@ -77,6 +79,16 @@
String keepRules,
boolean shrink,
List<Path> additionalProgramFiles) {
+ return buildDesugaredLibrary(
+ apiLevel, keepRules, shrink, additionalProgramFiles, options -> {});
+ }
+
+ protected Path buildDesugaredLibrary(
+ AndroidApiLevel apiLevel,
+ String keepRules,
+ boolean shrink,
+ List<Path> additionalProgramFiles,
+ Consumer<InternalOptions> optionsModifier) {
// We wrap exceptions in a RuntimeException to call this from a lambda.
try {
// If we compile extended library here, it means we use TestNG.
@@ -108,6 +120,7 @@
options.testing.disableL8AnnotationRemoval = true;
options.testing.forceLibBackportsInL8CfToCf = true;
}
+ optionsModifier.accept(options);
});
if (!extraFiles) {
assertTrue(
@@ -188,6 +201,7 @@
stringBuilder.append(string);
}
+ @Override
public void finished(DiagnosticsHandler handler) {
assert stringBuilder != null;
assert result == null;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
index fa6e974..6db1591 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
@@ -8,19 +8,24 @@
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.L8Command;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.PrintUses;
import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestCompileResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRunResult;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ThrowingSupplier;
import com.android.tools.r8.utils.codeinspector.CheckCastInstructionSubject;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -29,11 +34,14 @@
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.android.tools.r8.utils.codeinspector.TryCatchSubject;
import com.android.tools.r8.utils.codeinspector.TypeSubject;
+import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableSet;
import java.nio.file.Path;
import java.util.List;
import java.util.Set;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
+import kotlin.text.Charsets;
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -45,6 +53,7 @@
private final TestParameters parameters;
private final boolean shrinkDesugaredLibrary;
+ private final boolean printUsesKeepRules;
private static final String expectedOutput =
StringUtils.lines(
"Caught java.time.format.DateTimeParseException",
@@ -54,10 +63,11 @@
"GMT",
"Hello, world");
- @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+ @Parameters(name = "{2}, shrinkDesugaredLibrary: {0}, printUsesKeepRules {1}")
public static List<Object[]> data() {
return buildParameters(
BooleanUtils.values(),
+ BooleanUtils.values(),
getTestParameters()
.withAllRuntimes()
.withAllApiLevelsAlsoForCf()
@@ -65,8 +75,10 @@
.build());
}
- public JavaTimeTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ public JavaTimeTest(
+ boolean shrinkDesugaredLibrary, boolean printUsesKeepRules, TestParameters parameters) {
this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.printUsesKeepRules = printUsesKeepRules;
this.parameters = parameters;
}
@@ -121,8 +133,66 @@
assertEquals(expectedCatchGuards, foundCatchGuards);
}
+ // Build the desugared library in class file format.
+ private Path buildDesugaredLibraryClassFile() throws Exception {
+ Path desugaredLib = temp.newFolder().toPath().resolve("desugar_jdk_libs.jar");
+ L8Command.Builder l8Builder =
+ L8Command.builder()
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .addProgramFiles(ToolHelper.getDesugarJDKLibs())
+ .addProgramFiles(ToolHelper.DESUGAR_LIB_CONVERSIONS)
+ .setMode(CompilationMode.DEBUG)
+ .addDesugaredLibraryConfiguration(
+ StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
+ .setMinApiLevel(parameters.getApiLevel().getLevel())
+ .setOutput(desugaredLib, OutputMode.ClassFile);
+ ToolHelper.runL8(l8Builder.build());
+ return desugaredLib;
+ }
+
+ Supplier<Path> desugaredLibraryClassFile =
+ Suppliers.memoize(
+ () -> {
+ try {
+ return buildDesugaredLibraryClassFile();
+ } catch (Exception e) {
+ fail("Unexpected");
+ return null;
+ }
+ });
+
+ private String collectKeepRulesWithPrintUses(
+ Path desugaredProgramClassFile, Path desugaredLibraryClassFile) throws Exception {
+ Path printUsesKeepRules = temp.newFile().toPath();
+ PrintUses.main(
+ "--keeprules",
+ ToolHelper.getAndroidJar(AndroidApiLevel.P).toString(),
+ desugaredLibraryClassFile.toString(),
+ desugaredProgramClassFile.toString(),
+ printUsesKeepRules.toString());
+ return FileUtils.readTextFile(printUsesKeepRules, Charsets.UTF_8);
+ }
+
+ private String desugaredLibraryKeepRules(
+ KeepRuleConsumer keepRuleConsumer, ThrowingSupplier<Path, Exception> programSupplier)
+ throws Exception {
+ String desugaredLibraryKeepRules = null;
+ if (shrinkDesugaredLibrary) {
+ desugaredLibraryKeepRules = keepRuleConsumer.get();
+ if (desugaredLibraryKeepRules != null) {
+ if (printUsesKeepRules) {
+ desugaredLibraryKeepRules =
+ collectKeepRulesWithPrintUses(programSupplier.get(), desugaredLibraryClassFile.get());
+ }
+ }
+ }
+ return desugaredLibraryKeepRules;
+ }
+
@Test
public void testTimeD8Cf() throws Exception {
+ Assume.assumeTrue(shrinkDesugaredLibrary || !printUsesKeepRules);
+
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
// Use D8 to desugar with Java classfile output.
Path jar =
@@ -134,14 +204,17 @@
.inspect(this::checkRewrittenInvokes)
.writeToZip();
- if (parameters.getRuntime().isDex()) {
+ String desugaredLibraryKeepRules;
+ if (shrinkDesugaredLibrary && !printUsesKeepRules && keepRuleConsumer.get() != null) {
// Collection keep rules is only implemented in the DEX writer.
- String desugaredLibraryKeepRules = keepRuleConsumer.get();
- if (desugaredLibraryKeepRules != null) {
- assertEquals(0, desugaredLibraryKeepRules.length());
- desugaredLibraryKeepRules = "-keep class * { *; }";
- }
+ assertEquals(0, keepRuleConsumer.get().length());
+ desugaredLibraryKeepRules = "-keep class * { *; }";
+ } else {
+ desugaredLibraryKeepRules = desugaredLibraryKeepRules(keepRuleConsumer, () -> jar);
+ }
+ // Determine desugared library keep rules.
+ if (parameters.getRuntime().isDex()) {
// Convert to DEX without desugaring and run.
testForD8()
.addProgramFiles(jar)
@@ -156,25 +229,11 @@
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(expectedOutput);
} else {
- // Build the desugared library in class file format.
- Path desugaredLib = temp.newFolder().toPath().resolve("desugar_jdk_libs.jar");
- L8Command.Builder l8Builder =
- L8Command.builder()
- .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
- .addProgramFiles(ToolHelper.getDesugarJDKLibs())
- .addProgramFiles(ToolHelper.DESUGAR_LIB_CONVERSIONS)
- .setMode(CompilationMode.DEBUG)
- .addDesugaredLibraryConfiguration(
- StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
- .setMinApiLevel(parameters.getApiLevel().getLevel())
- .setOutput(desugaredLib, OutputMode.ClassFile);
- ToolHelper.runL8(l8Builder.build());
-
- // Run on the JVM with desuagred library on classpath.
+ // Run on the JVM with desugared library on classpath.
TestRunResult<?> result =
testForJvm()
.addProgramFiles(jar)
- .addRunClasspathFiles(desugaredLib)
+ .addRunClasspathFiles(desugaredLibraryClassFile.get())
.run(parameters.getRuntime(), TestClass.class);
if (parameters.getApiLevel().isGreaterThan(AndroidApiLevel.N_MR1)) {
// java.time is present from O, so the desugared library classes are not loaded.
@@ -190,17 +249,21 @@
@Test
public void testTimeD8() throws Exception {
Assume.assumeTrue(parameters.getRuntime().isDex());
+ Assume.assumeTrue(shrinkDesugaredLibrary || !printUsesKeepRules);
+
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
- testForD8()
- .addInnerClasses(JavaTimeTest.class)
- .setMinApi(parameters.getApiLevel())
- .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
- .compile()
- .inspect(this::checkRewrittenInvokes)
+ TestCompileResult<?, ?> result =
+ testForD8()
+ .addInnerClasses(JavaTimeTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .inspect(this::checkRewrittenInvokes);
+ result
.addDesugaredCoreLibraryRunClassPath(
this::buildDesugaredLibrary,
parameters.getApiLevel(),
- keepRuleConsumer.get(),
+ desugaredLibraryKeepRules(keepRuleConsumer, result::writeToZip),
shrinkDesugaredLibrary)
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(expectedOutput);
@@ -209,19 +272,23 @@
@Test
public void testTimeR8() throws Exception {
Assume.assumeTrue(parameters.getRuntime().isDex());
+ Assume.assumeTrue(shrinkDesugaredLibrary || !printUsesKeepRules);
+
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
- testForR8(parameters.getBackend())
- .addInnerClasses(JavaTimeTest.class)
- .addKeepMainRule(TestClass.class)
- .setMinApi(parameters.getApiLevel())
- .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
- .enableInliningAnnotations()
- .compile()
- .inspect(this::checkRewrittenInvokes)
+ TestCompileResult<?, ?> result =
+ testForR8(parameters.getBackend())
+ .addInnerClasses(JavaTimeTest.class)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .enableInliningAnnotations()
+ .compile()
+ .inspect(this::checkRewrittenInvokes);
+ result
.addDesugaredCoreLibraryRunClassPath(
this::buildDesugaredLibrary,
parameters.getApiLevel(),
- keepRuleConsumer.get(),
+ desugaredLibraryKeepRules(keepRuleConsumer, result::writeToZip),
shrinkDesugaredLibrary)
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(expectedOutput);
diff --git a/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java b/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
index 71d5ffb..c72cf47 100644
--- a/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
+++ b/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
@@ -20,6 +20,7 @@
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexCode.Try;
+import com.android.tools.r8.graph.DexCode.TryHandler;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexString;
@@ -150,14 +151,7 @@
private DexCode jumboStringProcess(
DexItemFactory factory, DexString string, Instruction[] instructions) {
- DexCode code = new DexCode(
- 1,
- 0,
- 0,
- instructions,
- new Try[0],
- null,
- null);
+ DexCode code = new DexCode(1, 0, 0, instructions, new Try[0], new TryHandler[0], null);
MethodAccessFlags flags = MethodAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC, false);
DexEncodedMethod method =
new DexEncodedMethod(
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/GenerateEnumUnboxingMethods.java b/src/test/java/com/android/tools/r8/enumunboxing/GenerateEnumUnboxingMethods.java
index 4abe533..54a405c 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/GenerateEnumUnboxingMethods.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/GenerateEnumUnboxingMethods.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.cfmethodgeneration.MethodGenerationBase;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableList;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
@@ -51,9 +50,6 @@
return METHOD_TEMPLATE_CLASSES;
}
- private static void setReadInputStackMap(InternalOptions options) {
- options.testing.readInputStackMaps = true;
- }
@Test
public void testEnumUtilityMethodsGenerated() throws Exception {
@@ -61,12 +57,10 @@
sorted.sort(Comparator.comparing(Class::getTypeName));
assertEquals("Classes should be listed in sorted order", sorted, getMethodTemplateClasses());
assertEquals(
- FileUtils.readTextFile(getGeneratedFile(), StandardCharsets.UTF_8),
- generateMethods(GenerateEnumUnboxingMethods::setReadInputStackMap));
+ FileUtils.readTextFile(getGeneratedFile(), StandardCharsets.UTF_8), generateMethods());
}
public static void main(String[] args) throws Exception {
- new GenerateEnumUnboxingMethods(null)
- .generateMethodsAndWriteThemToFile(GenerateEnumUnboxingMethods::setReadInputStackMap);
+ new GenerateEnumUnboxingMethods(null).generateMethodsAndWriteThemToFile();
}
}
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeProguardJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeProguardJarVerificationTest.java
index 7c02878..5092831 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeProguardJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeProguardJarVerificationTest.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.internal;
import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.google.common.collect.ImmutableList;
import org.junit.Test;
public class YouTubeProguardJarVerificationTest extends YouTubeCompilationBase {
@@ -14,11 +16,23 @@
@Test
public void buildDebugFromProguardJar() throws Exception {
- runR8AndCheckVerification(CompilationMode.DEBUG, PG_JAR);
+ runAndCheckVerification(
+ CompilerUnderTest.R8,
+ CompilationMode.DEBUG,
+ base + APK,
+ null,
+ options -> options.testing.disableStackMapVerification = true,
+ ImmutableList.of(base + PG_JAR));
}
@Test
public void buildReleaseFromProguardJar() throws Exception {
- runR8AndCheckVerification(CompilationMode.RELEASE, PG_JAR);
+ runAndCheckVerification(
+ CompilerUnderTest.R8,
+ CompilationMode.RELEASE,
+ base + APK,
+ null,
+ options -> options.testing.disableStackMapVerification = true,
+ ImmutableList.of(base + PG_JAR));
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
index cd417d1..05c11c4 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.cfmethodgeneration.MethodGenerationBase;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableList;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
@@ -69,22 +68,16 @@
return METHOD_TEMPLATE_CLASSES;
}
- private static void setReadInputStackMap(InternalOptions options) {
- options.testing.readInputStackMaps = true;
- }
-
@Test
public void testBackportsGenerated() throws Exception {
ArrayList<Class<?>> sorted = new ArrayList<>(getMethodTemplateClasses());
sorted.sort(Comparator.comparing(Class::getTypeName));
assertEquals("Classes should be listed in sorted order", sorted, getMethodTemplateClasses());
assertEquals(
- FileUtils.readTextFile(getGeneratedFile(), StandardCharsets.UTF_8),
- generateMethods(GenerateBackportMethods::setReadInputStackMap));
+ FileUtils.readTextFile(getGeneratedFile(), StandardCharsets.UTF_8), generateMethods());
}
public static void main(String[] args) throws Exception {
- new GenerateBackportMethods(null)
- .generateMethodsAndWriteThemToFile(GenerateBackportMethods::setReadInputStackMap);
+ new GenerateBackportMethods(null).generateMethodsAndWriteThemToFile();
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/B148366506.java b/src/test/java/com/android/tools/r8/ir/optimize/B148366506.java
index e89e6cd..1fa9b16 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/B148366506.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/B148366506.java
@@ -49,7 +49,7 @@
MethodVisitor methodVisitor;
classWriter.visit(
- V1_7,
+ V1_6,
ACC_PUBLIC | ACC_SUPER,
"d/b/c/e/e/a/b/a",
null,
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/B146957343.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/B146957343.java
index 3940548..4ca57c1 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/B146957343.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/B146957343.java
@@ -52,7 +52,6 @@
.addOptionsModification(
options -> options.enableUninstantiatedTypeOptimizationForInterfaces = true)
.compile()
- .disassemble()
.run(parameters.getRuntime(), Main.class)
.assertFailureWithErrorThatThrows(NullPointerException.class);
}
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
index 74d62e0..23c4628 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
@@ -8,12 +8,14 @@
import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestBase;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -453,7 +455,16 @@
file.readJasmin(new StringReader(builder.toString()), builder.name, false);
ByteArrayOutputStream out = new ByteArrayOutputStream();
file.write(out);
- return out.toByteArray();
+ // Jasmin incorrectly sets super on interfaces: https://sourceforge.net/p/jasmin/bugs/5/
+ return TestBase.transformer(
+ out.toByteArray(), Reference.classFromBinaryName(file.getClassName()))
+ .setAccessFlags(
+ flags -> {
+ if (flags.isInterface()) {
+ flags.unsetSuper();
+ }
+ })
+ .transform();
}
public ImmutableList.Builder<byte[]> buildClasses(ImmutableList.Builder<byte[]> builder)
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java
index 3b35a09..73b42ed 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.kotlin.lambda.b159688129;
import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
@@ -69,7 +70,7 @@
.assertSuccessWithOutputLines("3")
.inspect(
codeInspector -> {
- final List<FoundClassSubject> lambdaGroups =
+ List<FoundClassSubject> lambdaGroups =
codeInspector.allClasses().stream()
.filter(c -> c.getFinalName().contains("LambdaGroup"))
.collect(Collectors.toList());
@@ -79,6 +80,7 @@
ProcessResult processResult =
ToolHelper.runDex2OatRaw(path, oatFile, parameters.getRuntime().asDex().getVm());
assertEquals(0, processResult.exitCode);
- assertThat(processResult.stderr, containsString("Method exceeds compiler instruction limit"));
+ assertThat(
+ processResult.stderr, not(containsString("Method exceeds compiler instruction limit")));
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java
new file mode 100644
index 0000000..3926dea
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java
@@ -0,0 +1,96 @@
+// 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.kotlin.lambda.b159688129;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.stream.Collectors;
+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 LambdaSplitByCodeCorrectnessTest extends AbstractR8KotlinTestBase {
+
+ private final TestParameters parameters;
+ private final KotlinTargetVersion targetVersion;
+ private final boolean splitGroup;
+
+ @Parameters(name = "{0}, targetVersion: {1}, splitGroup: {2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withDexRuntimes().withAllApiLevels().build(),
+ KotlinTargetVersion.values(),
+ BooleanUtils.values());
+ }
+
+ public LambdaSplitByCodeCorrectnessTest(
+ TestParameters parameters, KotlinTargetVersion targetVersion, boolean splitGroup) {
+ super(targetVersion);
+ this.parameters = parameters;
+ this.targetVersion = targetVersion;
+ this.splitGroup = splitGroup;
+ }
+
+ @Test
+ public void testSplitLambdaGroups() throws Exception {
+ String PKG_NAME = LambdaSplitByCodeCorrectnessTest.class.getPackage().getName();
+ String folder = DescriptorUtils.getBinaryNameFromJavaType(PKG_NAME);
+ CfRuntime cfRuntime =
+ parameters.isCfRuntime() ? parameters.getRuntime().asCf() : TestRuntime.getCheckedInJdk9();
+ Path ktClasses =
+ kotlinc(cfRuntime, KOTLINC, targetVersion)
+ .addSourceFiles(getKotlinFileInTest(folder, "Simple"))
+ .compile();
+ testForR8(parameters.getBackend())
+ .addProgramFiles(ToolHelper.getKotlinStdlibJar())
+ .addProgramFiles(ktClasses)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(PKG_NAME + ".SimpleKt")
+ .applyIf(
+ splitGroup,
+ b ->
+ b.addOptionsModification(
+ internalOptions ->
+ // Setting verificationSizeLimitInBytesOverride = 1 will force a a chain
+ // having
+ // only a single implementation method in each.
+ internalOptions.testing.verificationSizeLimitInBytesOverride =
+ splitGroup ? 1 : -1))
+ .noMinification()
+ .allowDiagnosticWarningMessages()
+ .compile()
+ .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
+ .inspect(
+ codeInspector -> {
+ List<FoundClassSubject> lambdaGroups =
+ codeInspector.allClasses().stream()
+ .filter(c -> c.getFinalName().contains("LambdaGroup"))
+ .collect(Collectors.toList());
+ assertEquals(1, lambdaGroups.size());
+ FoundClassSubject lambdaGroup = lambdaGroups.get(0);
+ List<FoundMethodSubject> invokeChain =
+ lambdaGroup.allMethods(method -> method.getFinalName().contains("invoke$"));
+ assertEquals(splitGroup ? 5 : 0, invokeChain.size());
+ })
+ .run(parameters.getRuntime(), PKG_NAME + ".SimpleKt")
+ .assertSuccessWithOutputLines("Hello1", "Hello2", "Hello3", "Hello4", "Hello5", "Hello6");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/Main.kt b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/Main.kt
index 0bbca3c..eed8d91 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/Main.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/Main.kt
@@ -4,13 +4,10 @@
package com.android.tools.r8.kotlin.lambda.b159688129
-import com.android.tools.r8.NeverInline
-
fun main() {
run ({ arg -> println(arg)}, 3)
}
-@NeverInline
fun run(param: Function1<Int, Unit>, arg : Int) {
param.invoke(arg)
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/MainKtDump.java b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/MainKtDump.java
index 7321fcb..66c64b2 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/MainKtDump.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/MainKtDump.java
@@ -112,8 +112,6 @@
"(Lkotlin/jvm/functions/Function1<-Ljava/lang/Integer;Lkotlin/Unit;>;I)V",
null);
{
- annotationVisitor0 =
- methodVisitor.visitAnnotation("Lcom/android/tools/r8/NeverInline;", true);
annotationVisitor0.visitEnd();
}
methodVisitor.visitAnnotableParameterCount(2, false);
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/Simple.kt b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/Simple.kt
new file mode 100644
index 0000000..3fe6e9e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/Simple.kt
@@ -0,0 +1,18 @@
+// 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.kotlin.lambda.b159688129
+
+fun main() {
+ runSimple { println("Hello1")}
+ runSimple { println("Hello2")}
+ runSimple { println("Hello3")}
+ runSimple { println("Hello4")}
+ runSimple { println("Hello5")}
+ runSimple { println("Hello6")}
+}
+
+fun runSimple(cb: () -> Unit) {
+ cb()
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/regress/b111960171/B111960171.java b/src/test/java/com/android/tools/r8/regress/b111960171/B111960171.java
index 21d088f..d05520f 100644
--- a/src/test/java/com/android/tools/r8/regress/b111960171/B111960171.java
+++ b/src/test/java/com/android/tools/r8/regress/b111960171/B111960171.java
@@ -65,13 +65,13 @@
public void disableDex2OatInliningWithTryCatch()
throws IOException, CompilationFailedException, ExecutionException {
MethodSubject method = compileTestClassAndGetMethod(AndroidApiLevel.M.getLevel());
- assertTrue(method.getMethod().getCode().asDexCode().handlers != null);
+ assertTrue(method.getMethod().getCode().asDexCode().handlers.length > 0);
}
@Test
public void dontDisableDex2OatInliningWithTryCatch()
throws IOException, CompilationFailedException, ExecutionException {
MethodSubject method = compileTestClassAndGetMethod(AndroidApiLevel.N.getLevel());
- assertTrue(method.getMethod().getCode().asDexCode().handlers == null);
+ assertTrue(method.getMethod().getCode().asDexCode().handlers.length == 0);
}
}
diff --git a/src/test/java/com/android/tools/r8/regress/b113347830/B113347830.java b/src/test/java/com/android/tools/r8/regress/b113347830/B113347830.java
index a62622e..397f703 100644
--- a/src/test/java/com/android/tools/r8/regress/b113347830/B113347830.java
+++ b/src/test/java/com/android/tools/r8/regress/b113347830/B113347830.java
@@ -3,21 +3,35 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.regress.b113347830;
-import com.android.tools.r8.D8;
-import com.android.tools.r8.D8Command;
import com.android.tools.r8.DexIndexedConsumer;
+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.origin.Origin;
import jasmin.ClassFile;
import java.io.ByteArrayOutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
-public class B113347830 {
+@RunWith(Parameterized.class)
+public class B113347830 extends TestBase {
public static final Class CLASS = B113347830.class;
public static final String NAME = CLASS.getSimpleName();
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public B113347830(TestParameters parameters) {
+ this.parameters = parameters;
+ }
@Test
public void test() throws Exception {
@@ -32,11 +46,11 @@
jasminFile.write(out);
byte[] bytes = out.toByteArray();
- D8.run(
- D8Command.builder()
- .addClassProgramData(bytes, Origin.unknown())
- .setDisableDesugaring(true)
- .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
- .build());
+ testForD8(Backend.DEX)
+ .addProgramClassFileData(bytes)
+ .setDisableDesugaring(true)
+ .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+ .addOptionsModification(options -> options.testing.disableStackMapVerification = true)
+ .compile();
}
}
diff --git a/src/test/java/com/android/tools/r8/regress/b77842465/Regress77842465Dump.java b/src/test/java/com/android/tools/r8/regress/b77842465/Regress77842465Dump.java
index 7a5aaea..51af1ed 100644
--- a/src/test/java/com/android/tools/r8/regress/b77842465/Regress77842465Dump.java
+++ b/src/test/java/com/android/tools/r8/regress/b77842465/Regress77842465Dump.java
@@ -23,7 +23,7 @@
MethodVisitor mv;
cw.visit(
- V1_7, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, CLASS_INTERNAL, null, "java/lang/Object", null);
+ V1_6, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, CLASS_INTERNAL, null, "java/lang/Object", null);
{
fv = cw.visitField(0, "b", "I", null, null);
diff --git a/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java b/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java
index 0afec27..a2ca851 100644
--- a/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java
+++ b/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java
@@ -65,6 +65,7 @@
.addProgramClasses(CLASSES)
.addProgramClassFileData(CLASS_BYTES)
.setMinApi(parameters.getApiLevel())
+ .addOptionsModification(options -> options.testing.readInputStackMaps = false)
.run(parameters.getRuntime(), MAIN);
checkResult(result);
}
@@ -88,6 +89,7 @@
.treeShaking(treeShake)
.noMinification()
.setMinApi(parameters.getApiLevel())
+ .addOptionsModification(options -> options.testing.readInputStackMaps = false)
.addKeepMainRule(MAIN)
.run(parameters.getRuntime(), MAIN);
checkResult(result);
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageAfterCollisionWithPackagePrivateSignatureTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageAfterCollisionWithPackagePrivateSignatureTest.java
index 8e08452..c7060f0 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageAfterCollisionWithPackagePrivateSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageAfterCollisionWithPackagePrivateSignatureTest.java
@@ -4,44 +4,21 @@
package com.android.tools.r8.repackage;
-import static com.android.tools.r8.shaking.ProguardConfigurationParser.FLATTEN_PACKAGE_HIERARCHY;
-import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.repackage.testclasses.repackagetest.TestClass;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.google.common.collect.ImmutableList;
-import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class RepackageAfterCollisionWithPackagePrivateSignatureTest extends TestBase {
-
- private static final String REPACKAGE_DIR = "foo";
-
- private final String flattenPackageHierarchyOrRepackageClasses;
- private final TestParameters parameters;
-
- @Parameters(name = "{1}, kind: {0}")
- public static List<Object[]> data() {
- return buildParameters(
- ImmutableList.of(FLATTEN_PACKAGE_HIERARCHY, REPACKAGE_CLASSES),
- getTestParameters().withAllRuntimesAndApiLevels().build());
- }
+public class RepackageAfterCollisionWithPackagePrivateSignatureTest extends RepackageTestBase {
public RepackageAfterCollisionWithPackagePrivateSignatureTest(
String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
- this.flattenPackageHierarchyOrRepackageClasses = flattenPackageHierarchyOrRepackageClasses;
- this.parameters = parameters;
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
}
@Test
@@ -49,10 +26,8 @@
testForR8(parameters.getBackend())
.addInnerClasses(RepackageAfterCollisionWithPackagePrivateSignatureTest.class)
.addKeepClassAndMembersRules(TestClass.class)
- .addKeepRules(
- "-" + flattenPackageHierarchyOrRepackageClasses + " \"" + REPACKAGE_DIR + "\"")
.addClassObfuscationDictionary("a")
- .addOptionsModification(options -> options.testing.enableExperimentalRepackaging = true)
+ .apply(this::configureRepackaging)
.enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
@@ -62,13 +37,7 @@
}
private void inspect(CodeInspector inspector) {
- ClassSubject repackageClassSubject = inspector.clazz(RepackageCandidate.class);
- assertThat(repackageClassSubject, isPresent());
- assertEquals(
- flattenPackageHierarchyOrRepackageClasses.equals(FLATTEN_PACKAGE_HIERARCHY)
- ? REPACKAGE_DIR + ".a"
- : REPACKAGE_DIR,
- repackageClassSubject.getDexProgramClass().getType().getPackageName());
+ assertThat(RepackageCandidate.class, isRepackaged(inspector));
}
public static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageTest.java
index 1d9718b..2f6574b 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageTest.java
@@ -4,13 +4,12 @@
package com.android.tools.r8.repackage;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.FLATTEN_PACKAGE_HIERARCHY;
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
-import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.repackage.testclasses.repackagetest.AccessPackagePrivateKeptMethodAllowRenamingOnReachableClassDirect;
@@ -34,7 +33,6 @@
import com.android.tools.r8.repackage.testclasses.repackagetest.ReachableClassWithKeptMethodAllowRenaming;
import com.android.tools.r8.repackage.testclasses.repackagetest.TestClass;
import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
import java.util.List;
@@ -46,11 +44,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class RepackageTest extends TestBase {
-
- private static final String FLATTEN_PACKAGE_HIERARCHY = "flattenpackagehierarchy";
- private static final String REPACKAGE_CLASSES = "repackageclasses";
- private static final String REPACKAGE_DIR = "foo";
+public class RepackageTest extends RepackageTestBase {
private static final List<String> EXPECTED =
ImmutableList.of(
@@ -71,9 +65,6 @@
"ReachableClass.packagePrivateMethod()");
private final boolean allowAccessModification;
- private final boolean enableExperimentalRepackaging;
- private final String flattenPackageHierarchyOrRepackageClasses;
- private final TestParameters parameters;
@Parameters(name = "{3}, allow access modification: {0}, experimental: {1}, kind: {2}")
public static List<Object[]> data() {
@@ -89,17 +80,15 @@
boolean enableExperimentalRepackaging,
String flattenPackageHierarchyOrRepackageClasses,
TestParameters parameters) {
+ super(enableExperimentalRepackaging, flattenPackageHierarchyOrRepackageClasses, parameters);
this.allowAccessModification = allowAccessModification;
- this.enableExperimentalRepackaging = enableExperimentalRepackaging;
- this.flattenPackageHierarchyOrRepackageClasses = flattenPackageHierarchyOrRepackageClasses;
- this.parameters = parameters;
}
@Test
public void testJvm() throws Exception {
assumeFalse(allowAccessModification);
- assumeFalse(enableExperimentalRepackaging);
- assumeTrue(flattenPackageHierarchyOrRepackageClasses.equals(FLATTEN_PACKAGE_HIERARCHY));
+ assumeFalse(isExperimentalRepackaging());
+ assumeTrue(isFlattenPackageHierarchy());
assumeTrue(parameters.isCfRuntime());
testForJvm()
.addTestClasspath()
@@ -113,7 +102,6 @@
.addProgramFiles(ToolHelper.getClassFilesForTestPackage(TestClass.class.getPackage()))
.addKeepMainRule(TestClass.class)
.addKeepRules(
- "-" + flattenPackageHierarchyOrRepackageClasses + " \"" + REPACKAGE_DIR + "\"",
"-keep class " + KeptClass.class.getTypeName(),
"-keep,allowobfuscation class " + KeptClassAllowRenaming.class.getTypeName(),
"-keepclassmembers class " + ReachableClassWithKeptMethod.class.getTypeName() + " {",
@@ -125,9 +113,7 @@
" <methods>;",
"}")
.allowAccessModification(allowAccessModification)
- .addOptionsModification(
- options ->
- options.testing.enableExperimentalRepackaging = enableExperimentalRepackaging)
+ .apply(this::configureRepackaging)
.enableInliningAnnotations()
.enableNoStaticClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
@@ -139,23 +125,8 @@
private void inspect(CodeInspector inspector) {
forEachClass(
- (clazz, eligibleForRepackaging) -> {
- ClassSubject subject = inspector.clazz(clazz);
- assertThat(subject, isPresent());
- if (eligibleForRepackaging) {
- assertEquals(
- clazz.getTypeName(),
- flattenPackageHierarchyOrRepackageClasses.equals(FLATTEN_PACKAGE_HIERARCHY)
- ? REPACKAGE_DIR + ".a"
- : REPACKAGE_DIR,
- subject.getDexProgramClass().getType().getPackageName());
- } else {
- assertEquals(
- clazz.getTypeName(),
- RepackageTest.class.getPackage().getName() + ".testclasses.repackagetest",
- subject.getDexProgramClass().getType().getPackageName());
- }
- });
+ (clazz, eligibleForRepackaging) ->
+ assertThat(clazz, isRepackagedIf(inspector, eligibleForRepackaging)));
}
/**
@@ -167,7 +138,7 @@
// the consumer, since these classes should be repackaged independent of
// -allowaccessmodification.
Consumer<Class<?>> markShouldAlwaysBeEligible =
- clazz -> consumer.accept(clazz, allowAccessModification || enableExperimentalRepackaging);
+ clazz -> consumer.accept(clazz, allowAccessModification || isExperimentalRepackaging());
Consumer<Class<?>> markEligibleWithAllowAccessModification =
clazz -> consumer.accept(clazz, allowAccessModification);
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageTestBase.java b/src/test/java/com/android/tools/r8/repackage/RepackageTestBase.java
new file mode 100644
index 0000000..8eb3d77
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageTestBase.java
@@ -0,0 +1,156 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.repackage;
+
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.FLATTEN_PACKAGE_HIERARCHY;
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
+import static org.junit.Assert.assertFalse;
+
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.List;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.runners.Parameterized.Parameters;
+
+public abstract class RepackageTestBase extends TestBase {
+
+ private final boolean enableExperimentalRepackaging;
+ private final String flattenPackageHierarchyOrRepackageClasses;
+ protected final TestParameters parameters;
+
+ @Parameters(name = "{1}, kind: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ ImmutableList.of(FLATTEN_PACKAGE_HIERARCHY, REPACKAGE_CLASSES),
+ getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ public RepackageTestBase(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ this(true, flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ public RepackageTestBase(
+ boolean enableExperimentalRepackaging,
+ String flattenPackageHierarchyOrRepackageClasses,
+ TestParameters parameters) {
+ this.enableExperimentalRepackaging = enableExperimentalRepackaging;
+ this.flattenPackageHierarchyOrRepackageClasses = flattenPackageHierarchyOrRepackageClasses;
+ this.parameters = parameters;
+ }
+
+ protected String getRepackagePackage() {
+ return "foo";
+ }
+
+ protected Matcher<Class<?>> isRepackaged(CodeInspector inspector) {
+ return isRepackagedAsExpected(inspector, null, true);
+ }
+
+ protected Matcher<Class<?>> isRepackagedIf(
+ CodeInspector inspector, boolean eligibleForRepackaging) {
+ return isRepackagedAsExpected(inspector, null, eligibleForRepackaging);
+ }
+
+ /**
+ * Checks that the class of interest is repackaged as expected.
+ *
+ * <p>If building with -repackageclasses, it is checked that the given class of interest is
+ * repackaged into "foo" (unless getRepackagePackage() is overridden). In this case, {@param
+ * packageName} is unused.
+ *
+ * <p>If building with -flattenpackagehierarchy, it is checked that the given class is repackaged
+ * into "foo.<packageName>".
+ */
+ protected Matcher<Class<?>> isRepackagedAsExpected(CodeInspector inspector, String packageName) {
+ return isRepackagedAsExpected(inspector, packageName, true);
+ }
+
+ private Matcher<Class<?>> isRepackagedAsExpected(
+ CodeInspector inspector, String packageName, boolean eligibleForRepackaging) {
+ return new TypeSafeMatcher<Class<?>>() {
+ @Override
+ public boolean matchesSafely(Class<?> clazz) {
+ ClassSubject classSubject = inspector.clazz(clazz);
+ if (!classSubject.isPresent()) {
+ return false;
+ }
+ return getActualPackage(classSubject).equals(getExpectedPackage(clazz));
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ if (eligibleForRepackaging) {
+ description.appendText(
+ "class to be repackaged to '" + getExpectedPackageForEligibleClass() + "'");
+ } else {
+ description.appendText("class to be ineligible for repackaging");
+ }
+ }
+
+ @Override
+ public void describeMismatchSafely(Class<?> clazz, Description description) {
+ ClassSubject classSubject = inspector.clazz(clazz);
+ if (classSubject.isPresent()) {
+ description
+ .appendText("class ")
+ .appendValue(clazz.getTypeName())
+ .appendText(" was not (actual: '" + getActualPackage(classSubject) + "')");
+ } else {
+ description
+ .appendText("class ")
+ .appendValue(clazz.getTypeName())
+ .appendText(" was absent");
+ }
+ }
+
+ private String getActualPackage(ClassSubject classSubject) {
+ return classSubject.getDexProgramClass().getType().getPackageName();
+ }
+
+ private String getExpectedPackage(Class<?> clazz) {
+ return eligibleForRepackaging
+ ? getExpectedPackageForEligibleClass()
+ : clazz.getPackage().getName();
+ }
+
+ private String getExpectedPackageForEligibleClass() {
+ List<String> expectedPackageNames = new ArrayList<>();
+ expectedPackageNames.add(getRepackagePackage());
+ if (isFlattenPackageHierarchy()) {
+ expectedPackageNames.add(packageName != null ? packageName : "a");
+ }
+ return StringUtils.join(expectedPackageNames, ".");
+ }
+ };
+ }
+
+ protected void configureRepackaging(R8FullTestBuilder testBuilder) {
+ testBuilder
+ .addKeepRules(
+ "-" + flattenPackageHierarchyOrRepackageClasses + " \"" + getRepackagePackage() + "\"")
+ .addOptionsModification(
+ options -> {
+ assertFalse(options.testing.enableExperimentalRepackaging);
+ options.testing.enableExperimentalRepackaging = enableExperimentalRepackaging;
+ });
+ }
+
+ protected boolean isExperimentalRepackaging() {
+ return enableExperimentalRepackaging;
+ }
+
+ protected boolean isFlattenPackageHierarchy() {
+ return flattenPackageHierarchyOrRepackageClasses.equals(FLATTEN_PACKAGE_HIERARCHY);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithCollisionsTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithCollisionsTest.java
new file mode 100644
index 0000000..42f2492
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithCollisionsTest.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.repackage;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.util.Iterator;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageWithCollisionsTest extends RepackageTestBase {
+
+ public RepackageWithCollisionsTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Override
+ public String getRepackagePackage() {
+ return com.android.tools.r8.repackage.testclasses.repackagewithcollisionstest.destination.Foo
+ .class
+ .getPackage()
+ .getName();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addProgramClasses(getTestClasses())
+ .addKeepMainRule(TestClass.class)
+ .addKeepRules("-keep class " + getRepackagePackage() + ".** { *; }")
+ .addKeepAttributeInnerClassesAndEnclosingMethod()
+ .apply(this::configureRepackaging)
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines(
+ "first.Foo",
+ "first.Foo$Bar",
+ "first.first.Foo",
+ "first.first.Foo$Bar",
+ "second.Foo",
+ "second.Foo$Bar",
+ "destination.Foo",
+ "destination.Foo$Bar",
+ "destination.first.Foo",
+ "destination.first.Foo$Bar");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ Iterator<Class<?>> testClassesIterator = getTestClasses().iterator();
+
+ Class<?> firstFoo = testClassesIterator.next();
+ assertThat(firstFoo, isRepackagedAsExpected(inspector, "a"));
+
+ Class<?> firstFooBar = testClassesIterator.next();
+ assertThat(firstFooBar, isRepackagedAsExpected(inspector, "a"));
+
+ Class<?> firstFirstFoo = testClassesIterator.next();
+ assertThat(firstFirstFoo, isRepackagedAsExpected(inspector, "b"));
+
+ Class<?> firstFirstFooBar = testClassesIterator.next();
+ assertThat(firstFirstFooBar, isRepackagedAsExpected(inspector, "b"));
+
+ Class<?> secondFoo = testClassesIterator.next();
+ assertThat(secondFoo, isRepackagedAsExpected(inspector, "c"));
+
+ Class<?> secondBar = testClassesIterator.next();
+ assertThat(secondBar, isRepackagedAsExpected(inspector, "c"));
+
+ Class<?> destinationFoo = testClassesIterator.next();
+ assertThat(inspector.clazz(destinationFoo), isPresentAndNotRenamed());
+
+ Class<?> destinationFooBar = testClassesIterator.next();
+ assertThat(inspector.clazz(destinationFooBar), isPresentAndNotRenamed());
+
+ Class<?> destinationFirstFoo = testClassesIterator.next();
+ assertThat(inspector.clazz(destinationFirstFoo), isPresentAndNotRenamed());
+
+ Class<?> destinationFirstBar = testClassesIterator.next();
+ assertThat(inspector.clazz(destinationFirstBar), isPresentAndNotRenamed());
+ }
+
+ private static List<Class<?>> getTestClasses() {
+ return ImmutableList.of(
+ com.android.tools.r8.repackage.testclasses.repackagewithcollisionstest.first.Foo.class,
+ com.android.tools.r8.repackage.testclasses.repackagewithcollisionstest.first.Foo.Bar.class,
+ com.android.tools.r8.repackage.testclasses.repackagewithcollisionstest.first.first.Foo
+ .class,
+ com.android.tools.r8.repackage.testclasses.repackagewithcollisionstest.first.first.Foo.Bar
+ .class,
+ com.android.tools.r8.repackage.testclasses.repackagewithcollisionstest.second.Foo.class,
+ com.android.tools.r8.repackage.testclasses.repackagewithcollisionstest.second.Foo.Bar.class,
+ com.android.tools.r8.repackage.testclasses.repackagewithcollisionstest.destination.Foo
+ .class,
+ com.android.tools.r8.repackage.testclasses.repackagewithcollisionstest.destination.Foo.Bar
+ .class,
+ com.android.tools.r8.repackage.testclasses.repackagewithcollisionstest.destination.first.Foo
+ .class,
+ com.android.tools.r8.repackage.testclasses.repackagewithcollisionstest.destination.first.Foo
+ .Bar.class);
+ }
+
+ public static class TestClass {
+
+ public static void main(String[] args) {
+ new com.android.tools.r8.repackage.testclasses.repackagewithcollisionstest.first.Foo();
+ new com.android.tools.r8.repackage.testclasses.repackagewithcollisionstest.first.Foo.Bar();
+ new com.android.tools.r8.repackage.testclasses.repackagewithcollisionstest.first.first.Foo();
+ new com.android.tools.r8.repackage.testclasses.repackagewithcollisionstest.first.first.Foo
+ .Bar();
+ new com.android.tools.r8.repackage.testclasses.repackagewithcollisionstest.second.Foo();
+ new com.android.tools.r8.repackage.testclasses.repackagewithcollisionstest.second.Foo.Bar();
+ new com.android.tools.r8.repackage.testclasses.repackagewithcollisionstest.destination.Foo();
+ new com.android.tools.r8.repackage.testclasses.repackagewithcollisionstest.destination.Foo
+ .Bar();
+ new com.android.tools.r8.repackage.testclasses.repackagewithcollisionstest.destination.first
+ .Foo();
+ new com.android.tools.r8.repackage.testclasses.repackagewithcollisionstest.destination.first
+ .Foo.Bar();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithFeatureSplitTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithFeatureSplitTest.java
index 40e7721..1183c1f 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithFeatureSplitTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithFeatureSplitTest.java
@@ -9,10 +9,8 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.dexsplitter.SplitterTestBase.RunInterface;
import com.android.tools.r8.dexsplitter.SplitterTestBase.SplitRunner;
@@ -25,12 +23,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class RepackageWithFeatureSplitTest extends TestBase {
-
- private static final String REPACKAGE_DIR = "foo";
-
- private final String flattenPackageHierarchyOrRepackageClasses;
- private final TestParameters parameters;
+public class RepackageWithFeatureSplitTest extends RepackageTestBase {
@Parameters(name = "{1}, kind: {0}")
public static List<Object[]> data() {
@@ -41,8 +34,7 @@
public RepackageWithFeatureSplitTest(
String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
- this.flattenPackageHierarchyOrRepackageClasses = flattenPackageHierarchyOrRepackageClasses;
- this.parameters = parameters;
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
}
@Test
@@ -52,13 +44,7 @@
.addFeatureSplit(FeatureMain.class, FeatureClass.class)
.addFeatureSplitRuntime()
.addKeepFeatureMainRule(FeatureMain.class)
- .addKeepRules(
- "-" + flattenPackageHierarchyOrRepackageClasses + " \"" + REPACKAGE_DIR + "\"")
- .addOptionsModification(
- options -> {
- assertFalse(options.testing.enableExperimentalRepackaging);
- options.testing.enableExperimentalRepackaging = true;
- })
+ .apply(this::configureRepackaging)
.enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithInitClassTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithInitClassTest.java
index 501340a..9bc8115 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithInitClassTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithInitClassTest.java
@@ -4,43 +4,23 @@
package com.android.tools.r8.repackage;
-import static com.android.tools.r8.shaking.ProguardConfigurationParser.FLATTEN_PACKAGE_HIERARCHY;
-import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
-import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.google.common.collect.ImmutableList;
-import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class RepackageWithInitClassTest extends TestBase {
-
- private static final String REPACKAGE_PACKAGE = "foo";
-
- private final String flattenPackageHierarchyOrRepackageClasses;
- private final TestParameters parameters;
-
- @Parameters(name = "{1}, kind: {0}")
- public static List<Object[]> data() {
- return buildParameters(
- ImmutableList.of(FLATTEN_PACKAGE_HIERARCHY, REPACKAGE_CLASSES),
- getTestParameters().withAllRuntimesAndApiLevels().build());
- }
+public class RepackageWithInitClassTest extends RepackageTestBase {
public RepackageWithInitClassTest(
String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
- this.flattenPackageHierarchyOrRepackageClasses = flattenPackageHierarchyOrRepackageClasses;
- this.parameters = parameters;
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
}
@Test
@@ -49,13 +29,7 @@
.addInnerClasses(getClass())
.addClassObfuscationDictionary("a")
.addKeepMainRule(TestClass.class)
- .addKeepRules(
- "-" + flattenPackageHierarchyOrRepackageClasses + " \"" + REPACKAGE_PACKAGE + "\"")
- .addOptionsModification(
- options -> {
- assert !options.testing.enableExperimentalRepackaging;
- options.testing.enableExperimentalRepackaging = true;
- })
+ .apply(this::configureRepackaging)
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
@@ -73,11 +47,7 @@
assertThat(repackagedClassSubject.uniqueFieldWithName("GREETING"), not(isPresent()));
// Verify that the class was repackaged.
- assertEquals(
- flattenPackageHierarchyOrRepackageClasses.equals(FLATTEN_PACKAGE_HIERARCHY)
- ? REPACKAGE_PACKAGE + ".a"
- : REPACKAGE_PACKAGE,
- repackagedClassSubject.getDexProgramClass().getType().getPackageName());
+ assertThat(StaticMemberValuePropagation.class, isRepackaged(inspector));
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithMainDexListTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithMainDexListTest.java
index 00dd079..4fc28f5 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithMainDexListTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithMainDexListTest.java
@@ -9,11 +9,9 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertFalse;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
@@ -25,12 +23,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class RepackageWithMainDexListTest extends TestBase {
-
- private static final String REPACKAGE_DIR = "foo";
-
- private final String flattenPackageHierarchyOrRepackageClasses;
- private final TestParameters parameters;
+public class RepackageWithMainDexListTest extends RepackageTestBase {
@Parameters(name = "{1}, kind: {0}")
public static List<Object[]> data() {
@@ -44,8 +37,7 @@
public RepackageWithMainDexListTest(
String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
- this.flattenPackageHierarchyOrRepackageClasses = flattenPackageHierarchyOrRepackageClasses;
- this.parameters = parameters;
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
}
@Test
@@ -56,15 +48,9 @@
.addKeepClassRulesWithAllowObfuscation(TestClass.class, OtherTestClass.class)
.addKeepRules(
"-keepclassmembers class " + TestClass.class.getTypeName() + " { <methods>; }")
- .addKeepRules(
- "-" + flattenPackageHierarchyOrRepackageClasses + " \"" + REPACKAGE_DIR + "\"")
// Add a class that will be repackaged to the main dex list.
.addMainDexListClasses(TestClass.class)
- .addOptionsModification(
- options -> {
- assertFalse(options.testing.enableExperimentalRepackaging);
- options.testing.enableExperimentalRepackaging = true;
- })
+ .apply(this::configureRepackaging)
// Debug mode to enable minimal main dex.
.debug()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithNonReboundFieldReferenceTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithNonReboundFieldReferenceTest.java
new file mode 100644
index 0000000..2372f8a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithNonReboundFieldReferenceTest.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 static com.android.tools.r8.shaking.ProguardConfigurationParser.FLATTEN_PACKAGE_HIERARCHY;
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.repackage.testclasses.RepackagingWithNonReboundFieldReferenceTestClasses;
+import com.android.tools.r8.repackage.testclasses.RepackagingWithNonReboundFieldReferenceTestClasses.B;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RepackageWithNonReboundFieldReferenceTest extends RepackageTestBase {
+
+ private final boolean alwaysUseExistingAccessInfoCollectionsInMemberRebinding;
+
+ @Parameters(name = "{2}, use access info collections: {0}, kind: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(),
+ ImmutableList.of(FLATTEN_PACKAGE_HIERARCHY, REPACKAGE_CLASSES),
+ getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ public RepackageWithNonReboundFieldReferenceTest(
+ boolean alwaysUseExistingAccessInfoCollectionsInMemberRebinding,
+ String flattenPackageHierarchyOrRepackageClasses,
+ TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ this.alwaysUseExistingAccessInfoCollectionsInMemberRebinding =
+ alwaysUseExistingAccessInfoCollectionsInMemberRebinding;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass(), RepackagingWithNonReboundFieldReferenceTestClasses.class)
+ .addKeepMainRule(TestClass.class)
+ .addOptionsModification(
+ options -> {
+ assertTrue(options.testing.alwaysUseExistingAccessInfoCollectionsInMemberRebinding);
+ options.testing.alwaysUseExistingAccessInfoCollectionsInMemberRebinding =
+ alwaysUseExistingAccessInfoCollectionsInMemberRebinding;
+ })
+ .apply(this::configureRepackaging)
+ .enableMemberValuePropagationAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ System.out.println(B.GREETING);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithNonReboundMethodReferenceTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithNonReboundMethodReferenceTest.java
new file mode 100644
index 0000000..cbb352a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithNonReboundMethodReferenceTest.java
@@ -0,0 +1,71 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.repackage;
+
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.FLATTEN_PACKAGE_HIERARCHY;
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.repackage.testclasses.RepackagingWithNonReboundMethodReferenceTestClasses;
+import com.android.tools.r8.repackage.testclasses.RepackagingWithNonReboundMethodReferenceTestClasses.B;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RepackageWithNonReboundMethodReferenceTest extends RepackageTestBase {
+
+ private final boolean alwaysUseExistingAccessInfoCollectionsInMemberRebinding;
+
+ @Parameters(name = "{2}, use access info collections: {0}, kind: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(),
+ ImmutableList.of(FLATTEN_PACKAGE_HIERARCHY, REPACKAGE_CLASSES),
+ getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ public RepackageWithNonReboundMethodReferenceTest(
+ boolean alwaysUseExistingAccessInfoCollectionsInMemberRebinding,
+ String flattenPackageHierarchyOrRepackageClasses,
+ TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ this.alwaysUseExistingAccessInfoCollectionsInMemberRebinding =
+ alwaysUseExistingAccessInfoCollectionsInMemberRebinding;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass(), RepackagingWithNonReboundMethodReferenceTestClasses.class)
+ .addKeepMainRule(TestClass.class)
+ .addOptionsModification(
+ options -> {
+ assertTrue(options.testing.alwaysUseExistingAccessInfoCollectionsInMemberRebinding);
+ options.testing.alwaysUseExistingAccessInfoCollectionsInMemberRebinding =
+ alwaysUseExistingAccessInfoCollectionsInMemberRebinding;
+ })
+ .apply(this::configureRepackaging)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ new B().greet();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateClassAnnotationTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateClassAnnotationTest.java
new file mode 100644
index 0000000..4672079
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateClassAnnotationTest.java
@@ -0,0 +1,76 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.repackage;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageWithPackagePrivateClassAnnotationTest extends RepackageTestBase {
+
+ public RepackageWithPackagePrivateClassAnnotationTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .addKeepClassRules(NonPublicKeptAnnotation.class)
+ .addKeepRuntimeVisibleAnnotations()
+ .apply(this::configureRepackaging)
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject classSubject = inspector.clazz(IneligibleForRepackaging.class);
+ assertThat(classSubject, isPresent());
+
+ // Verify that the class was not repackaged.
+ assertEquals(
+ IneligibleForRepackaging.class.getPackage().getName(),
+ classSubject.getDexProgramClass().getType().getPackageName());
+ }
+
+ public static class TestClass {
+
+ public static void main(String[] args) {
+ IneligibleForRepackaging.greet();
+ }
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.TYPE})
+ @interface NonPublicKeptAnnotation {}
+
+ @NonPublicKeptAnnotation
+ public static class IneligibleForRepackaging {
+
+ @NeverInline
+ public static void greet() {
+ System.out.println("Hello world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateFieldAnnotationTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateFieldAnnotationTest.java
new file mode 100644
index 0000000..46a83ff
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateFieldAnnotationTest.java
@@ -0,0 +1,79 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.repackage;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverPropagateValue;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageWithPackagePrivateFieldAnnotationTest extends RepackageTestBase {
+
+ public RepackageWithPackagePrivateFieldAnnotationTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .addKeepClassRules(NonPublicKeptAnnotation.class)
+ .addKeepRuntimeVisibleAnnotations()
+ .apply(this::configureRepackaging)
+ .enableInliningAnnotations()
+ .enableMemberValuePropagationAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject classSubject = inspector.clazz(IneligibleForRepackaging.class);
+ assertThat(classSubject, isPresent());
+
+ // Verify that the class was not repackaged.
+ assertEquals(
+ IneligibleForRepackaging.class.getPackage().getName(),
+ classSubject.getDexProgramClass().getType().getPackageName());
+ }
+
+ public static class TestClass {
+
+ public static void main(String[] args) {
+ IneligibleForRepackaging.greet();
+ }
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.FIELD})
+ @interface NonPublicKeptAnnotation {}
+
+ public static class IneligibleForRepackaging {
+
+ @NeverPropagateValue @NonPublicKeptAnnotation private static String GREETING = "Hello world!";
+
+ @NeverInline
+ public static void greet() {
+ System.out.println(GREETING);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateFieldTypeTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateFieldTypeTest.java
new file mode 100644
index 0000000..9dd90f3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateFieldTypeTest.java
@@ -0,0 +1,77 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.repackage;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverPropagateValue;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageWithPackagePrivateFieldTypeTest extends RepackageTestBase {
+
+ public RepackageWithPackagePrivateFieldTypeTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .addKeepClassRules(NonPublicKeptClass.class)
+ .apply(this::configureRepackaging)
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject classSubject = inspector.clazz(IneligibleForRepackaging.class);
+ assertThat(classSubject, isPresent());
+
+ // Verify that the class was not repackaged.
+ assertEquals(
+ IneligibleForRepackaging.class.getPackage().getName(),
+ classSubject.getDexProgramClass().getType().getPackageName());
+ }
+
+ public static class TestClass {
+
+ public static void main(String[] args) {
+ IneligibleForRepackaging.greet();
+ }
+ }
+
+ static class NonPublicKeptClass {}
+
+ public static class PublicSubClass extends NonPublicKeptClass {}
+
+ public static class IneligibleForRepackaging {
+
+ @NeverPropagateValue
+ private static NonPublicKeptClass FIELD =
+ System.currentTimeMillis() > 0 ? new PublicSubClass() : null;
+
+ @NeverInline
+ public static void greet() {
+ if (FIELD != null) {
+ System.out.println("Hello world!");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateInnerClassTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateInnerClassTest.java
new file mode 100644
index 0000000..e4c7fd1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateInnerClassTest.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 static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.repackage.RepackageWithPackagePrivateInnerClassTest.IneligibleForRepackaging.NonPublicKeptClass;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageWithPackagePrivateInnerClassTest extends RepackageTestBase {
+
+ public RepackageWithPackagePrivateInnerClassTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .addKeepClassRules(NonPublicKeptClass.class)
+ .addKeepAttributeInnerClassesAndEnclosingMethod()
+ .apply(this::configureRepackaging)
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject classSubject = inspector.clazz(IneligibleForRepackaging.class);
+ assertThat(classSubject, isPresent());
+
+ // Verify that the class was not repackaged.
+ assertEquals(
+ IneligibleForRepackaging.class.getPackage().getName(),
+ classSubject.getDexProgramClass().getType().getPackageName());
+ }
+
+ public static class TestClass {
+
+ public static void main(String[] args) {
+ IneligibleForRepackaging.greet();
+ }
+ }
+
+ public static class IneligibleForRepackaging {
+
+ @NeverInline
+ public static void greet() {
+ System.out.println("Hello world!");
+ }
+
+ static class NonPublicKeptClass {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateInterfaceTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateInterfaceTest.java
new file mode 100644
index 0000000..7fa82cd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateInterfaceTest.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.repackage;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageWithPackagePrivateInterfaceTest extends RepackageTestBase {
+
+ public RepackageWithPackagePrivateInterfaceTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .addKeepClassRules(NonPublicKeptInterface.class)
+ .apply(this::configureRepackaging)
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject classSubject = inspector.clazz(IneligibleForRepackaging.class);
+ assertThat(classSubject, isPresent());
+
+ // Verify that the class was not repackaged.
+ assertEquals(
+ IneligibleForRepackaging.class.getPackage().getName(),
+ classSubject.getDexProgramClass().getType().getPackageName());
+ }
+
+ public static class TestClass {
+
+ public static void main(String[] args) {
+ IneligibleForRepackaging.greet();
+ }
+ }
+
+ interface NonPublicKeptInterface {}
+
+ public static class IneligibleForRepackaging implements NonPublicKeptInterface {
+
+ @NeverInline
+ public static void greet() {
+ System.out.println("Hello world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateMethodAnnotationTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateMethodAnnotationTest.java
new file mode 100644
index 0000000..068cc89
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateMethodAnnotationTest.java
@@ -0,0 +1,76 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.repackage;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageWithPackagePrivateMethodAnnotationTest extends RepackageTestBase {
+
+ public RepackageWithPackagePrivateMethodAnnotationTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .addKeepClassRules(NonPublicKeptAnnotation.class)
+ .addKeepRuntimeVisibleAnnotations()
+ .apply(this::configureRepackaging)
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject classSubject = inspector.clazz(IneligibleForRepackaging.class);
+ assertThat(classSubject, isPresent());
+
+ // Verify that the class was not repackaged.
+ assertEquals(
+ IneligibleForRepackaging.class.getPackage().getName(),
+ classSubject.getDexProgramClass().getType().getPackageName());
+ }
+
+ public static class TestClass {
+
+ public static void main(String[] args) {
+ IneligibleForRepackaging.greet();
+ }
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.METHOD})
+ @interface NonPublicKeptAnnotation {}
+
+ public static class IneligibleForRepackaging {
+
+ @NonPublicKeptAnnotation
+ @NeverInline
+ public static void greet() {
+ System.out.println("Hello world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateMethodParameterAnnotationTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateMethodParameterAnnotationTest.java
new file mode 100644
index 0000000..c7cd698
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateMethodParameterAnnotationTest.java
@@ -0,0 +1,75 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.repackage;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageWithPackagePrivateMethodParameterAnnotationTest extends RepackageTestBase {
+
+ public RepackageWithPackagePrivateMethodParameterAnnotationTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .addKeepClassRules(NonPublicKeptAnnotation.class)
+ .addKeepRuntimeVisibleParameterAnnotations()
+ .apply(this::configureRepackaging)
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject classSubject = inspector.clazz(IneligibleForRepackaging.class);
+ assertThat(classSubject, isPresent());
+
+ // Verify that the class was not repackaged.
+ assertEquals(
+ IneligibleForRepackaging.class.getPackage().getName(),
+ classSubject.getDexProgramClass().getType().getPackageName());
+ }
+
+ public static class TestClass {
+
+ public static void main(String[] args) {
+ IneligibleForRepackaging.greet("Hello world!");
+ }
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.PARAMETER})
+ @interface NonPublicKeptAnnotation {}
+
+ public static class IneligibleForRepackaging {
+
+ @NeverInline
+ public static void greet(@NonPublicKeptAnnotation String greeting) {
+ System.out.println(greeting);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateSuperClassTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateSuperClassTest.java
new file mode 100644
index 0000000..6029869
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateSuperClassTest.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.repackage;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageWithPackagePrivateSuperClassTest extends RepackageTestBase {
+
+ public RepackageWithPackagePrivateSuperClassTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .addKeepClassRules(NonPublicKeptClass.class)
+ .apply(this::configureRepackaging)
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject classSubject = inspector.clazz(IneligibleForRepackaging.class);
+ assertThat(classSubject, isPresent());
+
+ // Verify that the class was not repackaged.
+ assertEquals(
+ IneligibleForRepackaging.class.getPackage().getName(),
+ classSubject.getDexProgramClass().getType().getPackageName());
+ }
+
+ public static class TestClass {
+
+ public static void main(String[] args) {
+ IneligibleForRepackaging.greet();
+ }
+ }
+
+ static class NonPublicKeptClass {}
+
+ public static class IneligibleForRepackaging extends NonPublicKeptClass {
+
+ @NeverInline
+ public static void greet() {
+ System.out.println("Hello world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackagingWithNonReboundFieldReferenceTest.java b/src/test/java/com/android/tools/r8/repackage/RepackagingWithNonReboundFieldReferenceTest.java
deleted file mode 100644
index c8a9d82..0000000
--- a/src/test/java/com/android/tools/r8/repackage/RepackagingWithNonReboundFieldReferenceTest.java
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.repackage;
-
-import static com.android.tools.r8.shaking.ProguardConfigurationParser.FLATTEN_PACKAGE_HIERARCHY;
-import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.fail;
-
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.repackage.RepackageWithMainDexListTest.TestClass;
-import com.android.tools.r8.repackage.testclasses.RepackagingWithNonReboundFieldReferenceTestClasses;
-import com.android.tools.r8.repackage.testclasses.RepackagingWithNonReboundFieldReferenceTestClasses.B;
-import com.google.common.collect.ImmutableList;
-import java.util.List;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-@RunWith(Parameterized.class)
-public class RepackagingWithNonReboundFieldReferenceTest extends TestBase {
-
- private static final String REPACKAGE_DIR = "foo";
-
- private final String flattenPackageHierarchyOrRepackageClasses;
- private final TestParameters parameters;
-
- @Parameters(name = "{1}, kind: {0}")
- public static List<Object[]> data() {
- return buildParameters(
- ImmutableList.of(FLATTEN_PACKAGE_HIERARCHY, REPACKAGE_CLASSES),
- getTestParameters().withAllRuntimesAndApiLevels().build());
- }
-
- public RepackagingWithNonReboundFieldReferenceTest(
- String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
- this.flattenPackageHierarchyOrRepackageClasses = flattenPackageHierarchyOrRepackageClasses;
- this.parameters = parameters;
- }
-
- @Test
- public void test() throws Exception {
- try {
- testForR8(parameters.getBackend())
- .addInnerClasses(getClass(), RepackagingWithNonReboundFieldReferenceTestClasses.class)
- .addKeepMainRule(TestClass.class)
- .addKeepRules(
- "-" + flattenPackageHierarchyOrRepackageClasses + " \"" + REPACKAGE_DIR + "\"")
- .addOptionsModification(
- options -> {
- assertFalse(options.testing.enableExperimentalRepackaging);
- options.testing.enableExperimentalRepackaging = true;
- })
- .enableMemberValuePropagationAnnotations()
- .enableNoVerticalClassMergingAnnotations()
- .setMinApi(parameters.getApiLevel())
- .compile();
-
- // TODO(b/168282032): Support lens rewriting of non-rebound references in the writer.
- fail();
- } catch (CompilationFailedException exception) {
- // Ignore.
- }
- }
-
- static class TestClass {
-
- public static void main(String[] args) {
- System.out.println(B.GREETING);
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackagingWithNonReboundMethodReferenceTest.java b/src/test/java/com/android/tools/r8/repackage/RepackagingWithNonReboundMethodReferenceTest.java
deleted file mode 100644
index 48891e7..0000000
--- a/src/test/java/com/android/tools/r8/repackage/RepackagingWithNonReboundMethodReferenceTest.java
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.repackage;
-
-import static com.android.tools.r8.shaking.ProguardConfigurationParser.FLATTEN_PACKAGE_HIERARCHY;
-import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.fail;
-
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.repackage.testclasses.RepackagingWithNonReboundMethodReferenceTestClasses;
-import com.android.tools.r8.repackage.testclasses.RepackagingWithNonReboundMethodReferenceTestClasses.B;
-import com.google.common.collect.ImmutableList;
-import java.util.List;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-@RunWith(Parameterized.class)
-public class RepackagingWithNonReboundMethodReferenceTest extends TestBase {
-
- private static final String REPACKAGE_DIR = "foo";
-
- private final String flattenPackageHierarchyOrRepackageClasses;
- private final TestParameters parameters;
-
- @Parameters(name = "{1}, kind: {0}")
- public static List<Object[]> data() {
- return buildParameters(
- ImmutableList.of(FLATTEN_PACKAGE_HIERARCHY, REPACKAGE_CLASSES),
- getTestParameters().withAllRuntimesAndApiLevels().build());
- }
-
- public RepackagingWithNonReboundMethodReferenceTest(
- String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
- this.flattenPackageHierarchyOrRepackageClasses = flattenPackageHierarchyOrRepackageClasses;
- this.parameters = parameters;
- }
-
- @Test
- public void test() throws Exception {
- try {
- testForR8(parameters.getBackend())
- .addInnerClasses(getClass(), RepackagingWithNonReboundMethodReferenceTestClasses.class)
- .addKeepMainRule(TestClass.class)
- .addKeepRules(
- "-" + flattenPackageHierarchyOrRepackageClasses + " \"" + REPACKAGE_DIR + "\"")
- .addOptionsModification(
- options -> {
- assertFalse(options.testing.enableExperimentalRepackaging);
- options.testing.enableExperimentalRepackaging = true;
- })
- .enableInliningAnnotations()
- .enableNeverClassInliningAnnotations()
- .enableNoVerticalClassMergingAnnotations()
- .setMinApi(parameters.getApiLevel())
- .compile();
-
- // TODO(b/168282032): Support lens rewriting of non-rebound references in the writer.
- fail();
- } catch (CompilationFailedException exception) {
- // Ignore.
- }
- }
-
- static class TestClass {
-
- public static void main(String[] args) {
- new B().greet();
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/RepackagingWithNonReboundMethodReferenceTestClasses.java b/src/test/java/com/android/tools/r8/repackage/testclasses/RepackagingWithNonReboundMethodReferenceTestClasses.java
index fb4775f..3979c2b 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/RepackagingWithNonReboundMethodReferenceTestClasses.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/RepackagingWithNonReboundMethodReferenceTestClasses.java
@@ -20,5 +20,6 @@
}
}
+ @NeverClassInline
public static class B extends A {}
}
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagewithcollisionstest/destination/Foo.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagewithcollisionstest/destination/Foo.java
new file mode 100644
index 0000000..3def86a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagewithcollisionstest/destination/Foo.java
@@ -0,0 +1,23 @@
+// 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.testclasses.repackagewithcollisionstest.destination;
+
+import com.android.tools.r8.NeverClassInline;
+
+@NeverClassInline
+public class Foo {
+
+ public Foo() {
+ System.out.println("destination.Foo");
+ }
+
+ @NeverClassInline
+ public static class Bar {
+
+ public Bar() {
+ System.out.println("destination.Foo$Bar");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagewithcollisionstest/destination/first/Foo.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagewithcollisionstest/destination/first/Foo.java
new file mode 100644
index 0000000..7cde833
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagewithcollisionstest/destination/first/Foo.java
@@ -0,0 +1,23 @@
+// 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.testclasses.repackagewithcollisionstest.destination.first;
+
+import com.android.tools.r8.NeverClassInline;
+
+@NeverClassInline
+public class Foo {
+
+ public Foo() {
+ System.out.println("destination.first.Foo");
+ }
+
+ @NeverClassInline
+ public static class Bar {
+
+ public Bar() {
+ System.out.println("destination.first.Foo$Bar");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagewithcollisionstest/first/Foo.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagewithcollisionstest/first/Foo.java
new file mode 100644
index 0000000..9f8beaf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagewithcollisionstest/first/Foo.java
@@ -0,0 +1,23 @@
+// 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.testclasses.repackagewithcollisionstest.first;
+
+import com.android.tools.r8.NeverClassInline;
+
+@NeverClassInline
+public class Foo {
+
+ public Foo() {
+ System.out.println("first.Foo");
+ }
+
+ @NeverClassInline
+ public static class Bar {
+
+ public Bar() {
+ System.out.println("first.Foo$Bar");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagewithcollisionstest/first/first/Foo.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagewithcollisionstest/first/first/Foo.java
new file mode 100644
index 0000000..586ec4e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagewithcollisionstest/first/first/Foo.java
@@ -0,0 +1,23 @@
+// 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.testclasses.repackagewithcollisionstest.first.first;
+
+import com.android.tools.r8.NeverClassInline;
+
+@NeverClassInline
+public class Foo {
+
+ public Foo() {
+ System.out.println("first.first.Foo");
+ }
+
+ @NeverClassInline
+ public static class Bar {
+
+ public Bar() {
+ System.out.println("first.first.Foo$Bar");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagewithcollisionstest/second/Foo.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagewithcollisionstest/second/Foo.java
new file mode 100644
index 0000000..b950606
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagewithcollisionstest/second/Foo.java
@@ -0,0 +1,23 @@
+// 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.testclasses.repackagewithcollisionstest.second;
+
+import com.android.tools.r8.NeverClassInline;
+
+@NeverClassInline
+public class Foo {
+
+ public Foo() {
+ System.out.println("second.Foo");
+ }
+
+ @NeverClassInline
+ public static class Bar {
+
+ public Bar() {
+ System.out.println("second.Foo$Bar");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
index acf4aff..8a0f5e5 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.retrace.stacktraces.ActualRetraceBotStackTrace;
import com.android.tools.r8.retrace.stacktraces.ActualRetraceBotStackTraceWithInfo;
import com.android.tools.r8.retrace.stacktraces.FoundMethodVerboseStackTrace;
+import com.android.tools.r8.retrace.stacktraces.PGStackTrace;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.base.Charsets;
import java.io.ByteArrayOutputStream;
@@ -117,6 +118,16 @@
}
@Test
+ public void testPGStackTrace() throws Exception {
+ PGStackTrace pgStackTrace = new PGStackTrace();
+ runTest(
+ pgStackTrace.mapping(),
+ StringUtils.joinLines(pgStackTrace.obfuscatedStackTrace()),
+ false,
+ StringUtils.joinLines(pgStackTrace.retracedStackTrace()) + StringUtils.LINE_SEPARATOR);
+ }
+
+ @Test
public void testEmpty() throws IOException {
runTest("", "", false, "");
}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
index 9af9e28..8bf3b29 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.retrace.stacktraces.FileNameExtensionStackTrace;
import com.android.tools.r8.retrace.stacktraces.InlineFileNameStackTrace;
import com.android.tools.r8.retrace.stacktraces.InlineNoLineNumberStackTrace;
+import com.android.tools.r8.retrace.stacktraces.InlineSourceFileContextStackTrace;
import com.android.tools.r8.retrace.stacktraces.InlineWithLineNumbersStackTrace;
import com.android.tools.r8.retrace.stacktraces.InvalidStackTrace;
import com.android.tools.r8.retrace.stacktraces.NamedModuleStackTrace;
@@ -173,6 +174,11 @@
runRetraceTest(new UnknownSourceStackTrace());
}
+ @Test
+ public void testInlineSourceFileContext() {
+ runRetraceTest(new InlineSourceFileContextStackTrace());
+ }
+
private TestDiagnosticMessagesImpl runRetraceTest(StackTraceForTest stackTraceForTest) {
TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
RetraceCommand retraceCommand =
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineSourceFileContextStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineSourceFileContextStackTrace.java
new file mode 100644
index 0000000..3c22097
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineSourceFileContextStackTrace.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace.stacktraces;
+
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
+import java.util.List;
+
+public class InlineSourceFileContextStackTrace implements StackTraceForTest {
+
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return Arrays.asList(
+ " at com.google.appreduce.remapper.KotlinJavaSourceFileTestObject"
+ + ".main(KotlinJavaSourceFileTestObject.java:1)");
+ }
+
+ @Override
+ public String mapping() {
+ return StringUtils.joinLines(
+ "com.google.appreduce.remapper.KotlinJavaSourceFileTestLibrary ->"
+ + " com.google.appreduce.remapper.KotlinJavaSourceFileTestLibrary:",
+ "# {\"id\":\"sourceFile\",\"fileName\":\"KotlinJavaSourceFileTestLibrary.kt\"}",
+ " void <init>() -> <init>",
+ "com.google.appreduce.remapper.KotlinJavaSourceFileTestObject ->"
+ + " com.google.appreduce.remapper.KotlinJavaSourceFileTestObject:",
+ " void <init>() -> <init>",
+ " 1:1:void com.google.appreduce.remapper.KotlinJavaSourceFileTestLibrary"
+ + ".throwsException():22:22 -> main",
+ " 1:1:void com.google.appreduce.remapper.KotlinJavaSourceFileTestLibrary"
+ + ".callsThrowsException():19 -> main",
+ " 1:1:void main(java.lang.String[]):32 -> main",
+ " 2:7:void printStackTraceUpToMain(java.lang.Exception):19:24 -> main",
+ " 2:7:void main(java.lang.String[]):34 -> main");
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return Arrays.asList(
+ " at com.google.appreduce.remapper.KotlinJavaSourceFileTestLibrary"
+ + ".throwsException(KotlinJavaSourceFileTestLibrary.kt:22)",
+ " at com.google.appreduce.remapper.KotlinJavaSourceFileTestLibrary"
+ + ".callsThrowsException(KotlinJavaSourceFileTestLibrary.kt:19)",
+ " at com.google.appreduce.remapper.KotlinJavaSourceFileTestObject"
+ + ".main(KotlinJavaSourceFileTestObject.java:32)");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 0;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/PGStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/PGStackTrace.java
new file mode 100644
index 0000000..2732b34
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/PGStackTrace.java
@@ -0,0 +1,47 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace.stacktraces;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class PGStackTrace implements StackTraceForTest {
+
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return Arrays.asList(
+ "09-16 15:43:01.249 23316 23316 E AndroidRuntime: java.lang.NullPointerException: Attempt"
+ + " to invoke virtual method 'boolean"
+ + " com.google.android.foo(com.google.android.foo.Data$Key)' on a null object"
+ + " reference",
+ "09-16 15:43:01.249 23316 23316 E AndroidRuntime: at"
+ + " com.google.apps.sectionheader.SectionHeaderListController.onToolbarStateChanged(PG:586)",
+ "09-16 15:43:01.249 23316 23316 E AndroidRuntime: at"
+ + " com.google.apps.Controller.onToolbarStateChanged(PG:1087)");
+ }
+
+ @Override
+ public String mapping() {
+ return "";
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return Arrays.asList(
+ "09-16 15:43:01.249 23316 23316 E AndroidRuntime: java.lang.NullPointerException: Attempt"
+ + " to invoke virtual method 'boolean"
+ + " com.google.android.foo(com.google.android.foo.Data$Key)' on a null object"
+ + " reference",
+ "09-16 15:43:01.249 23316 23316 E AndroidRuntime: at"
+ + " com.google.apps.sectionheader.SectionHeaderListController.onToolbarStateChanged(SectionHeaderListController.java:586)",
+ "09-16 15:43:01.249 23316 23316 E AndroidRuntime: at"
+ + " com.google.apps.Controller.onToolbarStateChanged(Controller.java:1087)");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 0;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java
index a0049f4..0be3ece 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java
@@ -481,14 +481,12 @@
runR8Test(
builder -> {
builder.addAssertionsConfiguration(AssertionsConfiguration.Builder::enableAllAssertions);
- builder.addOptionsModification(options -> options.testing.readInputStackMaps = true);
},
inspector -> checkAssertionCodeEnabled(inspector, true),
allAssertionsExpectedLines());
runR8Test(
builder -> {
builder.addAssertionsConfiguration(AssertionsConfiguration.Builder::enableAllAssertions);
- builder.addOptionsModification(options -> options.testing.readInputStackMaps = true);
},
inspector -> checkAssertionCodeEnabled(inspector, true),
allAssertionsExpectedLines(),
diff --git a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
index d989fbf..fd676a3 100644
--- a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
@@ -210,8 +210,7 @@
+ " *** *(...); }")
.compile()
.graphInspector();
- // TODO(b/159418523): It appears that the insertion in never-inline causes additional retention.
- assertRetainedClassesEqual(referenceInspector, ifThenKeepClassMembersInspector, false, true);
+ assertRetainedClassesEqual(referenceInspector, ifThenKeepClassMembersInspector, false, false);
GraphInspector ifThenKeepClassesWithMembersInspector =
testForR8(Backend.CF)
@@ -228,9 +227,8 @@
+ " *** *(...); }")
.compile()
.graphInspector();
- // TODO(b/159418523): It appears that the insertion in never-inline causes additional retention.
assertRetainedClassesEqual(
- referenceInspector, ifThenKeepClassesWithMembersInspector, false, true);
+ referenceInspector, ifThenKeepClassesWithMembersInspector, false, false);
GraphInspector ifHasMemberThenKeepClassInspector =
testForR8(Backend.CF)
@@ -249,9 +247,8 @@
+ " *** <2>(...); }")
.compile()
.graphInspector();
- // TODO(b/159418523): It appears that the insertion in never-inline causes additional retention.
- // Also, here neither is a subset of the other. That also appears wrong.
- assertRetainedClassesEqual(referenceInspector, ifHasMemberThenKeepClassInspector, true, true);
+ // TODO(b/159418523): Should the reference be equal to the result with the conditional rule?
+ assertRetainedClassesEqual(referenceInspector, ifHasMemberThenKeepClassInspector, true, false);
}
private void assertRetainedClassesEqual(
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
index de762ac..cd6e4a3 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
@@ -54,8 +54,7 @@
.assertStdoutThatMatches(containsString("referenced in keep rule"))
// TODO(b/124655065): We should always know the reason for keeping.
// It is OK if this starts failing while the kept-graph API is incomplete, in which case
- // replace
- // the 'not(containsString(' by just 'containsString('.
+ // replace the 'not(containsString(' by just 'containsString('.
.assertStdoutThatMatches(not(containsString("kept for unknown reasons")));
}
}
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 c84415e..84621b6 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -627,7 +627,7 @@
void visitTryCatchBlock(Label start, Label end, Label handler, String type);
}
- private MethodVisitor redirectVistiTryCatchBlock(
+ private MethodVisitor redirectVisitTryCatchBlock(
MethodVisitor visitor, VisitTryCatchBlockCallback callback) {
return new MethodVisitor(ASM7, visitor) {
@Override
@@ -649,7 +649,7 @@
end,
handler,
type,
- redirectVistiTryCatchBlock(this, super::visitTryCatchBlock));
+ redirectVisitTryCatchBlock(this, super::visitTryCatchBlock));
} else {
super.visitTryCatchBlock(start, end, handler, type);
}
@@ -685,4 +685,18 @@
}
});
}
+
+ public ClassFileTransformer stripFrames(String methodName) {
+ return addMethodTransformer(
+ new MethodTransformer() {
+
+ @Override
+ public void visitFrame(
+ int type, int numLocal, Object[] local, int numStack, Object[] stack) {
+ if (!getContext().method.getMethodName().equals(methodName)) {
+ super.visitFrame(type, numLocal, local, numStack, stack);
+ }
+ }
+ });
+ }
}