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..cc01f837 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 dcd003c..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 e89e6cd5..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); + } + } + }); + } }