Merge commit '8e6ffb0601c56268382f651f7877e2e8f01a2519' into dev-release
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java index 9dc1cc9..8323666 100644 --- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java +++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -166,7 +166,7 @@ private CompilationMode mode; private int minApiLevel = 0; - private DesugarState desugarState = DesugarState.ON; + protected DesugarState desugarState = DesugarState.ON; private List<StringResource> desugaredLibraryConfigurationResources = new ArrayList<>(); private boolean includeClassesChecksum = false; private boolean lookupLibraryBeforeProgram = true;
diff --git a/src/main/java/com/android/tools/r8/CompatDxHelper.java b/src/main/java/com/android/tools/r8/CompatDxHelper.java index fb186f4..beec3dc 100644 --- a/src/main/java/com/android/tools/r8/CompatDxHelper.java +++ b/src/main/java/com/android/tools/r8/CompatDxHelper.java
@@ -6,15 +6,12 @@ import com.android.tools.r8.utils.AndroidApp; import com.android.tools.r8.utils.InternalOptions; -import com.android.tools.r8.utils.InternalOptions.DesugarState; public class CompatDxHelper { public static void run(D8Command command, Boolean minimalMainDex) throws CompilationFailedException { AndroidApp app = command.getInputApp(); InternalOptions options = command.getInternalOptions(); - // DX does not desugar. - options.desugarState = DesugarState.OFF; // DX allows --multi-dex without specifying a main dex list for legacy devices. // That is broken, but for CompatDX we do the same to not break existing builds // that are trying to transition. @@ -28,4 +25,8 @@ public static void ignoreDexInArchive(BaseCommand.Builder builder) { builder.setIgnoreDexInArchive(true); } + + public static void enableDesugarBackportStatics(D8Command.Builder builder) { + builder.enableDesugarBackportStatics(); + } }
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java index 467570f..d421ec3 100644 --- a/src/main/java/com/android/tools/r8/D8Command.java +++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -155,6 +155,12 @@ return self(); } + // Internal helper for compat tools to make them only desugar backports. + Builder enableDesugarBackportStatics() { + this.desugarState = DesugarState.ONLY_BACKPORT_STATICS; + return self(); + } + @Override Builder self() { return this;
diff --git a/src/main/java/com/android/tools/r8/PrintUses.java b/src/main/java/com/android/tools/r8/PrintUses.java index a384ebc..ab5707a 100644 --- a/src/main/java/com/android/tools/r8/PrintUses.java +++ b/src/main/java/com/android/tools/r8/PrintUses.java
@@ -91,7 +91,9 @@ @Override public boolean registerInvokeVirtual(DexMethod method) { - DexEncodedMethod target = appInfo.lookupVirtualTarget(method.holder, method); + ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method); + DexEncodedMethod target = + resolutionResult.isVirtualTarget() ? resolutionResult.getSingleTarget() : null; if (target != null && target.method != method) { addType(method.holder); addMethod(target.method);
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java index 1c7774d..77c48958 100644 --- a/src/main/java/com/android/tools/r8/R8.java +++ b/src/main/java/com/android/tools/r8/R8.java
@@ -41,6 +41,8 @@ import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple; import com.android.tools.r8.jar.CfApplicationWriter; import com.android.tools.r8.kotlin.Kotlin; +import com.android.tools.r8.kotlin.KotlinInfo; +import com.android.tools.r8.kotlin.KotlinMemberInfo; import com.android.tools.r8.logging.Log; import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.naming.Minifier; @@ -881,7 +883,9 @@ Kotlin kotlin = appView.dexItemFactory().kotlin; Reporter reporter = options.reporter; for (DexProgramClass programClass : application.classes()) { - programClass.setKotlinInfo(kotlin.getKotlinInfo(programClass, reporter)); + KotlinInfo kotlinInfo = kotlin.getKotlinInfo(programClass, reporter); + programClass.setKotlinInfo(kotlinInfo); + KotlinMemberInfo.markKotlinMemberInfo(programClass, kotlinInfo); } }
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 23dbc06..fc2a080 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
@@ -5,9 +5,7 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.errors.Unreachable; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.code.NumericType; import com.android.tools.r8.ir.code.ValueType; import com.android.tools.r8.ir.conversion.CfSourceCode; @@ -169,9 +167,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forBinop(); } }
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 f22b956..53536d2 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,9 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; import com.android.tools.r8.ir.conversion.CfState.Slot; @@ -45,9 +43,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forArrayLength(); } }
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 db6e106..1f667b4 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
@@ -5,9 +5,7 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.errors.Unreachable; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.code.MemberType; import com.android.tools.r8.ir.code.ValueType; import com.android.tools.r8.ir.conversion.CfSourceCode; @@ -90,9 +88,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forArrayGet(); } }
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 3f155f4..d0eadef 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
@@ -5,9 +5,7 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.errors.Unreachable; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.code.MemberType; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; @@ -80,9 +78,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forArrayPut(); } }
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 ca75d03..e89c480 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,9 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; @@ -61,9 +59,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forCheckCast(type, invocationContext); } }
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 10c8eab..dac3eca 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
@@ -5,9 +5,7 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.errors.Unreachable; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.code.Cmp; import com.android.tools.r8.ir.code.Cmp.Bias; import com.android.tools.r8.ir.code.NumericType; @@ -94,9 +92,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forBinop(); } }
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 a10e914..7f77ade 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,9 +5,7 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.errors.Unreachable; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; @@ -84,9 +82,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forConstClass(type, invocationContext); } }
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 6e34d8d..a9a1d73 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,8 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexMethodHandle; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.graph.UseRegistry.MethodHandleUse; import com.android.tools.r8.ir.conversion.CfSourceCode; @@ -60,9 +58,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forConstMethodHandle(); } }
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 f3b7e3b..063b6fa 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,8 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexProto; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; @@ -60,9 +58,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forConstMethodType(); } }
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 69acb80..7ad901a 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,9 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.code.ValueType; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; @@ -37,9 +35,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forConstInstruction(); } }
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 fddbba4..72e4638 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,9 +5,7 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.errors.Unreachable; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.code.ValueType; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; @@ -135,9 +133,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forConstInstruction(); } }
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 45e5a13..d52d901 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,10 +4,8 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexString; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; import com.android.tools.r8.ir.conversion.IRBuilder; @@ -67,9 +65,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forConstInstruction(); } }
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 d4ec522..36ba9b4 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,8 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.errors.Unreachable; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexReference; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; @@ -83,9 +81,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forDexItemBasedConstString(item, invocationContext); } }
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 996355b..a000dcf 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,10 +5,8 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.errors.Unreachable; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; @@ -125,9 +123,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { switch (opcode) { case Opcodes.GETSTATIC: return inliningConstraints.forStaticGet(field, invocationContext);
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 a9f440d..e5a3c19 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,10 +7,8 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.errors.Unreachable; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; import com.android.tools.r8.ir.conversion.IRBuilder; @@ -295,9 +293,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return ConstraintWithTarget.ALWAYS; } }
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 d5f5bc6..efe67ba 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,9 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; import com.android.tools.r8.ir.conversion.IRBuilder; @@ -62,9 +60,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forJumpInstruction(); } }
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 e23c0cc..1ba9c06 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,9 +5,7 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.errors.Unreachable; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.code.If; import com.android.tools.r8.ir.code.If.Type; import com.android.tools.r8.ir.code.ValueType; @@ -95,9 +93,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forJumpInstruction(); } }
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 47f90b2..28a59d7 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,9 +5,7 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.errors.Unreachable; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.code.If; import com.android.tools.r8.ir.code.If.Type; import com.android.tools.r8.ir.code.ValueType; @@ -96,9 +94,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forJumpInstruction(); } }
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 951da4f..92f2094 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,9 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.code.NumericType; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; @@ -53,9 +51,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return ConstraintWithTarget.ALWAYS; } }
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 2cfdbaf..cb4baef 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,9 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; @@ -59,9 +57,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forInstanceOf(type, invocationContext); } }
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 2801ca8..833e5e1 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,9 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; @@ -147,7 +145,5 @@ public abstract ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView); + DexType invocationContext); }
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 4656408..6311bfd 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.DexClass; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexProgramClass; @@ -25,7 +26,6 @@ import com.android.tools.r8.ir.conversion.IRBuilder; import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; -import com.android.tools.r8.jar.InliningConstraintVisitor; import com.android.tools.r8.naming.NamingLens; import java.util.Arrays; import org.objectweb.asm.MethodVisitor; @@ -196,11 +196,78 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { - return InliningConstraintVisitor.getConstraintForInvoke( - opcode, method, graphLense, appView, inliningConstraints, invocationContext); + DexType invocationContext) { + GraphLense graphLense = inliningConstraints.getGraphLense(); + AppView<?> appView = inliningConstraints.getAppView(); + DexMethod target = method; + // Find the DEX invocation type. + Type type; + switch (opcode) { + case Opcodes.INVOKEINTERFACE: + // Could have changed to an invoke-virtual instruction due to vertical class merging + // (if an interface is merged into a class). + type = graphLense.lookupMethod(target, null, Type.INTERFACE).getType(); + assert type == Type.INTERFACE || type == Type.VIRTUAL; + break; + + case Opcodes.INVOKESPECIAL: + if (appView.dexItemFactory().isConstructor(target)) { + type = Type.DIRECT; + assert noNeedToUseGraphLense(target, type, graphLense); + } else if (target.holder == invocationContext) { + // The method could have been publicized. + type = graphLense.lookupMethod(target, null, Type.DIRECT).getType(); + assert type == Type.DIRECT || type == Type.VIRTUAL; + } else { + // This is a super call. Note that the vertical class merger translates some invoke-super + // instructions to invoke-direct. However, when that happens, the invoke instruction and + // the target method end up being in the same class, and therefore, we will allow inlining + // it. The result of using type=SUPER below will be the same, since it leads to the + // inlining constraint SAMECLASS. + // TODO(christofferqa): Consider using graphLense.lookupMethod (to do this, we need the + // context for the graph lense, though). + type = Type.SUPER; + assert noNeedToUseGraphLense(target, type, graphLense); + } + break; + + case Opcodes.INVOKESTATIC: { + // Static invokes may have changed as a result of horizontal class merging. + GraphLenseLookupResult lookup = graphLense.lookupMethod(target, null, Type.STATIC); + target = lookup.getMethod(); + type = lookup.getType(); + break; + } + + case Opcodes.INVOKEVIRTUAL: { + type = Type.VIRTUAL; + // Instructions that target a private method in the same class translates to + // invoke-direct. + if (target.holder == invocationContext) { + DexClass clazz = appView.definitionFor(target.holder); + if (clazz != null && clazz.lookupDirectMethod(target) != null) { + type = Type.DIRECT; + } + } + + // Virtual invokes may have changed to interface invokes as a result of member rebinding. + GraphLenseLookupResult lookup = graphLense.lookupMethod(target, null, type); + target = lookup.getMethod(); + type = lookup.getType(); + break; + } + + default: + throw new Unreachable("Unexpected opcode " + opcode); + } + + return inliningConstraints.forInvoke(target, type, invocationContext); + } + + private static boolean noNeedToUseGraphLense( + DexMethod method, Invoke.Type type, GraphLense graphLense) { + assert graphLense.lookupMethod(method, null, type).getType() == type; + return true; } private Type invokeTypeForInvokeSpecialToNonInitMethodOnHolder(
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 301be60..231c115 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,7 +5,6 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.errors.Unreachable; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexCallSite; import com.android.tools.r8.graph.DexMethodHandle; import com.android.tools.r8.graph.DexString; @@ -19,7 +18,6 @@ import com.android.tools.r8.graph.DexValue.DexValueMethodType; import com.android.tools.r8.graph.DexValue.DexValueString; import com.android.tools.r8.graph.DexValue.DexValueType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.code.ValueType; import com.android.tools.r8.ir.conversion.CfSourceCode; @@ -118,9 +116,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forInvokeCustom(); } }
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 c6b2bd0..df9d6bd 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,9 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; import com.android.tools.r8.ir.conversion.IRBuilder; @@ -60,9 +58,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return ConstraintWithTarget.ALWAYS; } }
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 4067a79..ae1fd85 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,9 +5,7 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.errors.Unreachable; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.code.ValueType; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; @@ -89,9 +87,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forLoad(); } }
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 c9ddad6..1d03017 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
@@ -5,9 +5,7 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.errors.Unreachable; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.code.NumericType; import com.android.tools.r8.ir.code.ValueType; import com.android.tools.r8.ir.conversion.CfSourceCode; @@ -141,9 +139,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forBinop(); } }
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 0fda291..dc117dd 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,9 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.code.Monitor.Type; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; @@ -54,9 +52,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forMonitor(); } }
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 bb79f60..936f62d 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,9 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; @@ -66,9 +64,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forInvokeMultiNewArray(type, invocationContext); } }
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 cbd67ae..254ee77 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
@@ -5,9 +5,7 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.errors.Unreachable; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.code.NumericType; import com.android.tools.r8.ir.code.ValueType; import com.android.tools.r8.ir.conversion.CfSourceCode; @@ -83,9 +81,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forUnop(); } }
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 ab00d86..46821da 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,9 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; @@ -57,9 +55,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forNewInstance(type, invocationContext); } }
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 d4f26a6..10504d9 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,9 +5,7 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.errors.Unreachable; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; @@ -100,9 +98,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forNewArrayEmpty(type, invocationContext); } }
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 75f6be9..0ae7685 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,9 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; import com.android.tools.r8.ir.conversion.IRBuilder; @@ -41,9 +39,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return ConstraintWithTarget.ALWAYS; } }
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 73d8f34..c1118e5 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
@@ -5,9 +5,7 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.errors.Unreachable; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.code.NumericType; import com.android.tools.r8.ir.code.ValueType; import com.android.tools.r8.ir.conversion.CfSourceCode; @@ -154,9 +152,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forUnop(); } }
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 9302711..035604b 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,9 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.code.Position; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; @@ -69,9 +67,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return ConstraintWithTarget.ALWAYS; } }
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 88972fe..76475fa 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,9 +5,7 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.errors.Unreachable; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.code.ValueType; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; @@ -77,9 +75,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forReturn(); } }
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 52d2ae3..fd55aa0 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,9 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; import com.android.tools.r8.ir.conversion.IRBuilder; @@ -46,9 +44,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forReturn(); } }
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 33205c1..5886058 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
@@ -6,9 +6,7 @@ import com.android.tools.r8.cf.CfPrinter; 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.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.code.ValueType; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; @@ -317,9 +315,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return ConstraintWithTarget.ALWAYS; } }
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 45f274a..5f8a05d 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
@@ -5,9 +5,7 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.errors.Unreachable; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.code.ValueType; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; @@ -88,9 +86,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forStore(); } }
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 f9c5f7e..47aff14 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
@@ -4,9 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; import com.android.tools.r8.ir.conversion.CfState.Slot; @@ -105,9 +103,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forJumpInstruction(); } }
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 8d2f92b..a7609c0 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,9 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; -import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.ir.conversion.CfSourceCode; import com.android.tools.r8.ir.conversion.CfState; import com.android.tools.r8.ir.conversion.CfState.Slot; @@ -48,9 +46,7 @@ @Override public ConstraintWithTarget inliningConstraint( InliningConstraints inliningConstraints, - DexType invocationContext, - GraphLense graphLense, - AppView<?> appView) { + DexType invocationContext) { return inliningConstraints.forJumpInstruction(); } }
diff --git a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java index f4f3c7e..69cbddb 100644 --- a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java +++ b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
@@ -60,6 +60,7 @@ private String output = null; private int numberOfThreads = 8; private boolean noLocals = false; + private boolean backportStatics = false; public static void main(String[] args) throws IOException, InterruptedException, ExecutionException { @@ -108,6 +109,9 @@ case "--nolocals": noLocals = true; break; + case "--desugar-backport-statics": + backportStatics = true; + break; default: System.err.println("Unsupported option: " + flag); System.exit(1); @@ -168,6 +172,9 @@ .setMode(noLocals ? CompilationMode.RELEASE : CompilationMode.DEBUG) .setMinApiLevel(AndroidApiLevel.H_MR2.getLevel()) .setDisableDesugaring(true); + if (backportStatics) { + CompatDxHelper.enableDesugarBackportStatics(builder); + } try (InputStream stream = zipFile.getInputStream(classEntry)) { builder.addClassProgramData( ByteStreams.toByteArray(stream),
diff --git a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java index a52c2ea..e98d5c0 100644 --- a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java +++ b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
@@ -90,6 +90,7 @@ public final String mainDexList; public final boolean minimalMainDex; public final int minApiLevel; + public final boolean backportStatics; public final String inputList; public final ImmutableList<String> inputs; // Undocumented option @@ -147,6 +148,7 @@ final OptionSpec<String> mainDexList; final OptionSpec<Void> minimalMainDex; final OptionSpec<Integer> minApiLevel; + final OptionSpec<Void> backportStatics; final OptionSpec<String> inputList; final OptionSpec<String> inputs; final OptionSpec<Void> version; @@ -226,6 +228,8 @@ minApiLevel = parser .accepts("min-sdk-version", "Minimum Android API level compatibility.") .withRequiredArg().ofType(Integer.class); + backportStatics = + parser.accepts("desugar-backport-statics", "Backport additional Java 8 APIs"); inputList = parser .accepts("input-list", "File listing input files") .withRequiredArg() @@ -290,6 +294,7 @@ } else { minApiLevel = AndroidApiLevel.getDefault().getLevel(); } + backportStatics = options.has(spec.backportStatics); inputList = options.valueOf(spec.inputList); inputs = ImmutableList.copyOf(options.valuesOf(spec.inputs)); maxIndexNumber = options.valueOf(spec.maxIndexNumber); @@ -451,13 +456,16 @@ CompatDxHelper.ignoreDexInArchive(builder); builder .addProgramFiles(inputs) - .setProgramConsumer( - createConsumer(inputs, output, singleDexFile, dexArgs.keepClasses)) + .setProgramConsumer(createConsumer(inputs, output, singleDexFile, dexArgs.keepClasses)) .setMode(mode) + .setDisableDesugaring(true) // DX does not desugar. .setMinApiLevel(dexArgs.minApiLevel); if (mainDexList != null) { builder.addMainDexListFiles(mainDexList); } + if (dexArgs.backportStatics) { + CompatDxHelper.enableDesugarBackportStatics(builder); + } CompatDxHelper.run(builder.build(), dexArgs.minimalMainDex); } finally { executor.shutdown();
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 7ffcf30..4e3df21 100644 --- a/src/main/java/com/android/tools/r8/graph/AppInfo.java +++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -256,57 +256,6 @@ return null; } - // TODO(b/147578480): RemoveDeprecation Use AppInfoWithClassHierarchy and - // lookupXX(DexMethod, DexProgramClass). The following 3 methods should either be removed or - // return null. - - /** - * Lookup static method following the super chain from the holder of {@code method}. - * - * <p>This method will resolve the method on the holder of {@code method} and only return a - * non-null value if the result of resolution was a static, non-abstract method. - * - * @param method the method to lookup - * @return The actual target for {@code method} or {@code null} if none found. - */ - @Deprecated // TODO(b/147578480): Remove - public DexEncodedMethod lookupStaticTarget(DexMethod method) { - assert checkIfObsolete(); - ResolutionResult resolutionResult = resolveMethod(method.holder, method); - DexEncodedMethod target = resolutionResult.getSingleTarget(); - return target == null || target.isStatic() ? target : null; - } - - /** - * Lookup direct method following the super chain from the holder of {@code method}. - * - * <p>This method will lookup private and constructor methods. - * - * @param method the method to lookup - * @return The actual target for {@code method} or {@code null} if none found. - */ - public DexEncodedMethod lookupDirectTarget(DexMethod method) { - assert checkIfObsolete(); - ResolutionResult resolutionResult = resolveMethod(method.holder, method); - DexEncodedMethod target = resolutionResult.getSingleTarget(); - return target == null || target.isDirectMethod() ? target : null; - } - - /** - * Lookup virtual method starting in type and following the super chain. - * - * <p>This method will resolve the method on the holder of {@code method} and only return a - * non-null value if the result of resolution was a virtual target. - * - * <p>TODO(b/140204899): Delete this method as it does resolution and not a "lookup of targets". - */ - public DexEncodedMethod lookupVirtualTarget(DexType type, DexMethod method) { - assert checkIfObsolete(); - assert type.isClassType() || type.isArrayType(); - ResolutionResult resolutionResult = resolveMethod(type, method); - return resolutionResult.isVirtualTarget() ? resolutionResult.getSingleTarget() : null; - } - /** * Implements resolution of a method descriptor against a target type. * @@ -447,28 +396,12 @@ } /** - * Helper method used for emulated interface resolution (not in JVM specifications). The result - * may be abstract. - */ - public ResolutionResult resolveMaximallySpecificMethods(DexClass clazz, DexMethod method) { - assert !clazz.type.isArrayType(); - if (clazz.isInterface()) { - // Look for exact method on interface. - DexEncodedMethod result = clazz.lookupMethod(method); - if (result != null) { - return new SingleResolutionResult(clazz, clazz, result); - } - } - return resolveMethodStep3(clazz, method); - } - - /** * Implements step 3 of <a * href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.3">Section * 5.4.3.3 of the JVM Spec</a>. As this is the same for interfaces and classes, we share one * implementation. */ - private ResolutionResult resolveMethodStep3(DexClass clazz, DexMethod method) { + ResolutionResult resolveMethodStep3(DexClass clazz, DexMethod method) { MaximallySpecificMethodsBuilder builder = new MaximallySpecificMethodsBuilder(clazz); resolveMethodStep3Helper(clazz, method, builder); return builder.resolve(); @@ -563,32 +496,6 @@ } /** - * Lookup instance field starting in type and following the interface and super chain. - * <p> - * The result is the field that will be hit at runtime, if such field is known. A result - * of null indicates that the field is either undefined or not an instance field. - */ - public DexEncodedField lookupInstanceTarget(DexType type, DexField field) { - assert checkIfObsolete(); - assert type.isClassType(); - DexEncodedField result = resolveFieldOn(type, field); - return result == null || result.accessFlags.isStatic() ? null : result; - } - - /** - * Lookup static field starting in type and following the interface and super chain. - * <p> - * The result is the field that will be hit at runtime, if such field is known. A result - * of null indicates that the field is either undefined or not a static field. - */ - public DexEncodedField lookupStaticTarget(DexType type, DexField field) { - assert checkIfObsolete(); - assert type.isClassType(); - DexEncodedField result = resolveFieldOn(type, field); - return result == null || !result.accessFlags.isStatic() ? null : result; - } - - /** * Implements resolution of a field descriptor against the holder of the field. See also {@link * #resolveFieldOn}. */
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java index 4344794..96fee36 100644 --- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java +++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.graph; +import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult; import java.util.ArrayDeque; import java.util.Deque; @@ -89,6 +90,48 @@ } /** + * Helper method used for emulated interface resolution (not in JVM specifications). The result + * may be abstract. + */ + public ResolutionResult resolveMaximallySpecificMethods(DexClass clazz, DexMethod method) { + assert !clazz.type.isArrayType(); + if (clazz.isInterface()) { + // Look for exact method on interface. + DexEncodedMethod result = clazz.lookupMethod(method); + if (result != null) { + return new SingleResolutionResult(clazz, clazz, result); + } + } + return resolveMethodStep3(clazz, method); + } + + /** + * Lookup instance field starting in type and following the interface and super chain. + * + * <p>The result is the field that will be hit at runtime, if such field is known. A result of + * null indicates that the field is either undefined or not an instance field. + */ + public DexEncodedField lookupInstanceTarget(DexType type, DexField field) { + assert checkIfObsolete(); + assert type.isClassType(); + DexEncodedField result = resolveFieldOn(type, field); + return result == null || result.accessFlags.isStatic() ? null : result; + } + + /** + * Lookup static field starting in type and following the interface and super chain. + * + * <p>The result is the field that will be hit at runtime, if such field is known. A result of + * null indicates that the field is either undefined or not a static field. + */ + public DexEncodedField lookupStaticTarget(DexType type, DexField field) { + assert checkIfObsolete(); + assert type.isClassType(); + DexEncodedField result = resolveFieldOn(type, field); + return result == null || !result.accessFlags.isStatic() ? null : result; + } + + /** * Lookup static method following the super chain from the holder of {@code method}. * * <p>This method will resolve the method on the holder of {@code method} and only return a
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 1581a5e..1388051 100644 --- a/src/main/java/com/android/tools/r8/graph/AppView.java +++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -351,6 +351,13 @@ } @SuppressWarnings("unchecked") + public AppView<AppInfoWithClassHierarchy> withClassHierarchy() { + return appInfo.hasClassHierarchy() + ? (AppView<AppInfoWithClassHierarchy>) this + : null; + } + + @SuppressWarnings("unchecked") public AppView<AppInfoWithSubtyping> withSubtyping() { return appInfo.hasSubtyping() ? (AppView<AppInfoWithSubtyping>) this
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 2753232..5402e3a 100644 --- a/src/main/java/com/android/tools/r8/graph/CfCode.java +++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -509,7 +509,7 @@ constraint = ConstraintWithTarget.meet( constraint, - insn.inliningConstraint(inliningConstraints, invocationContext, graphLense, appView), + insn.inliningConstraint(inliningConstraints, invocationContext), appView); if (constraint == ConstraintWithTarget.NEVER) { return constraint;
diff --git a/src/main/java/com/android/tools/r8/graph/DelegatingUseRegistry.java b/src/main/java/com/android/tools/r8/graph/DelegatingUseRegistry.java deleted file mode 100644 index 1375e9d..0000000 --- a/src/main/java/com/android/tools/r8/graph/DelegatingUseRegistry.java +++ /dev/null
@@ -1,69 +0,0 @@ -// Copyright (c) 2017, 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 class DelegatingUseRegistry extends UseRegistry { - private final UseRegistry delegate; - - public DelegatingUseRegistry(DexItemFactory factory, UseRegistry delegate) { - super(factory); - this.delegate = delegate; - } - - @Override - public boolean registerInvokeVirtual(DexMethod method) { - return delegate.registerInvokeVirtual(method); - } - - @Override - public boolean registerInvokeDirect(DexMethod method) { - return delegate.registerInvokeDirect(method); - } - - @Override - public boolean registerInvokeStatic(DexMethod method) { - return delegate.registerInvokeStatic(method); - } - - @Override - public boolean registerInvokeInterface(DexMethod method) { - return delegate.registerInvokeInterface(method); - } - - @Override - public boolean registerInvokeSuper(DexMethod method) { - return delegate.registerInvokeSuper(method); - } - - @Override - public boolean registerInstanceFieldWrite(DexField field) { - return delegate.registerInstanceFieldWrite(field); - } - - @Override - public boolean registerInstanceFieldRead(DexField field) { - return delegate.registerInstanceFieldRead(field); - } - - @Override - public boolean registerNewInstance(DexType type) { - return delegate.registerNewInstance(type); - } - - @Override - public boolean registerStaticFieldRead(DexField field) { - return delegate.registerStaticFieldRead(field); - } - - @Override - public boolean registerStaticFieldWrite(DexField field) { - return delegate.registerStaticFieldWrite(field); - } - - @Override - public boolean registerTypeReference(DexType type) { - return delegate.registerTypeReference(type); - } - -}
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 06f579c..358af04 100644 --- a/src/main/java/com/android/tools/r8/graph/DexClass.java +++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -13,8 +13,6 @@ import com.android.tools.r8.utils.PredicateUtils; import com.google.common.base.MoreObjects; import com.google.common.base.Predicates; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.collect.Iterators; import com.google.common.collect.Sets; @@ -24,14 +22,10 @@ import java.util.Iterator; import java.util.List; import java.util.ListIterator; -import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; -import kotlinx.metadata.KmConstructor; -import kotlinx.metadata.KmFunction; -import kotlinx.metadata.KmProperty; public abstract class DexClass extends DexDefinition { @@ -214,56 +208,6 @@ return Arrays.asList(virtualMethods); } - public Map<DexEncodedMethod, KmConstructor> kotlinConstructors( - List<KmConstructor> constructors, AppView<?> appView) { - ImmutableMap.Builder<DexEncodedMethod, KmConstructor> builder = ImmutableMap.builder(); - for (DexEncodedMethod method : directMethods) { - if (method.isInstanceInitializer()) { - KmConstructor constructor = method.findCompatibleKotlinConstructor(constructors, appView); - if (constructor != null) { - // Found a compatible constructor that is likely asked to keep. - builder.put(method, constructor); - } - } - } - return builder.build(); - } - - public Map<DexEncodedMethod, KmFunction> kotlinExtensions( - List<KmFunction> extensions, AppView<?> appView) { - ImmutableMap.Builder<DexEncodedMethod, KmFunction> builder = ImmutableMap.builder(); - for (DexEncodedMethod method : directMethods) { - if (method.isInitializer()) { - continue; - } - KmFunction extension = method.findCompatibleKotlinExtension(extensions, appView); - if (extension != null) { - // Found a compatible extension that is likely asked to keep. - builder.put(method, extension); - } - } - return builder.build(); - } - - public List<DexEncodedMethod> kotlinFunctions( - List<KmFunction> functions, List<KmProperty> properties, AppView<?> appView) { - ImmutableList.Builder<DexEncodedMethod> builder = ImmutableList.builder(); - for (DexEncodedMethod method : methods()) { - if (method.isInitializer()) { - continue; - } - KmFunction function = method.findCompatibleKotlinFunction(functions, appView); - if (function != null) { - // Found a compatible function that is likely asked to keep. - builder.add(method); - } else if (!method.isKotlinProperty(properties, appView)) { - // This could be a newly merged method that is not part of properties. - builder.add(method); - } - } - return builder.build(); - } - public void appendVirtualMethod(DexEncodedMethod method) { DexEncodedMethod[] newMethods = new DexEncodedMethod[virtualMethods.length + 1]; System.arraycopy(virtualMethods, 0, newMethods, 0, virtualMethods.length);
diff --git a/src/main/java/com/android/tools/r8/graph/DexDefinition.java b/src/main/java/com/android/tools/r8/graph/DexDefinition.java index 9127b94..82dfc12 100644 --- a/src/main/java/com/android/tools/r8/graph/DexDefinition.java +++ b/src/main/java/com/android/tools/r8/graph/DexDefinition.java
@@ -12,16 +12,6 @@ */ public abstract class DexDefinition extends DexItem { - @Override - public boolean isDexDefinition() { - return true; - } - - @Override - public DexDefinition asDexDefinition() { - return this; - } - public boolean isDexClass() { return false; }
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java index 1764634..a920005 100644 --- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java +++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -16,6 +16,7 @@ import com.android.tools.r8.ir.optimize.info.DefaultFieldOptimizationInfo; import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo; import com.android.tools.r8.ir.optimize.info.MutableFieldOptimizationInfo; +import com.android.tools.r8.kotlin.KotlinMemberInfo; import com.android.tools.r8.shaking.AppInfoWithLiveness; public class DexEncodedField extends KeyedDexItem<DexField> { @@ -27,6 +28,7 @@ private DexValue staticValue; private FieldOptimizationInfo optimizationInfo = DefaultFieldOptimizationInfo.getInstance(); + private KotlinMemberInfo kotlinMemberInfo = KotlinMemberInfo.getNoKotlinMemberInfo(); public DexEncodedField( DexField field, @@ -65,6 +67,19 @@ optimizationInfo = info; } + public KotlinMemberInfo getKotlinMemberInfo() { + return kotlinMemberInfo; + } + + public void setKotlinMemberInfo(KotlinMemberInfo kotlinMemberInfo) { + assert this.kotlinMemberInfo == KotlinMemberInfo.getNoKotlinMemberInfo(); + this.kotlinMemberInfo = kotlinMemberInfo; + } + + public boolean isKotlinBackingField() { + return kotlinMemberInfo.memberKind.isBackingField(); + } + @Override public void collectIndexedItems( IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
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 2ecc549..ff31e35 100644 --- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java +++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -54,7 +54,7 @@ import com.android.tools.r8.ir.synthetic.FieldAccessorSourceCode; import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode; import com.android.tools.r8.ir.synthetic.SynthesizedCode; -import com.android.tools.r8.kotlin.KotlinMetadataSynthesizer; +import com.android.tools.r8.kotlin.KotlinMemberInfo; import com.android.tools.r8.logging.Log; import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.naming.MemberNaming.MethodSignature; @@ -74,9 +74,6 @@ import java.util.Map; import java.util.function.Consumer; import java.util.function.IntPredicate; -import kotlinx.metadata.KmConstructor; -import kotlinx.metadata.KmFunction; -import kotlinx.metadata.KmProperty; import org.objectweb.asm.Opcodes; public class DexEncodedMethod extends KeyedDexItem<DexMethod> { @@ -138,6 +135,7 @@ private MethodOptimizationInfo optimizationInfo = DefaultMethodOptimizationInfo.DEFAULT_INSTANCE; private CallSiteOptimizationInfo callSiteOptimizationInfo = CallSiteOptimizationInfo.BOTTOM; private int classFileVersion; + private KotlinMemberInfo kotlinMemberInfo = KotlinMemberInfo.getNoKotlinMemberInfo(); private DexEncodedMethod defaultInterfaceMethodImplementation = null; @@ -394,59 +392,42 @@ return accessFlags.isSynthetic(); } - // TODO(b/70169921): Handling JVM extensions as well. - KmConstructor findCompatibleKotlinConstructor( - List<KmConstructor> constructors, AppView<?> appView) { - if (!isInstanceInitializer()) { - return null; - } - for (KmConstructor constructor : constructors) { - if (KotlinMetadataSynthesizer.isCompatibleConstructor(constructor, this, appView)) { - return constructor; - } - } - return null; + public KotlinMemberInfo getKotlinMemberInfo() { + return kotlinMemberInfo; } - // TODO(b/70169921): Handling JVM extensions as well. - KmFunction findCompatibleKotlinExtension(List<KmFunction> extensions, AppView<?> appView) { - if (!isStaticMember()) { - return null; + public void setKotlinMemberInfo(KotlinMemberInfo kotlinMemberInfo) { + if (this.kotlinMemberInfo == KotlinMemberInfo.getNoKotlinMemberInfo()) { + // Initial setup or structure-changing optimizations that just need to copy metadata from the + // old instance of DexEncodedMethod to the new one. + this.kotlinMemberInfo = kotlinMemberInfo; + } else { + // Structure-changing optimizations, such as (vertical|horizontal) merger or inliner, that + // may need to redefine what this method is. Simply, the method merged/inlined by optimization + // is no longer what it used to be; it's safe to ignore metadata of that method, since it is + // not asked to be kept. But, the nature of the current one is not changed, hence keeping the + // original one as-is. + // E.g., originally the current method is extension function, and new information, say, from + // an inlinee, is extension property. Being merged here means: + // * That inlinee is not an extension property anymore. We can ignore metadata from it. + // * This method is still an extension function, just with a bigger body. } - for (KmFunction extension : extensions) { - if (KotlinMetadataSynthesizer.isCompatibleExtension(extension, this, appView)) { - return extension; - } - } - return null; } - KmFunction findCompatibleKotlinFunction(List<KmFunction> functions, AppView<?> appView) { - if (isStaticMember()) { - return null; - } - for (KmFunction function : functions) { - if (KotlinMetadataSynthesizer.isCompatibleFunction(function, this, appView)) { - return function; - } - } - return null; + public boolean isKotlinFunction() { + return kotlinMemberInfo.memberKind.isFunction(); } - boolean isKotlinProperty(List<KmProperty> properties, AppView<?> appView) { - return findCompatibleKotlinProperty(properties, appView) != null; + public boolean isKotlinExtensionFunction() { + return kotlinMemberInfo.memberKind.isExtensionFunction(); } - KmProperty findCompatibleKotlinProperty(List<KmProperty> properties, AppView<?> appView) { - if (isStaticMember()) { - return null; - } - for (KmProperty property : properties) { - if (KotlinMetadataSynthesizer.isCompatibleProperty(property, this, appView)) { - return property; - } - } - return null; + public boolean isKotlinProperty() { + return kotlinMemberInfo.memberKind.isProperty(); + } + + public boolean isKotlinExtensionProperty() { + return kotlinMemberInfo.memberKind.isExtensionProperty(); } public boolean isOnlyInlinedIntoNestMembers() { @@ -1255,6 +1236,7 @@ public void copyMetadata(DexEncodedMethod from) { checkIfObsolete(); + setKotlinMemberInfo(from.kotlinMemberInfo); // Record that the current method uses identifier name string if the inlinee did so. if (from.getOptimizationInfo().useIdentifierNameString()) { getMutableOptimizationInfo().markUseIdentifierNameString(); @@ -1281,6 +1263,7 @@ private Code code; private CompilationState compilationState; private MethodOptimizationInfo optimizationInfo; + private KotlinMemberInfo kotlinMemberInfo; private final int classFileVersion; private boolean d8R8Synthesized; @@ -1296,6 +1279,7 @@ code = from.code; compilationState = from.compilationState; optimizationInfo = from.optimizationInfo.mutableCopy(); + kotlinMemberInfo = from.kotlinMemberInfo; classFileVersion = from.classFileVersion; this.d8R8Synthesized = d8R8Synthesized; @@ -1390,6 +1374,7 @@ code, classFileVersion, d8R8Synthesized); + result.setKotlinMemberInfo(kotlinMemberInfo); result.compilationState = compilationState; result.optimizationInfo = optimizationInfo; return result;
diff --git a/src/main/java/com/android/tools/r8/graph/DexItem.java b/src/main/java/com/android/tools/r8/graph/DexItem.java index 8aa09b6..eb8e150 100644 --- a/src/main/java/com/android/tools/r8/graph/DexItem.java +++ b/src/main/java/com/android/tools/r8/graph/DexItem.java
@@ -64,22 +64,6 @@ return toString(); } - public boolean isDexReference() { - return false; - } - - public DexReference asDexReference() { - return null; - } - - public boolean isDexDefinition() { - return false; - } - - public DexDefinition asDexDefinition() { - return null; - } - static <T extends DexItem> Stream<T> filter(Stream<DexItem> stream, Class<T> clazz) { return stream.filter(clazz::isInstance).map(clazz::cast); }
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 af7dc20..d51810e 100644 --- a/src/main/java/com/android/tools/r8/graph/DexReference.java +++ b/src/main/java/com/android/tools/r8/graph/DexReference.java
@@ -13,16 +13,6 @@ */ public abstract class DexReference extends IndexedDexItem { - @Override - public boolean isDexReference() { - return true; - } - - @Override - public DexReference asDexReference() { - return this; - } - public boolean isDexType() { return false; }
diff --git a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java index 7e98588..23f2a61 100644 --- a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java +++ b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
@@ -71,9 +71,6 @@ public abstract DexEncodedMethod lookupInvokeDirectTarget( DexProgramClass context, AppInfoWithClassHierarchy appInfo); - @Deprecated - public abstract DexEncodedMethod lookupInvokeSuperTarget(DexClass context, AppInfo appInfo); - /** Lookup the single target of an invoke-static on this resolution result if possible. */ public abstract DexEncodedMethod lookupInvokeStaticTarget( DexProgramClass context, AppInfoWithClassHierarchy appInfo); @@ -186,10 +183,19 @@ @Override public DexEncodedMethod lookupInvokeSuperTarget( DexProgramClass context, AppInfoWithClassHierarchy appInfo) { + // TODO(b/147848950): Investigate and remove the Compilation error. It could compile to + // throw IAE. + if (resolvedMethod.isInstanceInitializer() + || (appInfo.hasSubtyping() + && initialResolutionHolder != context + && !isSuperclass(initialResolutionHolder, context, appInfo.withSubtyping()))) { + throw new CompilationError( + "Illegal invoke-super to " + resolvedMethod.toSourceString(), context.getOrigin()); + } if (!isAccessibleFrom(context, appInfo)) { return null; } - DexEncodedMethod target = lookupInvokeSuperTarget(context.asDexClass(), appInfo); + DexEncodedMethod target = internalInvokeSpecialOrSuper(context, appInfo, (sup, sub) -> true); if (target == null) { return null; } @@ -244,21 +250,10 @@ return null; } - @Override - public DexEncodedMethod lookupInvokeSuperTarget(DexClass context, AppInfo appInfo) { - assert context != null; - if (resolvedMethod.isInstanceInitializer() - || (appInfo.hasSubtyping() - && initialResolutionHolder != context - && !isSuperclass(initialResolutionHolder, context, appInfo.withSubtyping()))) { - throw new CompilationError( - "Illegal invoke-super to " + resolvedMethod.toSourceString(), context.getOrigin()); - } - return internalInvokeSpecialOrSuper(context, appInfo, (sup, sub) -> true); - } - private DexEncodedMethod internalInvokeSpecialOrSuper( - DexClass context, AppInfo appInfo, BiPredicate<DexClass, DexClass> isSuperclass) { + DexClass context, + AppInfoWithClassHierarchy appInfo, + BiPredicate<DexClass, DexClass> isSuperclass) { // Statics cannot be targeted by invoke-special/super. if (getResolvedMethod().isStatic()) { @@ -455,11 +450,6 @@ } @Override - public final DexEncodedMethod lookupInvokeSuperTarget(DexClass context, AppInfo appInfo) { - return null; - } - - @Override public DexEncodedMethod lookupInvokeStaticTarget(DexProgramClass context, AppInfoWithClassHierarchy appInfo) { return null;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java index 17b91f3..e9060a4 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
@@ -58,13 +58,13 @@ OptimizationFeedback feedback, DexProgramClass clazz, DexEncodedMethod method) { + assert clazz != null; assert clazz.type == method.method.holder; this.appView = appView; this.clazz = clazz; this.code = code; this.feedback = feedback; this.method = method; - assert this.clazz != null; } public static void run( @@ -261,9 +261,14 @@ // Record that this block reads all fields. result.put(block, UnknownFieldSet.getInstance()); changed = true; - } else if (knownReadSet.size() != oldSize) { - assert knownReadSet.size() > oldSize; - changed = true; + } else { + if (knownReadSet != readSet) { + result.put(block, knownReadSet.asConcreteFieldSet()); + } + if (knownReadSet.size() != oldSize) { + assert knownReadSet.size() > oldSize; + changed = true; + } } if (changed) { @@ -278,7 +283,12 @@ // Abstract value. Value root = value.getAliasedValue(); AbstractValue abstractValue = computeAbstractValue(root); - if (!abstractValue.isUnknown()) { + if (abstractValue.isUnknown()) { + if (field.isStatic()) { + feedback.recordFieldHasAbstractValue( + field, appView, appView.abstractValueFactory().createSingleFieldValue(field.field)); + } + } else { feedback.recordFieldHasAbstractValue(field, appView, abstractValue); }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java index d1cf2e5..e2881ad 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
@@ -4,14 +4,30 @@ package com.android.tools.r8.ir.analysis.proto; +import com.android.tools.r8.graph.AppInfoWithClassHierarchy; import com.android.tools.r8.graph.AppInfoWithSubtyping; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexEncodedMethod; -import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement; +import com.android.tools.r8.ir.analysis.type.TypeAnalysis; +import com.android.tools.r8.ir.code.CheckCast; +import com.android.tools.r8.ir.code.IRCode; +import com.android.tools.r8.ir.code.Instruction; +import com.android.tools.r8.ir.code.InstructionListIterator; +import com.android.tools.r8.ir.code.InvokeVirtual; +import com.android.tools.r8.ir.code.Value; import com.android.tools.r8.ir.conversion.CallGraph.Node; +import com.android.tools.r8.ir.conversion.MethodProcessor; +import com.android.tools.r8.ir.optimize.CodeRewriter; +import com.android.tools.r8.ir.optimize.Inliner; +import com.android.tools.r8.ir.optimize.Inliner.Reason; +import com.android.tools.r8.ir.optimize.info.OptimizationFeedback; +import com.android.tools.r8.ir.optimize.inliner.FixedInliningReasonStrategy; +import com.android.tools.r8.utils.PredicateSet; +import com.google.common.collect.Sets; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -22,9 +38,12 @@ // references a dead proto builder. public class GeneratedMessageLiteBuilderShrinker { + private final AppView<? extends AppInfoWithSubtyping> appView; private final ProtoReferences references; - GeneratedMessageLiteBuilderShrinker(ProtoReferences references) { + GeneratedMessageLiteBuilderShrinker( + AppView<? extends AppInfoWithSubtyping> appView, ProtoReferences references) { + this.appView = appView; this.references = references; } @@ -39,10 +58,19 @@ public static void addInliningHeuristicsForBuilderInlining( AppView<? extends AppInfoWithSubtyping> appView, + PredicateSet<DexType> alwaysClassInline, + Set<DexType> neverMerge, Set<DexMethod> alwaysInline, Set<DexMethod> neverInline, Set<DexMethod> bypassClinitforInlining) { - new RootSetExtension(appView, alwaysInline, neverInline, bypassClinitforInlining).extend(); + new RootSetExtension( + appView, + alwaysClassInline, + neverMerge, + alwaysInline, + neverInline, + bypassClinitforInlining) + .extend(); } public void preprocessCallGraphBeforeCycleElimination(Map<DexMethod, Node> nodes) { @@ -60,38 +88,147 @@ } } + public void inlineCallsToDynamicMethod( + DexEncodedMethod method, + IRCode code, + CodeRewriter codeRewriter, + OptimizationFeedback feedback, + MethodProcessor methodProcessor, + Inliner inliner) { + if (method.method.toSourceString().contains("proto2.BuilderWithReusedSettersTestClass")) { + System.out.println(); + } + strengthenCheckCastInstructions(code); + + ProtoInliningReasonStrategy inliningReasonStrategy = + new ProtoInliningReasonStrategy(appView, new FixedInliningReasonStrategy(Reason.NEVER)); + inliner.performInlining(method, code, feedback, methodProcessor, inliningReasonStrategy); + + // Run the enum optimization to optimize all Enum.ordinal() invocations. This is required to + // get rid of the enum switch in dynamicMethod(). + if (appView.options().enableEnumValueOptimization) { + codeRewriter.rewriteConstantEnumMethodCalls(code); + } + } + + /** + * This method tries to strengthen the type of check-cast instructions that cast a value to + * GeneratedMessageLite. + * + * <p>New proto messages are created by calling dynamicMethod(MethodToInvoke.NEW_MUTABLE_INSTANCE) + * and casting the result to GeneratedMessageLite. + * + * <p>If we encounter the following pattern, then we cannot inline the second call to + * dynamicMethod, because we don't have a precise receiver type. + * + * <pre> + * GeneratedMessageLite msg = + * (GeneratedMessageLite) + * Message.DEFAULT_INSTANCE.dynamicMethod(MethodToInvoke.NEW_MUTABLE_INSTANCE); + * GeneratedMessageLite msg2 = + * (GeneratedMessageLite) msg.dynamicMethod(MethodToInvoke.NEW_MUTABLE_INSTANCE); + * </pre> + * + * <p>This method therefore optimizes the code above into: + * + * <pre> + * Message msg = + * (Message) Message.DEFAULT_INSTANCE.dynamicMethod(MethodToInvoke.NEW_MUTABLE_INSTANCE); + * Message msg2 = (Message) msg.dynamicMethod(MethodToInvoke.NEW_MUTABLE_INSTANCE); + * </pre> + * + * <p>This is assuming that calling dynamicMethod() on a proto message with + * MethodToInvoke.NEW_MUTABLE_INSTANCE will create an instance of the enclosing class. + */ + private void strengthenCheckCastInstructions(IRCode code) { + Set<Value> affectedValues = Sets.newIdentityHashSet(); + InstructionListIterator instructionIterator = code.instructionListIterator(); + CheckCast checkCast; + while ((checkCast = instructionIterator.nextUntil(Instruction::isCheckCast)) != null) { + if (checkCast.getType() != references.generatedMessageLiteType) { + continue; + } + Value root = checkCast.object().getAliasedValue(); + if (root.isPhi() || !root.definition.isInvokeVirtual()) { + continue; + } + InvokeVirtual invoke = root.definition.asInvokeVirtual(); + DexMethod invokedMethod = invoke.getInvokedMethod(); + if (!references.isDynamicMethod(invokedMethod) + && !references.isDynamicMethodBridge(invokedMethod)) { + continue; + } + assert invokedMethod.proto.parameters.values[0] == references.methodToInvokeType; + Value methodToInvokeValue = invoke.arguments().get(1); + if (!references.methodToInvokeMembers.isNewMutableInstanceEnum(methodToInvokeValue)) { + continue; + } + ClassTypeLatticeElement receiverType = + invoke.getReceiver().getDynamicUpperBoundType(appView).asClassTypeLatticeElement(); + if (receiverType != null) { + AppInfoWithClassHierarchy appInfo = appView.appInfo(); + DexType rawReceiverType = receiverType.getClassType(); + if (appInfo.isStrictSubtypeOf(rawReceiverType, references.generatedMessageLiteType)) { + Value dest = code.createValue(receiverType.asMaybeNull(), checkCast.getLocalInfo()); + CheckCast replacement = new CheckCast(dest, checkCast.object(), rawReceiverType); + instructionIterator.replaceCurrentInstruction(replacement, affectedValues); + } + } + } + if (!affectedValues.isEmpty()) { + new TypeAnalysis(appView).narrowing(affectedValues); + } + } + private static class RootSetExtension { private final AppView<? extends AppInfoWithSubtyping> appView; private final ProtoReferences references; + private final PredicateSet<DexType> alwaysClassInline; + private final Set<DexType> neverMerge; + private final Set<DexMethod> alwaysInline; private final Set<DexMethod> neverInline; private final Set<DexMethod> bypassClinitforInlining; RootSetExtension( AppView<? extends AppInfoWithSubtyping> appView, + PredicateSet<DexType> alwaysClassInline, + Set<DexType> neverMerge, Set<DexMethod> alwaysInline, Set<DexMethod> neverInline, Set<DexMethod> bypassClinitforInlining) { this.appView = appView; this.references = appView.protoShrinker().references; + this.alwaysClassInline = alwaysClassInline; + this.neverMerge = neverMerge; this.alwaysInline = alwaysInline; this.neverInline = neverInline; this.bypassClinitforInlining = bypassClinitforInlining; } void extend() { + alwaysClassInlineGeneratedMessageLiteBuilders(); + // GeneratedMessageLite heuristics. alwaysInlineCreateBuilderFromGeneratedMessageLite(); neverInlineIsInitializedFromGeneratedMessageLite(); // * extends GeneratedMessageLite heuristics. bypassClinitforInliningNewBuilderMethods(); - alwaysInlineDynamicMethodFromGeneratedMessageLiteImplementations(); // GeneratedMessageLite$Builder heuristics. - alwaysInlineBuildPartialFromGeneratedMessageLiteBuilder(); + alwaysInlineBuildPartialFromGeneratedMessageLiteExtendableBuilder(); + neverMergeGeneratedMessageLiteBuilder(); + } + + private void alwaysClassInlineGeneratedMessageLiteBuilders() { + alwaysClassInline.addPredicate( + type -> + appView + .appInfo() + .isStrictSubtypeOf(type, references.generatedMessageLiteBuilderType)); } private void bypassClinitforInliningNewBuilderMethods() { @@ -108,29 +245,20 @@ } } - private void alwaysInlineBuildPartialFromGeneratedMessageLiteBuilder() { - alwaysInline.add(references.generatedMessageLiteBuilderMethods.buildPartialMethod); + private void alwaysInlineBuildPartialFromGeneratedMessageLiteExtendableBuilder() { + alwaysInline.add(references.generatedMessageLiteExtendableBuilderMethods.buildPartialMethod); } private void alwaysInlineCreateBuilderFromGeneratedMessageLite() { alwaysInline.add(references.generatedMessageLiteMethods.createBuilderMethod); } - private void alwaysInlineDynamicMethodFromGeneratedMessageLiteImplementations() { - // TODO(b/132600418): We should be able to determine that dynamicMethod() becomes 'SIMPLE' - // when the MethodToInvoke argument is MethodToInvoke.NEW_BUILDER. - DexItemFactory dexItemFactory = appView.dexItemFactory(); - for (DexType type : appView.appInfo().subtypes(references.generatedMessageLiteType)) { - alwaysInline.add( - dexItemFactory.createMethod( - type, - dexItemFactory.createProto( - dexItemFactory.objectType, - references.methodToInvokeType, - dexItemFactory.objectType, - dexItemFactory.objectType), - "dynamicMethod")); - } + private void neverMergeGeneratedMessageLiteBuilder() { + // For consistency, never merge the GeneratedMessageLite builders. These will only have a + // unique subtype if the application has a single proto message, which mostly happens during + // testing. + neverMerge.add(references.generatedMessageLiteBuilderType); + neverMerge.add(references.generatedMessageLiteExtendableBuilderType); } /**
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoInliningReasonStrategy.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoInliningReasonStrategy.java index 4d04a73..de37f38 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoInliningReasonStrategy.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoInliningReasonStrategy.java
@@ -7,6 +7,7 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.ir.analysis.proto.ProtoReferences.MethodToInvokeMembers; import com.android.tools.r8.ir.code.Instruction; import com.android.tools.r8.ir.code.InvokeMethod; @@ -22,19 +23,30 @@ private static final int METHOD_TO_INVOKE_ARGUMENT_POSITION_IN_DYNAMIC_METHOD = 1; + private final AppView<?> appView; private final InliningReasonStrategy parent; private final ProtoReferences references; public ProtoInliningReasonStrategy(AppView<?> appView, InliningReasonStrategy parent) { + this.appView = appView; this.parent = parent; this.references = appView.protoShrinker().references; } @Override - public Reason computeInliningReason(InvokeMethod invoke, DexEncodedMethod target) { - return references.isDynamicMethod(target) + public Reason computeInliningReason( + InvokeMethod invoke, DexEncodedMethod target, DexEncodedMethod context) { + DexProgramClass enclosingClass = appView.definitionFor(context.method.holder).asProgramClass(); + if (references.isAbstractGeneratedMessageLiteBuilder(enclosingClass) + && invoke.isInvokeSuper()) { + // Aggressively inline invoke-super calls inside the GeneratedMessageLite builders. Such + // instructions prohibit inlining of the enclosing method into other contexts, and therefore + // block class inlining of proto builders. + return Reason.ALWAYS; + } + return references.isDynamicMethod(target) || references.isDynamicMethodBridge(target) ? computeInliningReasonForDynamicMethod(invoke) - : parent.computeInliningReason(invoke, target); + : parent.computeInliningReason(invoke, target, context); } private Reason computeInliningReasonForDynamicMethod(InvokeMethod invoke) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java index c90eaf2..aacb2a0 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
@@ -12,6 +12,7 @@ import com.android.tools.r8.graph.DexProto; import com.android.tools.r8.graph.DexString; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.ir.code.Value; public class ProtoReferences { @@ -20,12 +21,15 @@ public final DexType generatedExtensionType; public final DexType generatedMessageLiteType; public final DexType generatedMessageLiteBuilderType; + public final DexType generatedMessageLiteExtendableBuilderType; public final DexType rawMessageInfoType; public final DexType messageLiteType; public final DexType methodToInvokeType; public final GeneratedMessageLiteMethods generatedMessageLiteMethods; public final GeneratedMessageLiteBuilderMethods generatedMessageLiteBuilderMethods; + public final GeneratedMessageLiteExtendableBuilderMethods + generatedMessageLiteExtendableBuilderMethods; public final MethodToInvokeMembers methodToInvokeMembers; public final DexString dynamicMethodName; @@ -53,6 +57,9 @@ generatedMessageLiteBuilderType = factory.createType( factory.createString("Lcom/google/protobuf/GeneratedMessageLite$Builder;")); + generatedMessageLiteExtendableBuilderType = + factory.createType( + factory.createString("Lcom/google/protobuf/GeneratedMessageLite$ExtendableBuilder;")); rawMessageInfoType = factory.createType(factory.createString("Lcom/google/protobuf/RawMessageInfo;")); messageLiteType = factory.createType(factory.createString("Lcom/google/protobuf/MessageLite;")); @@ -88,9 +95,16 @@ generatedMessageLiteMethods = new GeneratedMessageLiteMethods(factory); generatedMessageLiteBuilderMethods = new GeneratedMessageLiteBuilderMethods(factory); + generatedMessageLiteExtendableBuilderMethods = + new GeneratedMessageLiteExtendableBuilderMethods(factory); methodToInvokeMembers = new MethodToInvokeMembers(factory); } + public boolean isAbstractGeneratedMessageLiteBuilder(DexProgramClass clazz) { + return clazz.type == generatedMessageLiteBuilderType + || clazz.type == generatedMessageLiteExtendableBuilderType; + } + public boolean isDynamicMethod(DexMethod method) { return method.name == dynamicMethodName && method.proto == dynamicMethodProto; } @@ -113,7 +127,9 @@ } public boolean isGeneratedMessageLiteBuilder(DexProgramClass clazz) { - return clazz.superType == generatedMessageLiteBuilderType; + return (clazz.superType == generatedMessageLiteBuilderType + || clazz.superType == generatedMessageLiteExtendableBuilderType) + && !isAbstractGeneratedMessageLiteBuilder(clazz); } public boolean isMessageInfoConstructionMethod(DexMethod method) { @@ -164,6 +180,19 @@ } } + class GeneratedMessageLiteExtendableBuilderMethods { + + public final DexMethod buildPartialMethod; + + private GeneratedMessageLiteExtendableBuilderMethods(DexItemFactory dexItemFactory) { + buildPartialMethod = + dexItemFactory.createMethod( + generatedMessageLiteExtendableBuilderType, + dexItemFactory.createProto(extendableMessageType), + "buildPartial"); + } + } + public class MethodToInvokeMembers { public final DexField buildMessageInfoField; @@ -195,6 +224,17 @@ methodToInvokeType, methodToInvokeType, "SET_MEMOIZED_IS_INITIALIZED"); } + public boolean isNewMutableInstanceEnum(DexField field) { + return field == newMutableInstanceField; + } + + public boolean isNewMutableInstanceEnum(Value value) { + Value root = value.getAliasedValue(); + return !root.isPhi() + && root.definition.isStaticGet() + && isNewMutableInstanceEnum(root.definition.asStaticGet().getField()); + } + public boolean isMethodToInvokeWithSimpleBody(DexField field) { return field == getDefaultInstanceField || field == getMemoizedIsInitializedField
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoShrinker.java index d7ba0cc..b7b8ef1 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoShrinker.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoShrinker.java
@@ -32,7 +32,7 @@ : null; this.generatedMessageLiteBuilderShrinker = appView.options().protoShrinking().enableGeneratedMessageLiteBuilderShrinking - ? new GeneratedMessageLiteBuilderShrinker(references) + ? new GeneratedMessageLiteBuilderShrinker(appView, references) : null; this.references = references; }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeLatticeElement.java index 91086e1..b888ae5 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeLatticeElement.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeLatticeElement.java
@@ -83,6 +83,10 @@ return getOrCreateVariant(Nullability.definitelyNotNull()); } + public TypeLatticeElement asMaybeNull() { + return getOrCreateVariant(Nullability.maybeNull()); + } + @Override public boolean isReference() { return true;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java index f560b29..2bd87e0 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
@@ -28,6 +28,14 @@ return null; } + public boolean isSingleFieldValue() { + return false; + } + + public SingleFieldValue asSingleFieldValue() { + return null; + } + public boolean isSingleNumberValue() { return false; }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueFactory.java b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueFactory.java index 7e34815..887a171 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueFactory.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueFactory.java
@@ -11,6 +11,8 @@ public class AbstractValueFactory { private ConcurrentHashMap<DexField, SingleEnumValue> singleEnumValues = new ConcurrentHashMap<>(); + private ConcurrentHashMap<DexField, SingleFieldValue> singleFieldValues = + new ConcurrentHashMap<>(); private ConcurrentHashMap<Long, SingleNumberValue> singleNumberValues = new ConcurrentHashMap<>(); private ConcurrentHashMap<DexString, SingleStringValue> singleStringValues = new ConcurrentHashMap<>(); @@ -19,6 +21,10 @@ return singleEnumValues.computeIfAbsent(field, SingleEnumValue::new); } + public SingleFieldValue createSingleFieldValue(DexField field) { + return singleFieldValues.computeIfAbsent(field, SingleFieldValue::new); + } + public SingleNumberValue createSingleNumberValue(long value) { return singleNumberValues.computeIfAbsent(value, SingleNumberValue::new); }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleEnumValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleEnumValue.java index 1f7a76b..313f977 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleEnumValue.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleEnumValue.java
@@ -4,28 +4,13 @@ package com.android.tools.r8.ir.analysis.value; -import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull; -import static com.android.tools.r8.optimize.MemberRebindingAnalysis.isMemberVisibleFromOriginalContext; - -import com.android.tools.r8.graph.AppInfoWithSubtyping; -import com.android.tools.r8.graph.AppView; -import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexField; -import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.ir.analysis.type.TypeLatticeElement; -import com.android.tools.r8.ir.code.IRCode; -import com.android.tools.r8.ir.code.Instruction; -import com.android.tools.r8.ir.code.StaticGet; -import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier; -import com.android.tools.r8.ir.code.Value; -public class SingleEnumValue extends SingleValue { - - private final DexField field; +public class SingleEnumValue extends SingleFieldValue { /** Intentionally package private, use {@link AbstractValueFactory} instead. */ SingleEnumValue(DexField field) { - this.field = field; + super(field); } @Override @@ -39,35 +24,7 @@ } @Override - public boolean equals(Object o) { - return this == o; - } - - @Override - public int hashCode() { - return System.identityHashCode(this); - } - - @Override public String toString() { - return "SingleEnumValue(" + field.toSourceString() + ")"; - } - - @Override - public Instruction createMaterializingInstruction( - AppView<? extends AppInfoWithSubtyping> appView, - IRCode code, - TypeAndLocalInfoSupplier info) { - TypeLatticeElement type = TypeLatticeElement.fromDexType(field.type, maybeNull(), appView); - assert type.lessThanOrEqual(info.getTypeLattice(), appView); - Value outValue = code.createValue(type, info.getLocalInfo()); - return new StaticGet(outValue, field); - } - - @Override - public boolean isMaterializableInContext(AppView<?> appView, DexType context) { - DexEncodedField encodedField = appView.appInfo().resolveField(field); - return isMemberVisibleFromOriginalContext( - appView, context, encodedField.field.holder, encodedField.accessFlags); + return "SingleEnumValue(" + getField().toSourceString() + ")"; } }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java new file mode 100644 index 0000000..0adfe65 --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.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.ir.analysis.value; + +import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull; +import static com.android.tools.r8.optimize.MemberRebindingAnalysis.isMemberVisibleFromOriginalContext; + +import com.android.tools.r8.graph.AppInfoWithSubtyping; +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexEncodedField; +import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.ir.analysis.type.TypeLatticeElement; +import com.android.tools.r8.ir.code.IRCode; +import com.android.tools.r8.ir.code.Instruction; +import com.android.tools.r8.ir.code.StaticGet; +import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier; +import com.android.tools.r8.ir.code.Value; + +public class SingleFieldValue extends SingleValue { + + private final DexField field; + + /** Intentionally package private, use {@link AbstractValueFactory} instead. */ + SingleFieldValue(DexField field) { + this.field = field; + } + + public DexField getField() { + return field; + } + + @Override + public boolean isSingleFieldValue() { + return true; + } + + @Override + public SingleFieldValue asSingleFieldValue() { + return this; + } + + @Override + public boolean equals(Object o) { + return this == o; + } + + @Override + public int hashCode() { + return System.identityHashCode(this); + } + + @Override + public String toString() { + return "SingleFieldValue(" + field.toSourceString() + ")"; + } + + @Override + public Instruction createMaterializingInstruction( + AppView<? extends AppInfoWithSubtyping> appView, IRCode code, TypeAndLocalInfoSupplier info) { + TypeLatticeElement type = TypeLatticeElement.fromDexType(field.type, maybeNull(), appView); + assert type.lessThanOrEqual(info.getTypeLattice(), appView); + Value outValue = code.createValue(type, info.getLocalInfo()); + return new StaticGet(outValue, field); + } + + @Override + public boolean isMaterializableInContext(AppView<?> appView, DexType context) { + DexEncodedField encodedField = appView.appInfo().resolveField(field); + return isMemberVisibleFromOriginalContext( + appView, context, encodedField.field.holder, encodedField.accessFlags); + } +}
diff --git a/src/main/java/com/android/tools/r8/ir/code/AliasedValueConfiguration.java b/src/main/java/com/android/tools/r8/ir/code/AliasedValueConfiguration.java new file mode 100644 index 0000000..008d363 --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/code/AliasedValueConfiguration.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.ir.code; + +public interface AliasedValueConfiguration { + + boolean isIntroducingAnAlias(Instruction instruction); + + Value getAliasForOutValue(Instruction instruction); +}
diff --git a/src/main/java/com/android/tools/r8/ir/code/AssumeAndCheckCastAliasedValueConfiguration.java b/src/main/java/com/android/tools/r8/ir/code/AssumeAndCheckCastAliasedValueConfiguration.java new file mode 100644 index 0000000..8a6517b --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/code/AssumeAndCheckCastAliasedValueConfiguration.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.ir.code; + +import com.android.tools.r8.utils.ListUtils; + +public class AssumeAndCheckCastAliasedValueConfiguration implements AliasedValueConfiguration { + + private static final AssumeAndCheckCastAliasedValueConfiguration INSTANCE = + new AssumeAndCheckCastAliasedValueConfiguration(); + + private AssumeAndCheckCastAliasedValueConfiguration() {} + + public static AssumeAndCheckCastAliasedValueConfiguration getInstance() { + return INSTANCE; + } + + @Override + public boolean isIntroducingAnAlias(Instruction instruction) { + return instruction.isAssume() || instruction.isCheckCast(); + } + + @Override + public Value getAliasForOutValue(Instruction instruction) { + assert instruction.isAssume() || instruction.isCheckCast(); + return ListUtils.first(instruction.inValues()); + } +}
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java index 085c936..301be93 100644 --- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java +++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
@@ -181,7 +181,7 @@ } @Override - public void replaceCurrentInstruction(Instruction newInstruction) { + public void replaceCurrentInstruction(Instruction newInstruction, Set<Value> affectedValues) { if (current == null) { throw new IllegalStateException(); } @@ -190,6 +190,9 @@ } if (current.outValue() != null && current.outValue().isUsed()) { assert newInstruction.outValue() != null; + if (affectedValues != null) { + current.outValue().addAffectedValuesTo(affectedValues); + } current.outValue().replaceUsers(newInstruction.outValue()); } current.moveDebugValues(newInstruction);
diff --git a/src/main/java/com/android/tools/r8/ir/code/DefaultAliasedValueConfiguration.java b/src/main/java/com/android/tools/r8/ir/code/DefaultAliasedValueConfiguration.java new file mode 100644 index 0000000..b147deb --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/code/DefaultAliasedValueConfiguration.java
@@ -0,0 +1,28 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.ir.code; + +public class DefaultAliasedValueConfiguration implements AliasedValueConfiguration { + + private static final DefaultAliasedValueConfiguration INSTANCE = + new DefaultAliasedValueConfiguration(); + + private DefaultAliasedValueConfiguration() {} + + public static DefaultAliasedValueConfiguration getInstance() { + return INSTANCE; + } + + @Override + public boolean isIntroducingAnAlias(Instruction instruction) { + return instruction.isAssume(); + } + + @Override + public Value getAliasForOutValue(Instruction instruction) { + assert instruction.isAssume(); + return instruction.asAssume().src(); + } +}
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java index 1a34adf..9594d10 100644 --- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java +++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -1114,25 +1114,27 @@ return true; } - public void removeAllTrivialPhis() { - removeAllTrivialPhis(null, null); + public boolean removeAllTrivialPhis() { + return removeAllTrivialPhis(null, null); } - public void removeAllTrivialPhis(IRBuilder builder) { - removeAllTrivialPhis(builder, null); + public boolean removeAllTrivialPhis(IRBuilder builder) { + return removeAllTrivialPhis(builder, null); } - public void removeAllTrivialPhis(Set<Value> affectedValues) { - removeAllTrivialPhis(null, affectedValues); + public boolean removeAllTrivialPhis(Set<Value> affectedValues) { + return removeAllTrivialPhis(null, affectedValues); } - public void removeAllTrivialPhis(IRBuilder builder, Set<Value> affectedValues) { + public boolean removeAllTrivialPhis(IRBuilder builder, Set<Value> affectedValues) { + boolean anyTrivialPhisRemoved = false; for (BasicBlock block : blocks) { List<Phi> phis = new ArrayList<>(block.getPhis()); for (Phi phi : phis) { - phi.removeTrivialPhi(builder, affectedValues); + anyTrivialPhisRemoved |= phi.removeTrivialPhi(builder, affectedValues); } } + return anyTrivialPhisRemoved; } public int reserveMarkingColor() {
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java index fdea0f6..f070e0f 100644 --- a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java +++ b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
@@ -147,8 +147,8 @@ } @Override - public void replaceCurrentInstruction(Instruction newInstruction) { - instructionIterator.replaceCurrentInstruction(newInstruction); + public void replaceCurrentInstruction(Instruction newInstruction, Set<Value> affectedValues) { + instructionIterator.replaceCurrentInstruction(newInstruction, affectedValues); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java index 315568e..a8b539e 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java +++ b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
@@ -17,6 +17,11 @@ public interface InstructionListIterator extends InstructionIterator, ListIterator<Instruction>, PreviousUntilIterator<Instruction> { + /** See {@link #replaceCurrentInstruction(Instruction, Set)}. */ + default void replaceCurrentInstruction(Instruction newInstruction) { + replaceCurrentInstruction(newInstruction, null); + } + /** * Replace the current instruction (aka the {@link Instruction} returned by the previous call to * {@link #next} with the passed in <code>newInstruction</code>. @@ -31,8 +36,9 @@ * <p>The debug information of the current instruction will be attached to the new instruction. * * @param newInstruction the instruction to insert instead of the current. + * @param affectedValues if non-null, all users of the out value will be added to this set. */ - void replaceCurrentInstruction(Instruction newInstruction); + void replaceCurrentInstruction(Instruction newInstruction, Set<Value> affectedValues); // Do not show a deprecation warning for InstructionListIterator.remove(). @SuppressWarnings("deprecation")
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java index 6f79a82..2c7dcfb 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
@@ -67,8 +67,8 @@ if (!appView.appInfo().hasSubtyping()) { return returnTypeLattice; } - - List<DexType> lambdaInterfaces = LambdaDescriptor.getInterfaces(callSite, appView.appInfo()); + List<DexType> lambdaInterfaces = + LambdaDescriptor.getInterfaces(callSite, appView.appInfo().withSubtyping()); if (lambdaInterfaces == null || lambdaInterfaces.isEmpty()) { return returnTypeLattice; }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java index c8f8b49..5d782c3 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -32,7 +32,7 @@ public InvokeStatic(DexMethod target, Value result, List<Value> arguments) { this(target, result, arguments, false); - assert target.asDexReference().asDexMethod().proto.parameters.size() == arguments.size(); + assert target.proto.parameters.size() == arguments.size(); } public InvokeStatic(DexMethod target, Value result, List<Value> arguments, boolean itf) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java index 3a098ca..6de9e58 100644 --- a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java +++ b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
@@ -36,8 +36,8 @@ } @Override - public void replaceCurrentInstruction(Instruction newInstruction) { - currentBlockIterator.replaceCurrentInstruction(newInstruction); + public void replaceCurrentInstruction(Instruction newInstruction, Set<Value> affectedValues) { + currentBlockIterator.replaceCurrentInstruction(newInstruction, affectedValues); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/NextUntilIterator.java b/src/main/java/com/android/tools/r8/ir/code/NextUntilIterator.java index d15376e..cd08f22 100644 --- a/src/main/java/com/android/tools/r8/ir/code/NextUntilIterator.java +++ b/src/main/java/com/android/tools/r8/ir/code/NextUntilIterator.java
@@ -11,14 +11,15 @@ /** * Continue to call {@link #next} while {@code predicate} tests {@code false}. * - * @returns the item that matched the predicate or {@code null} if all items fail - * the predicate test + * @returns the item that matched the predicate or {@code null} if all items fail the predicate + * test */ - default T nextUntil(Predicate<T> predicate) { + @SuppressWarnings("unchecked") + default <S extends T> S nextUntil(Predicate<T> predicate) { while (hasNext()) { T item = next(); if (predicate.test(item)) { - return item; + return (S) item; } } return null;
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java index d6372b5..bba9148 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Value.java +++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -45,6 +45,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.function.Consumer; import java.util.function.Predicate; public class Value implements Comparable<Value> { @@ -264,25 +265,31 @@ * <p>This method is useful to find the "true" definition of a value inside the current method. */ public Value getAliasedValue() { - return getAliasedValue(Predicates.alwaysFalse()); + return getAliasedValue( + DefaultAliasedValueConfiguration.getInstance(), Predicates.alwaysFalse()); } - public Value getAliasedValue(Predicate<Value> stoppingCriterion) { + public Value getAliasedValue(AliasedValueConfiguration configuration) { + return getAliasedValue(configuration, Predicates.alwaysFalse()); + } + + public Value getAliasedValue( + AliasedValueConfiguration configuration, Predicate<Value> stoppingCriterion) { assert stoppingCriterion != null; Set<Value> visited = Sets.newIdentityHashSet(); Value lastAliasedValue; Value aliasedValue = this; do { - if (stoppingCriterion.test(aliasedValue)) { - return aliasedValue; - } lastAliasedValue = aliasedValue; if (aliasedValue.isPhi()) { return aliasedValue; } + if (stoppingCriterion.test(aliasedValue)) { + return aliasedValue; + } Instruction definitionOfAliasedValue = aliasedValue.definition; - if (definitionOfAliasedValue.isIntroducingAnAlias()) { - aliasedValue = definitionOfAliasedValue.getAliasForOutValue(); + if (configuration.isIntroducingAnAlias(definitionOfAliasedValue)) { + aliasedValue = configuration.getAliasForOutValue(definitionOfAliasedValue); // There shouldn't be a cycle. assert visited.add(aliasedValue); @@ -293,7 +300,8 @@ } public Value getSpecificAliasedValue(Predicate<Value> stoppingCriterion) { - Value aliasedValue = getAliasedValue(stoppingCriterion); + Value aliasedValue = + getAliasedValue(DefaultAliasedValueConfiguration.getInstance(), stoppingCriterion); return stoppingCriterion.test(aliasedValue) ? aliasedValue : null; } @@ -440,21 +448,29 @@ } public Set<Instruction> aliasedUsers() { + return aliasedUsers(DefaultAliasedValueConfiguration.getInstance()); + } + + public Set<Instruction> aliasedUsers(AliasedValueConfiguration configuration) { Set<Instruction> users = SetUtils.newIdentityHashSet(uniqueUsers()); Set<Instruction> visited = Sets.newIdentityHashSet(); - collectAliasedUsersViaAssume(visited, uniqueUsers(), users); + collectAliasedUsersViaAssume(configuration, visited, uniqueUsers(), users); return users; } private static void collectAliasedUsersViaAssume( - Set<Instruction> visited, Set<Instruction> usersToTest, Set<Instruction> collectedUsers) { + AliasedValueConfiguration configuration, + Set<Instruction> visited, + Set<Instruction> usersToTest, + Set<Instruction> collectedUsers) { for (Instruction user : usersToTest) { if (!visited.add(user)) { continue; } - if (user.isAssume()) { + if (configuration.isIntroducingAnAlias(user)) { collectedUsers.addAll(user.outValue().uniqueUsers()); - collectAliasedUsersViaAssume(visited, user.outValue().uniqueUsers(), collectedUsers); + collectAliasedUsersViaAssume( + configuration, visited, user.outValue().uniqueUsers(), collectedUsers); } } } @@ -627,13 +643,21 @@ // Returns the set of Value that are affected if the current value's type lattice is updated. public Set<Value> affectedValues() { ImmutableSet.Builder<Value> affectedValues = ImmutableSet.builder(); + forEachAffectedValue(affectedValues::add); + return affectedValues.build(); + } + + public void addAffectedValuesTo(Set<Value> affectedValues) { + forEachAffectedValue(affectedValues::add); + } + + public void forEachAffectedValue(Consumer<Value> consumer) { for (Instruction user : uniqueUsers()) { - if (user.outValue() != null) { - affectedValues.add(user.outValue()); + if (user.hasOutValue()) { + consumer.accept(user.outValue()); } } - affectedValues.addAll(uniquePhiUsers()); - return affectedValues.build(); + uniquePhiUsers().forEach(consumer::accept); } public void replaceUsers(Value newValue) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CodeOptimization.java b/src/main/java/com/android/tools/r8/ir/conversion/CodeOptimization.java index 8a40db4..ce571d6 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/CodeOptimization.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/CodeOptimization.java
@@ -6,7 +6,6 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.optimize.info.OptimizationFeedback; -import com.google.common.collect.ImmutableList; import java.util.Arrays; import java.util.Collection; import java.util.function.Consumer; @@ -41,42 +40,4 @@ } }; } - - /** - * Builder for {@link CodeOptimization}. - * - * Users can append either {@link CodeOptimization}, or simply {@link IRCode} consumer. - * - * Note that the order of everything that is appended through the builder matters. - */ - public static class Builder { - private ImmutableList.Builder<CodeOptimization> processingQueue; - - private Builder() { - processingQueue = ImmutableList.builder(); - } - - public static Builder builder() { - return new Builder(); - } - - public Builder addIRCodeConsumer(Consumer<IRCode> consumer) { - processingQueue.add(from(consumer)); - return this; - } - - public Builder addCodeOptimization(CodeOptimization optimization) { - processingQueue.add(optimization); - return this; - } - - public CodeOptimization build() { - return (code, feedback, methodProcessor) -> { - processingQueue - .build() - .forEach(codeOptimization -> - codeOptimization.optimize(code, feedback, methodProcessor)); - }; - } - } }
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 ace131b..028a397 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
@@ -193,7 +193,7 @@ this.printer = printer; this.mainDexClasses = mainDexClasses.getClasses(); this.codeRewriter = new CodeRewriter(appView, this); - this.constantCanonicalizer = new ConstantCanonicalizer(); + this.constantCanonicalizer = new ConstantCanonicalizer(codeRewriter); this.classInitializerDefaultsOptimization = options.debug ? null : new ClassInitializerDefaultsOptimization(appView, this); this.stringConcatRewriter = new StringConcatRewriter(appView); @@ -255,10 +255,7 @@ ((options.desugarState == DesugarState.ON) && enableTwrCloseResourceDesugaring()) ? new TwrCloseResourceRewriter(appView, this) : null; - this.backportedMethodRewriter = - options.desugarState == DesugarState.ON - ? new BackportedMethodRewriter(appView, this) - : null; + this.backportedMethodRewriter = new BackportedMethodRewriter(appView, this); this.covariantReturnTypeAnnotationTransformer = options.processCovariantReturnTypeAnnotations ? new CovariantReturnTypeAnnotationTransformer(this, appView.dexItemFactory()) @@ -444,9 +441,7 @@ private void synthesizeJava8UtilityClass( Builder<?> builder, ExecutorService executorService) throws ExecutionException { - if (backportedMethodRewriter != null) { - backportedMethodRewriter.synthesizeUtilityClasses(builder, executorService); - } + backportedMethodRewriter.synthesizeUtilityClasses(builder, executorService); } private void processCovariantReturnTypeAnnotations(Builder<?> builder) { @@ -1295,9 +1290,7 @@ if (options.desugarState == DesugarState.ON && enableTryWithResourcesDesugaring()) { codeRewriter.rewriteThrowableAddAndGetSuppressed(code); } - if (backportedMethodRewriter != null) { - backportedMethodRewriter.desugar(code); - } + backportedMethodRewriter.desugar(code); stringConcatRewriter.desugarStringConcats(method.method, code); @@ -1317,6 +1310,7 @@ stringOptimizer, method, code, + feedback, methodProcessor, inliner, Suppliers.memoize( @@ -1350,7 +1344,8 @@ previous = printMethod(code, "IR after interface method rewriting (SSA)", previous); // This pass has to be after interfaceMethodRewriter and BackportedMethodRewriter. - if (desugaredLibraryAPIConverter != null) { + if (desugaredLibraryAPIConverter != null + && (!appView.enableWholeProgramOptimizations() || methodProcessor.isPrimary())) { desugaredLibraryAPIConverter.desugar(code); assert code.isConsistentSSA(); }
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 dc425e4..b65b164 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
@@ -29,6 +29,7 @@ import com.android.tools.r8.graph.GraphLense.GraphLenseLookupResult; import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription; import com.android.tools.r8.graph.GraphLense.RewrittenPrototypeDescription.RemovedArgumentsInfo; +import com.android.tools.r8.graph.ResolutionResult; import com.android.tools.r8.graph.UseRegistry.MethodHandleUse; import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses; import com.android.tools.r8.ir.analysis.type.DestructivePhiTypeUpdater; @@ -626,7 +627,9 @@ // Virtual invoke is already as specific as it can get. return target; } - DexEncodedMethod newTarget = appView.appInfo().lookupVirtualTarget(receiverType, target); + ResolutionResult resolutionResult = appView.appInfo().resolveMethod(receiverType, target); + DexEncodedMethod newTarget = + resolutionResult.isVirtualTarget() ? resolutionResult.getSingleTarget() : null; if (newTarget == null || newTarget.method == target) { // Most likely due to a missing class, or invoke is already as specific as it gets. return target;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java index 81f93a7..b283f96 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
@@ -9,7 +9,6 @@ import com.android.tools.r8.graph.DexMethodHandle; import com.android.tools.r8.graph.DexProto; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.MethodAccessFlags; import com.android.tools.r8.ir.code.Invoke; import com.android.tools.r8.ir.code.Position; import com.android.tools.r8.ir.code.ValueType; @@ -56,13 +55,6 @@ return true; } - private boolean isPrivateMethod() { - // We should be able to find targets for all private impl-methods, so - // we can rely on knowing accessibility flags for them. - MethodAccessFlags flags = descriptor().getAccessibility(); - return flags != null && flags.isPrivate(); - } - // Are we delegating to a constructor? private boolean delegatingToConstructor() { return descriptor().implHandle.type.isInvokeConstructor();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java index 584bfc4..fe87616 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -44,6 +44,7 @@ import com.android.tools.r8.origin.SynthesizedOrigin; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.InternalOptions; +import com.android.tools.r8.utils.InternalOptions.DesugarState; import com.android.tools.r8.utils.StringDiagnostic; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -70,7 +71,7 @@ private final IRConverter converter; private final DexItemFactory factory; private final RewritableMethods rewritableMethods; - private final boolean enable; + private final boolean enabled; private final Set<DexType> holders = Sets.newConcurrentHashSet(); private final Map<DexMethod, MethodProvider> methodProviders = new ConcurrentHashMap<>(); @@ -84,8 +85,10 @@ // the highest known API level when the compiler is built. This ensures that when this is used // by the Android Platform build (which normally use an API level of 10000) there will be // no rewriting of backported methods. See b/147480264. - this.enable = - !this.rewritableMethods.isEmpty() + this.enabled = + (appView.options().desugarState == DesugarState.ON + || appView.options().desugarState == DesugarState.ONLY_BACKPORT_STATICS) + && !this.rewritableMethods.isEmpty() && appView.options().minApiLevel <= AndroidApiLevel.LATEST.getLevel(); } @@ -101,7 +104,7 @@ } public void desugar(IRCode code) { - if (!enable) { + if (!enabled) { return; // Nothing to do! } @@ -113,6 +116,10 @@ } InvokeMethod invoke = instruction.asInvokeMethod(); + if (appView.options().desugarState == DesugarState.ONLY_BACKPORT_STATICS + && !invoke.isInvokeStatic()) { + continue; + } MethodProvider provider = getMethodProviderOrNull(invoke.getInvokedMethod()); if (provider == null) { if (!rewritableMethods.matchesVirtualRewrite(invoke.getInvokedMethod())) { @@ -183,6 +190,9 @@ public void synthesizeUtilityClasses(Builder<?> builder, ExecutorService executorService) throws ExecutionException { + if (!enabled) { + return; + } if (appView.options().isDesugaredLibraryCompilation()) { synthesizeEmulatedDispatchMethods(builder); } else { @@ -1626,6 +1636,8 @@ } private void initializeRetargetCoreLibraryMembers(AppView<?> appView) { + assert appView.appInfo().hasClassHierarchy() + : "Class hierarchy required for desugared library."; Map<DexString, Map<DexType, DexType>> retargetCoreLibMember = appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember(); for (DexString methodName : retargetCoreLibMember.keySet()) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java index 0204455..2f54714 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.ir.desugar; +import com.android.tools.r8.graph.AppInfoWithClassHierarchy; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexAnnotationSet; import com.android.tools.r8.graph.DexClass; @@ -174,7 +175,7 @@ } } - private final AppView<?> appView; + private final AppView<? extends AppInfoWithClassHierarchy> appView; private final DexItemFactory dexItemFactory; private final InterfaceMethodRewriter rewriter; private final Consumer<DexEncodedMethod> newSynthesizedMethodConsumer; @@ -195,7 +196,7 @@ new IdentityHashMap<>(); ClassProcessor( - AppView<?> appView, + AppView<? extends AppInfoWithClassHierarchy> appView, InterfaceMethodRewriter rewriter, Consumer<DexEncodedMethod> newSynthesizedMethodConsumer) { this.appView = appView;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java index ab215b6..09cd2aa 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -8,6 +8,7 @@ import com.android.tools.r8.errors.CompilationError; import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.graph.AppInfo; +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.DexAnnotationSet; @@ -97,7 +98,7 @@ public static final String DEFAULT_METHOD_PREFIX = "$default$"; public static final String PRIVATE_METHOD_PREFIX = "$private$"; - private final AppView<?> appView; + private final AppView<? extends AppInfoWithClassHierarchy> appView; private final IRConverter converter; private final InternalOptions options; final DexItemFactory factory; @@ -132,7 +133,9 @@ public InterfaceMethodRewriter(AppView<?> appView, IRConverter converter) { assert converter != null; - this.appView = appView; + assert appView.appInfo().hasClassHierarchy() + : "Cannot desugar interfaces without class hierarchy"; + this.appView = appView.withClassHierarchy(); this.converter = converter; this.options = appView.options(); this.factory = appView.dexItemFactory(); @@ -280,7 +283,6 @@ DexEncodedMethod dexEncodedMethod = appView .appInfo() - .withClassHierarchy() .lookupSuperTarget(invokeSuper.getInvokedMethod(), code.method.method.holder); if (dexEncodedMethod != null) { DexClass dexClass = appView.definitionFor(dexEncodedMethod.method.holder);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java index 6bb06d5..e7d2f34 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -25,6 +25,8 @@ import com.android.tools.r8.graph.FieldAccessFlags; import com.android.tools.r8.graph.MethodAccessFlags; import com.android.tools.r8.graph.ParameterAnnotationsList; +import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult; import com.android.tools.r8.ir.code.Invoke; import com.android.tools.r8.ir.code.Invoke.Type; import com.android.tools.r8.ir.synthetic.SynthesizedCode; @@ -364,11 +366,15 @@ // Lambda$ method. We must always find it. assert implMethod.holder == accessedFrom; - assert descriptor.targetFoundInClass(accessedFrom); - assert descriptor.getAccessibility() != null; - + assert descriptor.verifyTargetFoundInClass(accessedFrom); if (implHandle.type.isInvokeStatic()) { - return new StaticLambdaImplTarget(); + SingleResolutionResult resolution = + rewriter.getAppInfo().resolveMethod(implMethod.holder, implMethod).asSingleResolution(); + assert resolution.getResolvedMethod().isStatic(); + assert resolution.getResolvedHolder().isProgramClass(); + return new StaticLambdaImplTarget( + new ProgramMethod( + resolution.getResolvedHolder().asProgramClass(), resolution.getResolvedMethod())); } assert implHandle.type.isInvokeInstance() || implHandle.type.isInvokeDirect(); @@ -575,18 +581,19 @@ // Used for static private lambda$ methods. Only needs access relaxation. private final class StaticLambdaImplTarget extends Target { - StaticLambdaImplTarget() { + final ProgramMethod target; + + StaticLambdaImplTarget(ProgramMethod target) { super(descriptor.implHandle.asMethod(), Invoke.Type.STATIC); + this.target = target; } @Override DexEncodedMethod ensureAccessibility() { // We already found the static method to be called, just relax its accessibility. - assert descriptor.getAccessibility() != null; - descriptor.getAccessibility().unsetPrivate(); - DexClass implMethodHolder = definitionFor(descriptor.implHandle.asMethod().holder); - if (implMethodHolder.isInterface()) { - descriptor.getAccessibility().setPublic(); + target.method.accessFlags.unsetPrivate(); + if (target.holder.isInterface()) { + target.method.accessFlags.setPublic(); } return null; }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java index 20becfb..2eb7b03 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
@@ -5,12 +5,13 @@ package com.android.tools.r8.ir.desugar; import com.android.tools.r8.errors.Unreachable; -import com.android.tools.r8.graph.AppInfo; +import com.android.tools.r8.graph.AppInfoWithClassHierarchy; import com.android.tools.r8.graph.DexCallSite; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexMethodHandle; +import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexProto; import com.android.tools.r8.graph.DexString; import com.android.tools.r8.graph.DexType; @@ -44,7 +45,8 @@ final DexTypeList captures; // Used for accessibility analysis and few assertions only. - private final DexEncodedMethod targetMethod; + private final MethodAccessFlags targetAccessFlags; + private final DexType targetHolder; private LambdaDescriptor() { uniqueId = null; @@ -53,12 +55,20 @@ enforcedProto = null; implHandle = null; captures = null; - targetMethod = null; + targetAccessFlags = null; + targetHolder = null; } - private LambdaDescriptor(AppInfo appInfo, DexCallSite callSite, - DexString name, DexProto erasedProto, DexProto enforcedProto, - DexMethodHandle implHandle, DexType mainInterface, DexTypeList captures) { + private LambdaDescriptor( + AppInfoWithClassHierarchy appInfo, + DexType invocationContext, + DexCallSite callSite, + DexString name, + DexProto erasedProto, + DexProto enforcedProto, + DexMethodHandle implHandle, + DexType mainInterface, + DexTypeList captures) { assert appInfo != null; assert callSite != null; assert name != null; @@ -76,7 +86,15 @@ this.captures = captures; this.interfaces.add(mainInterface); - this.targetMethod = lookupTargetMethod(appInfo); + DexEncodedMethod targetMethod = + invocationContext == null ? null : lookupTargetMethod(appInfo, invocationContext); + if (targetMethod != null) { + targetAccessFlags = targetMethod.accessFlags.copy(); + targetHolder = targetMethod.method.holder; + } else { + targetAccessFlags = null; + targetHolder = null; + } } final DexType getImplReceiverType() { @@ -88,15 +106,18 @@ return captures.length > 0 ? captures[0] : params[0]; } - private DexEncodedMethod lookupTargetMethod(AppInfo appInfo) { + private DexEncodedMethod lookupTargetMethod( + AppInfoWithClassHierarchy appInfo, DexType invocationContext) { + assert invocationContext != null; // Find the lambda's impl-method target. DexMethod method = implHandle.asMethod(); switch (implHandle.type) { case INVOKE_DIRECT: case INVOKE_INSTANCE: { - DexEncodedMethod target = appInfo.lookupVirtualTarget(getImplReceiverType(), method); + DexEncodedMethod target = + appInfo.resolveMethod(getImplReceiverType(), method).getSingleTarget(); if (target == null) { - target = appInfo.lookupDirectTarget(method); + target = appInfo.lookupDirectTarget(method, invocationContext); } assert target == null || (implHandle.type.isInvokeInstance() && isInstanceMethod(target)) @@ -106,19 +127,20 @@ } case INVOKE_STATIC: { - DexEncodedMethod target = appInfo.lookupStaticTarget(method); + DexEncodedMethod target = appInfo.lookupStaticTarget(method, invocationContext); assert target == null || target.accessFlags.isStatic(); return target; } case INVOKE_CONSTRUCTOR: { - DexEncodedMethod target = appInfo.lookupDirectTarget(method); + DexEncodedMethod target = appInfo.lookupDirectTarget(method, invocationContext); assert target == null || target.accessFlags.isConstructor(); return target; } case INVOKE_INTERFACE: { - DexEncodedMethod target = appInfo.lookupVirtualTarget(getImplReceiverType(), method); + DexEncodedMethod target = + appInfo.resolveMethod(getImplReceiverType(), method).getSingleTarget(); assert target == null || isInstanceMethod(target); return target; } @@ -143,12 +165,8 @@ return encodedMethod.isPublicized() && isInstanceMethod(encodedMethod); } - final MethodAccessFlags getAccessibility() { - return targetMethod == null ? null : targetMethod.accessFlags; - } - - final boolean targetFoundInClass(DexType type) { - return targetMethod != null && targetMethod.method.holder == type; + public final boolean verifyTargetFoundInClass(DexType type) { + return targetHolder == type; } /** If the lambda delegates to lambda$ method. */ @@ -177,9 +195,12 @@ boolean instanceTarget = implHandle.type.isInvokeInstance() || implHandle.type.isInvokeDirect(); boolean initTarget = implHandle.type.isInvokeConstructor(); assert instanceTarget || staticTarget || initTarget; - assert !implHandle.type.isInvokeDirect() || isPrivateInstanceMethod(targetMethod); + assert !implHandle.type.isInvokeDirect() + || (targetAccessFlags.isPrivate() + && !targetAccessFlags.isConstructor() + && !targetAccessFlags.isStatic()); - if (targetMethod == null) { + if (targetAccessFlags == null) { // The target cannot be a private method, since otherwise it // should have been found. @@ -200,7 +221,7 @@ return true; } - MethodAccessFlags flags = targetMethod.accessFlags; + MethodAccessFlags flags = targetAccessFlags; // Private methods always need accessors. if (flags.isPrivate()) { @@ -211,26 +232,27 @@ } boolean accessedFromSamePackage = - accessedFrom.getPackageDescriptor().equals( - targetMethod.method.holder.getPackageDescriptor()); + accessedFrom.getPackageDescriptor().equals(targetHolder.getPackageDescriptor()); assert flags.isProtected() || accessedFromSamePackage; return flags.isProtected() && !accessedFromSamePackage; } /** - * Matches call site for lambda metafactory invocation pattern and - * returns extracted match information, or null if match failed. + * Matches call site for lambda metafactory invocation pattern and returns extracted match + * information, or null if match failed. */ - public static LambdaDescriptor tryInfer(DexCallSite callSite, AppInfo appInfo) { - LambdaDescriptor descriptor = infer(callSite, appInfo); + public static LambdaDescriptor tryInfer( + DexCallSite callSite, AppInfoWithClassHierarchy appInfo, DexProgramClass invocationContext) { + LambdaDescriptor descriptor = infer(callSite, appInfo, invocationContext.type); return descriptor == MATCH_FAILED ? null : descriptor; } /** - * Matches call site for lambda metafactory invocation pattern and - * returns extracted match information, or MATCH_FAILED if match failed. + * Matches call site for lambda metafactory invocation pattern and returns extracted match + * information, or MATCH_FAILED if match failed. */ - static LambdaDescriptor infer(DexCallSite callSite, AppInfo appInfo) { + static LambdaDescriptor infer( + DexCallSite callSite, AppInfoWithClassHierarchy appInfo, DexType invocationContext) { // We expect bootstrap method to be either `metafactory` or `altMetafactory` method // of `java.lang.invoke.LambdaMetafactory` class. Both methods are static. if (!callSite.bootstrapMethod.type.isInvokeStatic()) { @@ -277,9 +299,17 @@ DexTypeList captures = lambdaFactoryProto.parameters; // Create a match. - LambdaDescriptor match = new LambdaDescriptor(appInfo, callSite, - funcMethodName, funcErasedSignature.value, funcEnforcedSignature.value, - lambdaImplMethodHandle, mainFuncInterface, captures); + LambdaDescriptor match = + new LambdaDescriptor( + appInfo, + invocationContext, + callSite, + funcMethodName, + funcErasedSignature.value, + funcEnforcedSignature.value, + lambdaImplMethodHandle, + mainFuncInterface, + captures); if (bootstrapMethod == factory.metafactoryMethod) { if (callSite.bootstrapArgs.size() != 3) { @@ -342,8 +372,10 @@ } } - public static List<DexType> getInterfaces(DexCallSite callSite, AppInfo appInfo) { - LambdaDescriptor descriptor = infer(callSite, appInfo); + public static List<DexType> getInterfaces( + DexCallSite callSite, AppInfoWithClassHierarchy appInfo) { + // No need for the invocationContext to figure out only the interfaces. + LambdaDescriptor descriptor = infer(callSite, appInfo, null); if (descriptor == LambdaDescriptor.MATCH_FAILED) { return null; }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java index 2fd29e1..6ce1f5f 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
@@ -4,7 +4,7 @@ package com.android.tools.r8.ir.desugar; -import com.android.tools.r8.graph.AppInfo; +import com.android.tools.r8.graph.AppInfoWithClassHierarchy; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.Code; import com.android.tools.r8.graph.DefaultUseRegistry; @@ -63,7 +63,7 @@ static final String EXPECTED_LAMBDA_METHOD_PREFIX = "lambda$"; private static final String LAMBDA_INSTANCE_FIELD_NAME = "INSTANCE"; - private final AppView<?> appView; + private final AppView<? extends AppInfoWithClassHierarchy> appView; final DexString instanceFieldName; @@ -87,7 +87,9 @@ } public LambdaRewriter(AppView<?> appView) { - this.appView = appView; + assert appView.appInfo().hasClassHierarchy() + : "Lambda desugaring is not available without class hierarchy."; + this.appView = appView.withClassHierarchy(); this.graphLens = new LambdaRewriterGraphLense(appView); this.instanceFieldName = getFactory().createString(LAMBDA_INSTANCE_FIELD_NAME); } @@ -96,8 +98,8 @@ return appView; } - public AppInfo getAppInfo() { - return getAppView().appInfo(); + public AppInfoWithClassHierarchy getAppInfo() { + return appView.appInfo(); } public DexItemFactory getFactory() { @@ -214,7 +216,8 @@ @Override public void registerCallSite(DexCallSite callSite) { LambdaDescriptor descriptor = - inferLambdaDescriptor(lensCodeRewriter.rewriteCallSite(callSite, method)); + inferLambdaDescriptor( + lensCodeRewriter.rewriteCallSite(callSite, method), method.method.holder); if (descriptor != LambdaDescriptor.MATCH_FAILED) { consumer.accept(getOrCreateLambdaClass(descriptor, method.method.holder)); } @@ -238,7 +241,8 @@ Instruction instruction = instructions.next(); if (instruction.isInvokeCustom()) { InvokeCustom invoke = instruction.asInvokeCustom(); - LambdaDescriptor descriptor = inferLambdaDescriptor(invoke.getCallSite()); + LambdaDescriptor descriptor = + inferLambdaDescriptor(invoke.getCallSite(), encodedMethod.method.holder); if (descriptor == LambdaDescriptor.MATCH_FAILED) { continue; } @@ -333,7 +337,7 @@ // corresponding to this lambda invocation point. // // Returns the lambda descriptor or `MATCH_FAILED`. - private LambdaDescriptor inferLambdaDescriptor(DexCallSite callSite) { + private LambdaDescriptor inferLambdaDescriptor(DexCallSite callSite, DexType invocationContext) { // We check the map before and after inferring lambda descriptor to minimize time // spent in synchronized block. As a result we may throw away calculated descriptor // in rare case when another thread has same call site processed concurrently, @@ -341,7 +345,10 @@ LambdaDescriptor descriptor = getKnown(knownCallSites, callSite); return descriptor != null ? descriptor - : putIfAbsent(knownCallSites, callSite, LambdaDescriptor.infer(callSite, getAppInfo())); + : putIfAbsent( + knownCallSites, + callSite, + LambdaDescriptor.infer(callSite, getAppInfo(), invocationContext)); } private boolean isInMainDexList(DexType type) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java index 1382317..eff0d07 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
@@ -12,6 +12,7 @@ import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.ir.analysis.value.AbstractValue; import com.android.tools.r8.ir.code.BasicBlock; import com.android.tools.r8.ir.code.ConstClass; import com.android.tools.r8.ir.code.ConstNumber; @@ -29,8 +30,10 @@ import it.unimi.dsi.fastutil.objects.Object2IntArrayMap; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenCustomHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; import it.unimi.dsi.fastutil.objects.Object2ObjectSortedMap.FastSortedEntrySet; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; /** @@ -40,14 +43,17 @@ // Threshold to limit the number of constant canonicalization. private static final int MAX_CANONICALIZED_CONSTANT = 22; + private final CodeRewriter codeRewriter; + private int numberOfConstNumberCanonicalization = 0; private int numberOfConstStringCanonicalization = 0; private int numberOfDexItemBasedConstStringCanonicalization = 0; private int numberOfConstClassCanonicalization = 0; - private int numberOfEnumCanonicalization = 0; + private int numberOfEffectivelyFinalFieldCanonicalization = 0; private final Object2IntMap<Long> histogramOfCanonicalizationCandidatesPerMethod; - public ConstantCanonicalizer() { + public ConstantCanonicalizer(CodeRewriter codeRewriter) { + this.codeRewriter = codeRewriter; if (Log.ENABLED) { histogramOfCanonicalizationCandidatesPerMethod = new Object2IntArrayMap<>(); } else { @@ -66,7 +72,10 @@ numberOfDexItemBasedConstStringCanonicalization); Log.info(getClass(), "# const-class canonicalization: %s", numberOfConstClassCanonicalization); - Log.info(getClass(), "# enum canonicalization: %s", numberOfEnumCanonicalization); + Log.info( + getClass(), + "# effectively final field canonicalization: %s", + numberOfEffectivelyFinalFieldCanonicalization); assert histogramOfCanonicalizationCandidatesPerMethod != null; Log.info(getClass(), "------ histogram of constant canonicalization candidates ------"); histogramOfCanonicalizationCandidatesPerMethod.forEach((length, count) -> { @@ -129,9 +138,11 @@ && code.metadata().mayHaveMonitorInstruction()) { continue; } - // Only canonicalize enum values. This is only OK if the instruction cannot have side effects. + // Canonicalize effectively final fields that are guaranteed to be written before they are + // read. This is only OK if the instruction cannot have side effects. if (current.isStaticGet()) { - if (!current.outValue().getAbstractValue(appView, context).isSingleEnumValue()) { + AbstractValue abstractValue = current.outValue().getAbstractValue(appView, context); + if (!abstractValue.isSingleFieldValue()) { continue; } if (current.instructionMayHaveSideEffects(appView, context)) { @@ -174,59 +185,69 @@ histogramOfCanonicalizationCandidatesPerMethod.put(numOfCandidates, count + 1); } } - entries.stream() - .filter(a -> a.getValue().size() > 1) - .sorted((a, b) -> Integer.compare(b.getValue().size(), a.getValue().size())) - .limit(MAX_CANONICALIZED_CONSTANT) - .forEach( - (entry) -> { - Instruction canonicalizedConstant = entry.getKey(); - assert canonicalizedConstant.instructionTypeCanBeCanonicalized(); - Instruction newConst; - switch (canonicalizedConstant.opcode()) { - case CONST_CLASS: - if (Log.ENABLED) { - numberOfConstClassCanonicalization++; - } - newConst = ConstClass.copyOf(code, canonicalizedConstant.asConstClass()); - break; - case CONST_NUMBER: - if (Log.ENABLED) { - numberOfConstNumberCanonicalization++; - } - newConst = ConstNumber.copyOf(code, canonicalizedConstant.asConstNumber()); - break; - case CONST_STRING: - if (Log.ENABLED) { - numberOfConstStringCanonicalization++; - } - newConst = ConstString.copyOf(code, canonicalizedConstant.asConstString()); - break; - case DEX_ITEM_BASED_CONST_STRING: - if (Log.ENABLED) { - numberOfDexItemBasedConstStringCanonicalization++; - } - newConst = - DexItemBasedConstString.copyOf( - code, canonicalizedConstant.asDexItemBasedConstString()); - break; - case STATIC_GET: - if (Log.ENABLED) { - numberOfEnumCanonicalization++; - } - newConst = StaticGet.copyOf(code, canonicalizedConstant.asStaticGet()); - break; - default: - throw new Unreachable(); - } - newConst.setPosition(firstNonNonePosition); - insertCanonicalizedConstant(code, newConst); - for (Value outValue : entry.getValue()) { - outValue.replaceUsers(newConst.outValue()); - } - }); - code.removeAllTrivialPhis(); + Iterator<Object2ObjectMap.Entry<Instruction, List<Value>>> iterator = + entries.stream() + .filter(a -> a.getValue().size() > 1) + .sorted((a, b) -> Integer.compare(b.getValue().size(), a.getValue().size())) + .limit(MAX_CANONICALIZED_CONSTANT) + .iterator(); + + if (!iterator.hasNext()) { + return; + } + do { + Object2ObjectMap.Entry<Instruction, List<Value>> entry = iterator.next(); + Instruction canonicalizedConstant = entry.getKey(); + assert canonicalizedConstant.instructionTypeCanBeCanonicalized(); + Instruction newConst; + switch (canonicalizedConstant.opcode()) { + case CONST_CLASS: + if (Log.ENABLED) { + numberOfConstClassCanonicalization++; + } + newConst = ConstClass.copyOf(code, canonicalizedConstant.asConstClass()); + break; + case CONST_NUMBER: + if (Log.ENABLED) { + numberOfConstNumberCanonicalization++; + } + newConst = ConstNumber.copyOf(code, canonicalizedConstant.asConstNumber()); + break; + case CONST_STRING: + if (Log.ENABLED) { + numberOfConstStringCanonicalization++; + } + newConst = ConstString.copyOf(code, canonicalizedConstant.asConstString()); + break; + case DEX_ITEM_BASED_CONST_STRING: + if (Log.ENABLED) { + numberOfDexItemBasedConstStringCanonicalization++; + } + newConst = + DexItemBasedConstString.copyOf( + code, canonicalizedConstant.asDexItemBasedConstString()); + break; + case STATIC_GET: + if (Log.ENABLED) { + numberOfEffectivelyFinalFieldCanonicalization++; + } + newConst = StaticGet.copyOf(code, canonicalizedConstant.asStaticGet()); + break; + default: + throw new Unreachable(); + } + newConst.setPosition(firstNonNonePosition); + insertCanonicalizedConstant(code, newConst); + for (Value outValue : entry.getValue()) { + outValue.replaceUsers(newConst.outValue()); + } + } while (iterator.hasNext()); + + if (code.removeAllTrivialPhis()) { + codeRewriter.simplifyControlFlow(code); + } + assert code.isConsistentSSA(); }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java index c7e511a..8370b69 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -16,7 +16,6 @@ import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis; -import com.android.tools.r8.ir.analysis.proto.ProtoInliningReasonStrategy; import com.android.tools.r8.ir.code.BasicBlock; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.InstancePut; @@ -32,7 +31,6 @@ import com.android.tools.r8.ir.optimize.Inliner.InlineeWithReason; import com.android.tools.r8.ir.optimize.Inliner.Reason; import com.android.tools.r8.ir.optimize.info.OptimizationFeedback; -import com.android.tools.r8.ir.optimize.inliner.DefaultInliningReasonStrategy; import com.android.tools.r8.ir.optimize.inliner.InliningReasonStrategy; import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter; import com.android.tools.r8.logging.Log; @@ -64,6 +62,7 @@ DefaultInliningOracle( AppView<AppInfoWithLiveness> appView, Inliner inliner, + InliningReasonStrategy inliningReasonStrategy, DexEncodedMethod method, IRCode code, MethodProcessor methodProcessor, @@ -71,20 +70,13 @@ int inliningInstructionAllowance) { this.appView = appView; this.inliner = inliner; + this.reasonStrategy = inliningReasonStrategy; this.method = method; this.code = code; this.methodProcessor = methodProcessor; this.isProcessedConcurrently = methodProcessor::isProcessedConcurrently; this.inliningInstructionLimit = inliningInstructionLimit; this.instructionAllowance = inliningInstructionAllowance; - - DefaultInliningReasonStrategy defaultInliningReasonStrategy = - new DefaultInliningReasonStrategy( - appView, methodProcessor.getCallSiteInformation(), inliner); - this.reasonStrategy = - appView.withGeneratedMessageLiteShrinker( - ignore -> new ProtoInliningReasonStrategy(appView, defaultInliningReasonStrategy), - defaultInliningReasonStrategy); } @Override @@ -333,6 +325,7 @@ public InlineAction computeInlining( InvokeMethod invoke, DexEncodedMethod singleTarget, + DexEncodedMethod context, ClassInitializationAnalysis classInitializationAnalysis, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { if (isSingleTargetInvalid(invoke, singleTarget, whyAreYouNotInliningReporter)) { @@ -343,7 +336,7 @@ return null; } - Reason reason = reasonStrategy.computeInliningReason(invoke, singleTarget); + Reason reason = reasonStrategy.computeInliningReason(invoke, singleTarget, context); if (reason == Reason.NEVER) { return null; }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java index 15281a5..26c28d7 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
@@ -61,6 +61,7 @@ public InlineAction computeInlining( InvokeMethod invoke, DexEncodedMethod singleTarget, + DexEncodedMethod context, ClassInitializationAnalysis classInitializationAnalysis, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { return computeForInvoke(invoke, whyAreYouNotInliningReporter);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java index ed35f73..5bbc662 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -19,6 +19,7 @@ import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.graph.NestMemberClassAttribute; import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis; +import com.android.tools.r8.ir.analysis.proto.ProtoInliningReasonStrategy; import com.android.tools.r8.ir.analysis.type.Nullability; import com.android.tools.r8.ir.analysis.type.TypeLatticeElement; import com.android.tools.r8.ir.code.BasicBlock; @@ -44,7 +45,9 @@ import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter; import com.android.tools.r8.ir.optimize.info.OptimizationFeedback; import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore; +import com.android.tools.r8.ir.optimize.inliner.DefaultInliningReasonStrategy; import com.android.tools.r8.ir.optimize.inliner.InliningIRProvider; +import com.android.tools.r8.ir.optimize.inliner.InliningReasonStrategy; import com.android.tools.r8.ir.optimize.inliner.NopWhyAreYouNotInliningReporter; import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter; import com.android.tools.r8.ir.optimize.lambda.LambdaMerger; @@ -830,12 +833,25 @@ performInliningImpl( oracle, oracle, method, code, OptimizationFeedbackIgnore.getInstance(), inliningIRProvider); } - public void performInlining( DexEncodedMethod method, IRCode code, OptimizationFeedback feedback, MethodProcessor methodProcessor) { + performInlining( + method, + code, + feedback, + methodProcessor, + createDefaultInliningReasonStrategy(methodProcessor)); + } + + public void performInlining( + DexEncodedMethod method, + IRCode code, + OptimizationFeedback feedback, + MethodProcessor methodProcessor, + InliningReasonStrategy inliningReasonStrategy) { InternalOptions options = appView.options(); DefaultInliningOracle oracle = createDefaultOracle( @@ -843,21 +859,48 @@ code, methodProcessor, options.inliningInstructionLimit, - options.inliningInstructionAllowance - numberOfInstructions(code)); + options.inliningInstructionAllowance - numberOfInstructions(code), + inliningReasonStrategy); InliningIRProvider inliningIRProvider = new InliningIRProvider(appView, method, code); assert inliningIRProvider.verifyIRCacheIsEmpty(); performInliningImpl(oracle, oracle, method, code, feedback, inliningIRProvider); } + public InliningReasonStrategy createDefaultInliningReasonStrategy( + MethodProcessor methodProcessor) { + DefaultInliningReasonStrategy defaultInliningReasonStrategy = + new DefaultInliningReasonStrategy(appView, methodProcessor.getCallSiteInformation(), this); + return appView.withGeneratedMessageLiteShrinker( + ignore -> new ProtoInliningReasonStrategy(appView, defaultInliningReasonStrategy), + defaultInliningReasonStrategy); + } + public DefaultInliningOracle createDefaultOracle( DexEncodedMethod method, IRCode code, MethodProcessor methodProcessor, int inliningInstructionLimit, int inliningInstructionAllowance) { + return createDefaultOracle( + method, + code, + methodProcessor, + inliningInstructionLimit, + inliningInstructionAllowance, + createDefaultInliningReasonStrategy(methodProcessor)); + } + + public DefaultInliningOracle createDefaultOracle( + DexEncodedMethod method, + IRCode code, + MethodProcessor methodProcessor, + int inliningInstructionLimit, + int inliningInstructionAllowance, + InliningReasonStrategy inliningReasonStrategy) { return new DefaultInliningOracle( appView, this, + inliningReasonStrategy, method, code, methodProcessor, @@ -905,7 +948,11 @@ : WhyAreYouNotInliningReporter.createFor(singleTarget, appView, context); InlineAction action = oracle.computeInlining( - invoke, singleTarget, classInitializationAnalysis, whyAreYouNotInliningReporter); + invoke, + singleTarget, + context, + classInitializationAnalysis, + whyAreYouNotInliningReporter); if (action == null) { assert whyAreYouNotInliningReporter.unsetReasonHasBeenReportedFlag(); continue;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java index fd4b0ca..e50011a 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
@@ -11,15 +11,18 @@ import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexReference; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.graph.ResolutionResult; +import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult; import com.android.tools.r8.ir.code.Invoke.Type; import com.android.tools.r8.ir.optimize.Inliner.Constraint; import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.shaking.AppInfoWithLiveness; import java.util.Collection; +import java.util.function.BiFunction; // Computes the inlining constraint for a given instruction. public class InliningConstraints { @@ -47,10 +50,22 @@ this.graphLense = graphLense; // Note: Intentionally *not* appView.graphLense(). } + public AppView<AppInfoWithLiveness> getAppView() { + return appView; + } + + public GraphLense getGraphLense() { + return graphLense; + } + public void disallowStaticInterfaceMethodCalls() { allowStaticInterfaceMethodCalls = false; } + private boolean isVerticalClassMerging() { + return !graphLense.isIdentityLense(); + } + public ConstraintWithTarget forAlwaysMaterializingUser() { return ConstraintWithTarget.ALWAYS; } @@ -154,10 +169,47 @@ return ConstraintWithTarget.NEVER; } + private DexEncodedMethod lookupWhileVerticalClassMerging( + DexMethod method, + DexType invocationContext, + BiFunction<SingleResolutionResult, DexProgramClass, DexEncodedMethod> lookupFunction) { + SingleResolutionResult singleResolutionResult = + appView.appInfo().resolveMethod(method.holder, method).asSingleResolution(); + if (singleResolutionResult == null) { + return null; + } + DexProgramClass context = appView.definitionForProgramType(invocationContext); + if (context == null) { + return null; + } + DexEncodedMethod dexEncodedMethod = lookupFunction.apply(singleResolutionResult, context); + if (dexEncodedMethod != null) { + return dexEncodedMethod; + } + assert graphLense.lookupType(context.superType) == context.type; + DexProgramClass superContext = appView.definitionForProgramType(context.superType); + if (superContext == null) { + return null; + } + DexEncodedMethod alternativeDexEncodedMethod = + lookupFunction.apply(singleResolutionResult, superContext); + if (alternativeDexEncodedMethod != null + && alternativeDexEncodedMethod.method.holder == superContext.type) { + return alternativeDexEncodedMethod; + } + return null; + } + public ConstraintWithTarget forInvokeDirect(DexMethod method, DexType invocationContext) { DexMethod lookup = graphLense.lookupMethod(method); - return forSingleTargetInvoke( - lookup, appView.appInfo().lookupDirectTarget(lookup), invocationContext); + DexEncodedMethod target = + isVerticalClassMerging() + ? lookupWhileVerticalClassMerging( + lookup, + invocationContext, + (res, ctxt) -> res.lookupInvokeDirectTarget(ctxt, appView.appInfo())) + : appView.appInfo().lookupDirectTarget(lookup, invocationContext); + return forSingleTargetInvoke(lookup, target, invocationContext); } public ConstraintWithTarget forInvokeInterface(DexMethod method, DexType invocationContext) { @@ -182,8 +234,14 @@ public ConstraintWithTarget forInvokeStatic(DexMethod method, DexType invocationContext) { DexMethod lookup = graphLense.lookupMethod(method); - return forSingleTargetInvoke( - lookup, appView.appInfo().lookupStaticTarget(lookup, invocationContext), invocationContext); + DexEncodedMethod target = + isVerticalClassMerging() + ? lookupWhileVerticalClassMerging( + lookup, + invocationContext, + (res, ctxt) -> res.lookupInvokeStaticTarget(ctxt, appView.appInfo())) + : appView.appInfo().lookupStaticTarget(lookup, invocationContext); + return forSingleTargetInvoke(lookup, target, invocationContext); } public ConstraintWithTarget forInvokeSuper(DexMethod method, DexType invocationContext) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java index 3fbb76f..6f4570f 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
@@ -31,6 +31,7 @@ InlineAction computeInlining( InvokeMethod invoke, DexEncodedMethod singleTarget, + DexEncodedMethod context, ClassInitializationAnalysis classInitializationAnalysis, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter); }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java index c71006f..c4a761b 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -4,10 +4,14 @@ package com.android.tools.r8.ir.optimize.classinliner; +import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull; + import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.DexType; import com.android.tools.r8.ir.analysis.type.TypeAnalysis; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.Instruction; @@ -18,6 +22,7 @@ import com.android.tools.r8.ir.optimize.Inliner; import com.android.tools.r8.ir.optimize.InliningOracle; import com.android.tools.r8.ir.optimize.classinliner.InlineCandidateProcessor.IllegalClassInlinerStateException; +import com.android.tools.r8.ir.optimize.info.OptimizationFeedback; import com.android.tools.r8.ir.optimize.inliner.InliningIRProvider; import com.android.tools.r8.ir.optimize.string.StringOptimizer; import com.android.tools.r8.logging.Log; @@ -161,6 +166,7 @@ StringOptimizer stringOptimizer, DexEncodedMethod method, IRCode code, + OptimizationFeedback feedback, MethodProcessor methodProcessor, Inliner inliner, Supplier<InliningOracle> defaultOracle) { @@ -174,7 +180,7 @@ // We loop inlining iterations until there was no inlining, but still use same set // of roots to avoid infinite inlining. Looping makes possible for some roots to // become eligible after other roots are inlined. - + boolean anyInlinedGeneratedMessageLiteBuilders = false; boolean anyInlinedMethods = false; boolean repeat; do { @@ -233,6 +239,15 @@ continue; } + if (appView.protoShrinker() != null && root.isNewInstance()) { + DexType instantiatedType = root.asNewInstance().clazz; + DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(instantiatedType)); + if (clazz != null + && appView.protoShrinker().references.isGeneratedMessageLiteBuilder(clazz)) { + anyInlinedGeneratedMessageLiteBuilders = true; + } + } + // Inline the class instance. try { anyInlinedMethods |= processor.processInlining(code, defaultOracle, inliningIRProvider); @@ -262,6 +277,14 @@ } } while (repeat); + if (anyInlinedGeneratedMessageLiteBuilders) { + // Inline all calls to dynamicMethod() where the given MethodToInvoke is considered simple. + appView.withGeneratedMessageLiteBuilderShrinker( + shrinker -> + shrinker.inlineCallsToDynamicMethod( + method, code, codeRewriter, feedback, methodProcessor, inliner)); + } + if (anyInlinedMethods) { // If a method was inlined we may be able to remove check-cast instructions because we may // have more information about the types of the arguments at the call site. This is
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java index 17510c2..a5b8d52 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -20,8 +20,10 @@ import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis; import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement; import com.android.tools.r8.ir.analysis.type.TypeAnalysis; -import com.android.tools.r8.ir.code.Assume; +import com.android.tools.r8.ir.code.AliasedValueConfiguration; +import com.android.tools.r8.ir.code.AssumeAndCheckCastAliasedValueConfiguration; import com.android.tools.r8.ir.code.BasicBlock; +import com.android.tools.r8.ir.code.CheckCast; import com.android.tools.r8.ir.code.ConstNumber; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.If; @@ -29,6 +31,7 @@ import com.android.tools.r8.ir.code.InstancePut; import com.android.tools.r8.ir.code.Instruction; import com.android.tools.r8.ir.code.InstructionOrPhi; +import com.android.tools.r8.ir.code.Invoke; import com.android.tools.r8.ir.code.Invoke.Type; import com.android.tools.r8.ir.code.InvokeDirect; import com.android.tools.r8.ir.code.InvokeMethod; @@ -50,6 +53,7 @@ import com.android.tools.r8.ir.optimize.inliner.NopWhyAreYouNotInliningReporter; import com.android.tools.r8.kotlin.KotlinInfo; import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.utils.ListUtils; import com.android.tools.r8.utils.Pair; import com.android.tools.r8.utils.StringUtils; import com.google.common.collect.ImmutableSet; @@ -76,6 +80,8 @@ private static final ImmutableSet<If.Type> ALLOWED_ZERO_TEST_TYPES = ImmutableSet.of(If.Type.EQ, If.Type.NE); + private static final AliasedValueConfiguration aliasesThroughAssumeAndCheckCasts = + AssumeAndCheckCastAliasedValueConfiguration.getInstance(); private final AppView<AppInfoWithLiveness> appView; private final Inliner inliner; @@ -89,6 +95,8 @@ private final Map<InvokeMethodWithReceiver, InliningInfo> methodCallsOnInstance = new IdentityHashMap<>(); + + private final Set<DexEncodedMethod> indirectMethodCallsOnInstance = Sets.newIdentityHashSet(); private final Map<InvokeMethod, InliningInfo> extraMethodCalls = new IdentityHashMap<>(); private final List<Pair<InvokeMethod, Integer>> unusedArguments @@ -338,10 +346,7 @@ boolean anyInlinedMethods = forceInlineExtraMethodInvocations(code, inliningIRProvider); if (anyInlinedMethods) { // Reset the collections. - methodCallsOnInstance.clear(); - extraMethodCalls.clear(); - unusedArguments.clear(); - receivers.reset(); + clear(); // Repeat user analysis InstructionOrPhi ineligibleUser = areInstanceUsersEligible(defaultOracle); @@ -355,7 +360,8 @@ } anyInlinedMethods |= forceInlineDirectMethodInvocations(code, inliningIRProvider); - removeAssumeInstructionsLinkedToEligibleInstance(); + anyInlinedMethods |= forceInlineIndirectMethodInvocations(code, inliningIRProvider); + removeAliasIntroducingInstructionsLinkedToEligibleInstance(); removeMiscUsages(code); removeFieldReads(code); removeFieldWrites(); @@ -363,6 +369,14 @@ return anyInlinedMethods; } + private void clear() { + methodCallsOnInstance.clear(); + indirectMethodCallsOnInstance.clear(); + extraMethodCalls.clear(); + unusedArguments.clear(); + receivers.reset(); + } + private void replaceUsagesAsUnusedArgument(IRCode code) { for (Pair<InvokeMethod, Integer> unusedArgument : unusedArguments) { InvokeMethod invoke = unusedArgument.getFirst(); @@ -448,21 +462,73 @@ return true; } - private void removeAssumeInstructionsLinkedToEligibleInstance() { - for (Instruction user : eligibleInstance.aliasedUsers()) { - if (!user.isAssume()) { - continue; - } - Assume<?> assumeInstruction = user.asAssume(); - Value src = assumeInstruction.src(); - Value dest = assumeInstruction.outValue(); - assert receivers.isReceiverAlias(dest); - assert !dest.hasPhiUsers(); - dest.replaceUsers(src); - removeInstruction(user); + private boolean forceInlineIndirectMethodInvocations( + IRCode code, InliningIRProvider inliningIRProvider) throws IllegalClassInlinerStateException { + if (indirectMethodCallsOnInstance.isEmpty()) { + return false; } - // Verify that no more assume instructions are left as users. + + Map<InvokeMethodWithReceiver, InliningInfo> methodCallsOnInstance = new IdentityHashMap<>(); + + Set<Instruction> currentUsers = eligibleInstance.uniqueUsers(); + while (!currentUsers.isEmpty()) { + Set<Instruction> indirectOutValueUsers = Sets.newIdentityHashSet(); + for (Instruction instruction : currentUsers) { + if (instruction.isAssume() || instruction.isCheckCast()) { + indirectOutValueUsers.addAll(instruction.outValue().uniqueUsers()); + continue; + } + + if (instruction.isInvokeMethodWithReceiver()) { + InvokeMethodWithReceiver invoke = instruction.asInvokeMethodWithReceiver(); + DexMethod invokedMethod = invoke.getInvokedMethod(); + if (invokedMethod == appView.dexItemFactory().objectMethods.constructor) { + continue; + } + + Value receiver = invoke.getReceiver().getAliasedValue(aliasesThroughAssumeAndCheckCasts); + if (receiver != eligibleInstance) { + continue; + } + + DexEncodedMethod singleTarget = + invoke.lookupSingleTarget(appView, code.method.method.holder); + if (singleTarget == null || !indirectMethodCallsOnInstance.contains(singleTarget)) { + throw new IllegalClassInlinerStateException(); + } + + methodCallsOnInstance.put(invoke, new InliningInfo(singleTarget, null)); + } + } + currentUsers = indirectOutValueUsers; + } + + assert !methodCallsOnInstance.isEmpty(); + + inliner.performForcedInlining(method, code, methodCallsOnInstance, inliningIRProvider); + return true; + } + + private void removeAliasIntroducingInstructionsLinkedToEligibleInstance() { + Set<Instruction> currentUsers = eligibleInstance.uniqueUsers(); + while (!currentUsers.isEmpty()) { + Set<Instruction> indirectOutValueUsers = Sets.newIdentityHashSet(); + for (Instruction instruction : currentUsers) { + if (instruction.isAssume() || instruction.isCheckCast()) { + Value src = ListUtils.first(instruction.inValues()); + Value dest = instruction.outValue(); + indirectOutValueUsers.addAll(dest.uniqueUsers()); + assert !dest.hasPhiUsers(); + dest.replaceUsers(src); + removeInstruction(instruction); + } + } + currentUsers = indirectOutValueUsers; + } + + // Verify that no more assume or check-cast instructions are left as users. assert eligibleInstance.aliasedUsers().stream().noneMatch(Instruction::isAssume); + assert eligibleInstance.aliasedUsers().stream().noneMatch(Instruction::isCheckCast); } // Remove miscellaneous users before handling field reads. @@ -700,7 +766,13 @@ while (!currentUsers.isEmpty()) { Set<Instruction> indirectOutValueUsers = Sets.newIdentityHashSet(); for (Instruction instruction : currentUsers) { - if (instruction.isAssume()) { + if (instruction.isAssume() || instruction.isCheckCast()) { + if (instruction.isCheckCast()) { + CheckCast checkCast = instruction.asCheckCast(); + if (!appView.appInfo().isSubtype(eligibleClass.type, checkCast.getType())) { + return false; // Unsafe cast. + } + } Value outValueAlias = instruction.outValue(); if (outValueAlias.hasPhiUsers() || outValueAlias.hasDebugUsers()) { return false; @@ -714,11 +786,12 @@ if (instruction.isInvokeMethodWithReceiver()) { InvokeMethodWithReceiver user = instruction.asInvokeMethodWithReceiver(); - if (user.getReceiver().getAliasedValue() != outValue) { + if (user.getReceiver().getAliasedValue(aliasesThroughAssumeAndCheckCasts) != outValue) { return false; } for (int i = 1; i < user.inValues().size(); i++) { - if (user.inValues().get(i).getAliasedValue() == outValue) { + Value inValue = user.inValues().get(i); + if (inValue.getAliasedValue(aliasesThroughAssumeAndCheckCasts) == outValue) { return false; } } @@ -767,15 +840,45 @@ return null; } + ClassInlinerEligibilityInfo eligibility = + singleTarget.getOptimizationInfo().getClassInlinerEligibility(); + if (eligibility.callsReceiver.size() > 1) { + return null; + } + if (!eligibility.callsReceiver.isEmpty()) { + assert eligibility.callsReceiver.get(0).getFirst() == Invoke.Type.VIRTUAL; + DexMethod indirectlyInvokedMethod = eligibility.callsReceiver.get(0).getSecond(); + DexEncodedMethod indirectSingleTarget = + appView.appInfo().resolveMethod(eligibleClass, indirectlyInvokedMethod).getSingleTarget(); + if (indirectSingleTarget == null) { + return null; + } + if (!isEligibleIndirectVirtualMethodCall(indirectlyInvokedMethod, indirectSingleTarget)) { + return null; + } + indirectMethodCallsOnInstance.add(indirectSingleTarget); + } + return new InliningInfo(singleTarget, eligibleClass.type); } - private boolean isEligibleIndirectVirtualMethodCall(DexMethod callee) { + private boolean isEligibleIndirectVirtualMethodCall(DexMethod invokedMethod) { DexEncodedMethod singleTarget = - appView.appInfo().resolveMethod(eligibleClass, callee).getSingleTarget(); - return isEligibleSingleTarget(singleTarget) - && isEligibleVirtualMethodCall( - null, callee, singleTarget, eligibility -> eligibility.returnsReceiver.isFalse()); + appView.appInfo().resolveMethod(eligibleClass, invokedMethod).getSingleTarget(); + return isEligibleIndirectVirtualMethodCall(invokedMethod, singleTarget); + } + + private boolean isEligibleIndirectVirtualMethodCall( + DexMethod invokedMethod, DexEncodedMethod singleTarget) { + if (!isEligibleSingleTarget(singleTarget)) { + return false; + } + return isEligibleVirtualMethodCall( + null, + invokedMethod, + singleTarget, + eligibility -> + eligibility.callsReceiver.isEmpty() && eligibility.returnsReceiver.isFalse()); } private boolean isEligibleVirtualMethodCall( @@ -803,7 +906,7 @@ MethodOptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo(); ClassInlinerEligibilityInfo eligibility = optimizationInfo.getClassInlinerEligibility(); - if (eligibility == null || !eligibility.callsReceiver.isEmpty()) { + if (eligibility == null) { return false; } @@ -1009,6 +1112,7 @@ oracle.computeInlining( invoke, singleTarget, + method, ClassInitializationAnalysis.trivial(), NopWhyAreYouNotInliningReporter.getInstance()); if (inlineAction == null) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java index 8238723..b97cc0d 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -27,6 +27,7 @@ import static com.android.tools.r8.ir.code.Opcodes.INVOKE_NEW_ARRAY; import static com.android.tools.r8.ir.code.Opcodes.INVOKE_STATIC; import static com.android.tools.r8.ir.code.Opcodes.INVOKE_VIRTUAL; +import static com.android.tools.r8.ir.code.Opcodes.MONITOR; import static com.android.tools.r8.ir.code.Opcodes.MUL; import static com.android.tools.r8.ir.code.Opcodes.NEW_ARRAY_EMPTY; import static com.android.tools.r8.ir.code.Opcodes.NEW_INSTANCE; @@ -59,6 +60,8 @@ import com.android.tools.r8.ir.analysis.type.Nullability; import com.android.tools.r8.ir.analysis.type.TypeLatticeElement; import com.android.tools.r8.ir.analysis.value.AbstractValue; +import com.android.tools.r8.ir.code.AliasedValueConfiguration; +import com.android.tools.r8.ir.code.AssumeAndCheckCastAliasedValueConfiguration; import com.android.tools.r8.ir.code.BasicBlock; import com.android.tools.r8.ir.code.DominatorTree; import com.android.tools.r8.ir.code.FieldInstruction; @@ -99,6 +102,7 @@ import java.util.List; import java.util.Set; import java.util.function.BiFunction; +import java.util.function.Predicate; public class MethodOptimizationInfoCollector { private final AppView<AppInfoWithLiveness> appView; @@ -167,80 +171,79 @@ List<Pair<Invoke.Type, DexMethod>> callsReceiver = new ArrayList<>(); boolean seenSuperInitCall = false; boolean seenMonitor = false; - for (Instruction insn : receiver.aliasedUsers()) { - if (insn.isAssume()) { - continue; - } - if (insn.isMonitor()) { - seenMonitor = true; - continue; - } + AliasedValueConfiguration configuration = + AssumeAndCheckCastAliasedValueConfiguration.getInstance(); + Predicate<Value> isReceiverAlias = value -> value.getAliasedValue(configuration) == receiver; + for (Instruction insn : receiver.aliasedUsers(configuration)) { + switch (insn.opcode()) { + case ASSUME: + case CHECK_CAST: + case RETURN: + break; - if (insn.isInstanceGet() || insn.isInstancePut()) { - if (insn.isInstancePut()) { - InstancePut instancePutInstruction = insn.asInstancePut(); - // Only allow field writes to the receiver. - if (instancePutInstruction.object().getAliasedValue() != receiver) { + case MONITOR: + seenMonitor = true; + break; + + case INSTANCE_GET: + case INSTANCE_PUT: + { + if (insn.isInstancePut()) { + InstancePut instancePutInstruction = insn.asInstancePut(); + // Only allow field writes to the receiver. + if (!isReceiverAlias.test(instancePutInstruction.object())) { + return; + } + // Do not allow the receiver to escape via a field write. + if (isReceiverAlias.test(instancePutInstruction.value())) { + return; + } + } + DexField field = insn.asFieldInstruction().getField(); + if (appView.appInfo().resolveFieldOn(clazz, field) != null) { + // Require only accessing direct or indirect instance fields of the current class. + break; + } return; } - // Do not allow the receiver to escape via a field write. - if (instancePutInstruction.value().getAliasedValue() == receiver) { + + case INVOKE_DIRECT: + { + InvokeDirect invoke = insn.asInvokeDirect(); + DexMethod invokedMethod = invoke.getInvokedMethod(); + if (dexItemFactory.isConstructor(invokedMethod) + && invokedMethod.holder == clazz.superType + && ListUtils.lastIndexMatching(invoke.arguments(), isReceiverAlias) == 0 + && !seenSuperInitCall + && instanceInitializer) { + seenSuperInitCall = true; + break; + } + // We don't support other direct calls yet. return; } - } - DexField field = insn.asFieldInstruction().getField(); - if (appView.appInfo().resolveFieldOn(clazz, field) != null) { - // Require only accessing direct or indirect instance fields of the current class. - continue; - } - return; - } - // If this is an instance initializer allow one call to superclass instance initializer. - if (insn.isInvokeDirect()) { - InvokeDirect invokedDirect = insn.asInvokeDirect(); - DexMethod invokedMethod = invokedDirect.getInvokedMethod(); - if (dexItemFactory.isConstructor(invokedMethod) - && invokedMethod.holder == clazz.superType - && ListUtils.lastIndexMatching( - invokedDirect.inValues(), v -> v.getAliasedValue() == receiver) == 0 - && !seenSuperInitCall - && instanceInitializer) { - seenSuperInitCall = true; - continue; - } - // We don't support other direct calls yet. - return; - } - - if (insn.isInvokeVirtual()) { - InvokeVirtual invoke = insn.asInvokeVirtual(); - if (invoke.getReceiver().getAliasedValue() != receiver) { - return; // Not allowed. - } - for (int i = 1; i < invoke.arguments().size(); i++) { - Value argument = invoke.arguments().get(i); - if (argument.getAliasedValue() == receiver) { - return; // Not allowed. + case INVOKE_VIRTUAL: + { + InvokeVirtual invoke = insn.asInvokeVirtual(); + if (ListUtils.lastIndexMatching(invoke.arguments(), isReceiverAlias) != 0) { + return; // Not allowed. + } + DexMethod invokedMethod = invoke.getInvokedMethod(); + DexType returnType = invokedMethod.proto.returnType; + if (returnType.isClassType() + && appView.appInfo().isRelatedBySubtyping(returnType, method.method.holder)) { + return; // Not allowed, could introduce an alias of the receiver. + } + callsReceiver.add(new Pair<>(Invoke.Type.VIRTUAL, invokedMethod)); } - } - DexMethod invokedMethod = invoke.getInvokedMethod(); - DexType returnType = invokedMethod.proto.returnType; - if (returnType.isClassType() - && appView.appInfo().isRelatedBySubtyping(returnType, method.method.holder)) { - return; // Not allowed, could introduce an alias of the receiver. - } - callsReceiver.add(new Pair<>(Invoke.Type.VIRTUAL, invokedMethod)); - continue; - } + break; - if (insn.isReturn()) { - continue; + default: + // Other receiver usages make the method not eligible. + return; } - - // Other receiver usages make the method not eligible. - return; } if (instanceInitializer && !seenSuperInitCall) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java index f28afbe..5c45a37 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java
@@ -28,7 +28,8 @@ } @Override - public Reason computeInliningReason(InvokeMethod invoke, DexEncodedMethod target) { + public Reason computeInliningReason( + InvokeMethod invoke, DexEncodedMethod target, DexEncodedMethod context) { if (target.getOptimizationInfo().forceInline() || (appView.appInfo().hasLiveness() && appView.withLiveness().appInfo().forceInline.contains(target.method))) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/FixedInliningReasonStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/FixedInliningReasonStrategy.java new file mode 100644 index 0000000..2b4c072 --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/FixedInliningReasonStrategy.java
@@ -0,0 +1,24 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.ir.optimize.inliner; + +import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.ir.code.InvokeMethod; +import com.android.tools.r8.ir.optimize.Inliner.Reason; + +public class FixedInliningReasonStrategy implements InliningReasonStrategy { + + private final Reason reason; + + public FixedInliningReasonStrategy(Reason reason) { + this.reason = reason; + } + + @Override + public Reason computeInliningReason( + InvokeMethod invoke, DexEncodedMethod target, DexEncodedMethod context) { + return reason; + } +}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningReasonStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningReasonStrategy.java index 5450988..6e8e1aa 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningReasonStrategy.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningReasonStrategy.java
@@ -10,5 +10,6 @@ public interface InliningReasonStrategy { - Reason computeInliningReason(InvokeMethod invoke, DexEncodedMethod target); + Reason computeInliningReason( + InvokeMethod invoke, DexEncodedMethod target, DexEncodedMethod context); }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java index 8682fc1..5af6c6f 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
@@ -4,7 +4,6 @@ package com.android.tools.r8.ir.optimize.staticizer; -import com.android.tools.r8.graph.AppInfo; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexApplication; import com.android.tools.r8.graph.DexClass; @@ -16,6 +15,7 @@ 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.ResolutionResult; import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.Instruction; @@ -458,7 +458,7 @@ // Check constructor. InvokeDirect invoke = instruction.asInvokeDirect(); DexEncodedMethod methodInvoked = - appView.appInfo().lookupDirectTarget(invoke.getInvokedMethod()); + appView.appInfo().lookupDirectTarget(invoke.getInvokedMethod(), info.candidate); List<Value> values = invoke.inValues(); if (ListUtils.lastIndexMatching(values, v -> v.getAliasedValue() == candidateValue) != 0 @@ -599,10 +599,13 @@ } return candidateInfo.invalidate(); } - AppInfo appInfo = appView.appInfo(); - DexEncodedMethod methodInvoked = user.isInvokeDirect() - ? appInfo.lookupDirectTarget(methodReferenced) - : appInfo.lookupVirtualTarget(methodReferenced.holder, methodReferenced); + AppInfoWithLiveness appInfo = appView.appInfo(); + ResolutionResult resolutionResult = + appInfo.resolveMethod(methodReferenced.holder, methodReferenced); + DexEncodedMethod methodInvoked = + user.isInvokeDirect() + ? resolutionResult.lookupInvokeDirectTarget(candidateInfo.candidate, appInfo) + : resolutionResult.isVirtualTarget() ? resolutionResult.getSingleTarget() : null; if (ListUtils.lastIndexMatching(invoke.inValues(), isAliasedValue) == 0 && methodInvoked != null && methodInvoked.method.holder == candidateInfo.candidate.type) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java index 02b6e64..8e8d2da 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
@@ -415,7 +415,10 @@ Map<Instruction, BuilderState> perInstrState = getBuilderState(builder); BuilderState dominantState = findDominantState(dominatorTree, perInstrState, instr); if (dominantState != null) { - perInstrState.put(instr, dominantState); + // Instead of using the dominant state directly, treat this retrieval point as a new + // state without an addition so that dominant state can account for dependent states. + BuilderState currentState = dominantState.createChild(""); + perInstrState.put(instr, currentState); } else { // TODO(b/114002137): if we want to utilize partial results, don't remove it here. candidateBuilders.remove(builder);
diff --git a/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java b/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java deleted file mode 100644 index 88f89c3..0000000 --- a/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java +++ /dev/null
@@ -1,268 +0,0 @@ -// 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.jar; - -import static com.android.tools.r8.utils.InternalOptions.ASM_VERSION; - -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.DexClass; -import com.android.tools.r8.graph.DexEncodedMethod; -import com.android.tools.r8.graph.DexField; -import com.android.tools.r8.graph.DexMethod; -import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLense; -import com.android.tools.r8.graph.GraphLense.GraphLenseLookupResult; -import com.android.tools.r8.graph.JarApplicationReader; -import com.android.tools.r8.ir.code.Invoke; -import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; -import com.android.tools.r8.ir.optimize.InliningConstraints; -import com.android.tools.r8.shaking.AppInfoWithLiveness; -import org.objectweb.asm.ConstantDynamic; -import org.objectweb.asm.Handle; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; -import org.objectweb.asm.tree.TryCatchBlockNode; - -// This visitor can be used to determine if a piece of jar code has any instructions that the -// inliner would not be willing to inline. This can be used to determine if a method can be force -// inlined although its IR is still not available. -// -// Note that this class has only been implemented for the hooks in InliningConstraints that may -// return a non-ALWAYS inlining constraint (e.g., InliningConstraints.forReturn is not called). -public class InliningConstraintVisitor extends MethodVisitor { - - private final JarApplicationReader application; - private final AppView<AppInfoWithLiveness> appView; - private final GraphLense graphLense; - private final InliningConstraints inliningConstraints; - private final DexEncodedMethod method; - private final DexType invocationContext; - - private ConstraintWithTarget constraint; - - public InliningConstraintVisitor( - JarApplicationReader application, - AppView<AppInfoWithLiveness> appView, - GraphLense graphLense, - DexEncodedMethod method, - DexType invocationContext) { - super(ASM_VERSION); - assert graphLense.isContextFreeForMethods(); - this.application = application; - this.appView = appView; - this.graphLense = graphLense; - this.inliningConstraints = new InliningConstraints(appView, graphLense); - this.method = method; - this.invocationContext = invocationContext; - - // Model a synchronized method as having a monitor instruction. - this.constraint = method.accessFlags.isSynchronized() - ? inliningConstraints.forMonitor() : ConstraintWithTarget.ALWAYS; - } - - public void disallowStaticInterfaceMethodCalls() { - inliningConstraints.disallowStaticInterfaceMethodCalls(); - } - - public ConstraintWithTarget getConstraint() { - return constraint; - } - - private void updateConstraint(ConstraintWithTarget other) { - constraint = ConstraintWithTarget.meet(constraint, other, appView); - } - - // Used to signal that the result is ready, such that we do not need to visit all instructions of - // the method, if we can see early on that it cannot be inlined anyway. - public boolean isFinished() { - return constraint == ConstraintWithTarget.NEVER; - } - - public void accept(TryCatchBlockNode tryCatchBlock) { - // Model a try-catch as a move-exception instruction. - updateConstraint(inliningConstraints.forMoveException()); - } - - @Override - public void visitFieldInsn(int opcode, String owner, String name, String desc) { - DexField field = application.getField(owner, name, desc); - switch (opcode) { - case Opcodes.GETFIELD: - updateConstraint(inliningConstraints.forInstanceGet(field, invocationContext)); - break; - - case Opcodes.PUTFIELD: - updateConstraint(inliningConstraints.forInstancePut(field, invocationContext)); - break; - - case Opcodes.GETSTATIC: - updateConstraint(inliningConstraints.forStaticGet(field, invocationContext)); - break; - - case Opcodes.PUTSTATIC: - updateConstraint(inliningConstraints.forStaticPut(field, invocationContext)); - break; - - default: - throw new Unreachable("Unexpected opcode " + opcode); - } - } - - @Override - public void visitLdcInsn(Object cst) { - if (cst instanceof Type && ((Type) cst).getSort() != Type.METHOD) { - DexType type = application.getType((Type) cst); - updateConstraint(inliningConstraints.forConstClass(type, invocationContext)); - } else if (cst instanceof Handle) { - updateConstraint(inliningConstraints.forConstMethodHandle()); - } else if (cst instanceof ConstantDynamic) { - throw new CompilationError("Unsupported dynamic constant: " + cst.toString()); - } else { - updateConstraint(inliningConstraints.forConstInstruction()); - } - } - - @Override - public void visitInvokeDynamicInsn( - String name, - String descriptor, - Handle bootstrapMethodHandle, - Object... bootstrapMethodArguments) { - updateConstraint(inliningConstraints.forInvokeCustom()); - } - - @Override - public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { - DexType ownerType = application.getTypeFromName(owner); - DexMethod target = application.getMethod(ownerType, name, desc); - updateConstraint( - getConstraintForInvoke( - opcode, target, graphLense, appView, inliningConstraints, invocationContext)); - } - - public static ConstraintWithTarget getConstraintForInvoke( - int opcode, - DexMethod target, - GraphLense graphLense, - AppView<?> appView, - InliningConstraints inliningConstraints, - DexType invocationContext) { - // Find the DEX invocation type. - Invoke.Type type; - switch (opcode) { - case Opcodes.INVOKEINTERFACE: - // Could have changed to an invoke-virtual instruction due to vertical class merging - // (if an interface is merged into a class). - type = graphLense.lookupMethod(target, null, Invoke.Type.INTERFACE).getType(); - assert type == Invoke.Type.INTERFACE || type == Invoke.Type.VIRTUAL; - break; - - case Opcodes.INVOKESPECIAL: - if (appView.dexItemFactory().isConstructor(target)) { - type = Invoke.Type.DIRECT; - assert noNeedToUseGraphLense(target, type, graphLense); - } else if (target.holder == invocationContext) { - // The method could have been publicized. - type = graphLense.lookupMethod(target, null, Invoke.Type.DIRECT).getType(); - assert type == Invoke.Type.DIRECT || type == Invoke.Type.VIRTUAL; - } else { - // This is a super call. Note that the vertical class merger translates some invoke-super - // instructions to invoke-direct. However, when that happens, the invoke instruction and - // the target method end up being in the same class, and therefore, we will allow inlining - // it. The result of using type=SUPER below will be the same, since it leads to the - // inlining constraint SAMECLASS. - // TODO(christofferqa): Consider using graphLense.lookupMethod (to do this, we need the - // context for the graph lense, though). - type = Invoke.Type.SUPER; - assert noNeedToUseGraphLense(target, type, graphLense); - } - break; - - case Opcodes.INVOKESTATIC: { - // Static invokes may have changed as a result of horizontal class merging. - GraphLenseLookupResult lookup = graphLense.lookupMethod(target, null, Invoke.Type.STATIC); - target = lookup.getMethod(); - type = lookup.getType(); - break; - } - - case Opcodes.INVOKEVIRTUAL: { - type = Invoke.Type.VIRTUAL; - // Instructions that target a private method in the same class translates to - // invoke-direct. - if (target.holder == invocationContext) { - DexClass clazz = appView.definitionFor(target.holder); - if (clazz != null && clazz.lookupDirectMethod(target) != null) { - type = Invoke.Type.DIRECT; - } - } - - // Virtual invokes may have changed to interface invokes as a result of member rebinding. - GraphLenseLookupResult lookup = graphLense.lookupMethod(target, null, type); - target = lookup.getMethod(); - type = lookup.getType(); - break; - } - - default: - throw new Unreachable("Unexpected opcode " + opcode); - } - - return inliningConstraints.forInvoke(target, type, invocationContext); - } - - private static boolean noNeedToUseGraphLense( - DexMethod method, Invoke.Type type, GraphLense graphLense) { - assert graphLense.lookupMethod(method, null, type).getType() == type; - return true; - } - - @Override - public void visitInsn(int opcode) { - switch (opcode) { - case Opcodes.MONITORENTER: - case Opcodes.MONITOREXIT: - updateConstraint(inliningConstraints.forMonitor()); - break; - - default: - // All instructions here lead to the inlining constraint ALWAYS. - } - } - - @Override - public void visitMultiANewArrayInsn(String desc, int dims) { - DexType type = application.getTypeFromDescriptor(desc); - updateConstraint(inliningConstraints.forInvokeMultiNewArray(type, invocationContext)); - } - - @Override - public void visitTypeInsn(int opcode, String typeName) { - DexType type = application.getTypeFromName(typeName); - switch (opcode) { - case Opcodes.ANEWARRAY: - updateConstraint(inliningConstraints.forNewArrayEmpty(type, invocationContext)); - break; - - case Opcodes.CHECKCAST: - updateConstraint(inliningConstraints.forCheckCast(type, invocationContext)); - break; - - case Opcodes.INSTANCEOF: - updateConstraint(inliningConstraints.forInstanceOf(type, invocationContext)); - break; - - case Opcodes.NEW: - updateConstraint(inliningConstraints.forNewInstance(type, invocationContext)); - break; - - default: - throw new Unreachable("Unexpected opcode " + opcode); - } - } -}
diff --git a/src/main/java/com/android/tools/r8/jar/JarArgumentUseVisitor.java b/src/main/java/com/android/tools/r8/jar/JarArgumentUseVisitor.java deleted file mode 100644 index b46df5f..0000000 --- a/src/main/java/com/android/tools/r8/jar/JarArgumentUseVisitor.java +++ /dev/null
@@ -1,85 +0,0 @@ -// 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.jar; - -import static org.objectweb.asm.Opcodes.ALOAD; -import static com.android.tools.r8.utils.InternalOptions.ASM_VERSION; -import static org.objectweb.asm.Opcodes.DLOAD; -import static org.objectweb.asm.Opcodes.FLOAD; -import static org.objectweb.asm.Opcodes.ILOAD; -import static org.objectweb.asm.Opcodes.LLOAD; - -import com.android.tools.r8.graph.ArgumentUse; -import com.android.tools.r8.graph.DexEncodedMethod; -import com.android.tools.r8.graph.DexProto; -import com.android.tools.r8.graph.DexType; -import it.unimi.dsi.fastutil.ints.Int2IntArrayMap; -import it.unimi.dsi.fastutil.ints.Int2IntMap; -import org.objectweb.asm.MethodVisitor; - -public class JarArgumentUseVisitor extends MethodVisitor { - private final ArgumentUse registry; - private final Int2IntMap slotToArgument; - private final int arguments; - - public JarArgumentUseVisitor(DexEncodedMethod method, ArgumentUse registry) { - super(ASM_VERSION); - this.registry = registry; - - DexProto proto = method.method.proto; - boolean isStatic = method.accessFlags.isStatic(); - - boolean hasLongOrDouble = false; - for (DexType value : proto.parameters.values) { - if (value.isLongType() || value.isDoubleType()) { - hasLongOrDouble = true; - break; - } - } - if (hasLongOrDouble) { - this.slotToArgument = new Int2IntArrayMap(); - int nextSlot = 0; - int nextArgument = 0; - if (!isStatic) { - slotToArgument.put(nextSlot++, nextArgument++); - } - for (DexType value : proto.parameters.values) { - slotToArgument.put(nextSlot++, nextArgument++); - if (value.isLongType() || value.isDoubleType()) { - nextSlot++; - } - } - this.arguments = nextSlot; - } else { - this.slotToArgument = null; - this.arguments = proto.parameters.values.length + (isStatic ? 0 : 1); - } - } - - @Override - public void visitIincInsn(final int var, final int increment) { - if (var < arguments) { - registry.register(slotToArgument == null ? var : slotToArgument.get(var)); - } - } - - @Override - public void visitVarInsn(final int opcode, final int var) { - switch (opcode) { - case ILOAD: - case LLOAD: - case FLOAD: - case DLOAD: - case ALOAD: - if (var < arguments) { - registry.register(slotToArgument == null ? var : slotToArgument.get(var)); - } - break; - - default: - // Ignore - } - } -}
diff --git a/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java b/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java deleted file mode 100644 index fc9ea8b..0000000 --- a/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java +++ /dev/null
@@ -1,127 +0,0 @@ -// Copyright (c) 2016, 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.jar; - -import static com.android.tools.r8.utils.InternalOptions.ASM_VERSION; - -import com.android.tools.r8.dex.Constants; -import com.android.tools.r8.errors.CompilationError; -import com.android.tools.r8.errors.Unreachable; -import com.android.tools.r8.graph.DexCallSite; -import com.android.tools.r8.graph.DexField; -import com.android.tools.r8.graph.DexMethod; -import com.android.tools.r8.graph.DexMethodHandle; -import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.JarApplicationReader; -import com.android.tools.r8.graph.UseRegistry; -import com.android.tools.r8.graph.UseRegistry.MethodHandleUse; -import org.objectweb.asm.ConstantDynamic; -import org.objectweb.asm.Handle; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; - -public class JarRegisterEffectsVisitor extends MethodVisitor { - private final DexType clazz; - private final UseRegistry registry; - private final JarApplicationReader application; - - public JarRegisterEffectsVisitor(DexType clazz, UseRegistry registry, - JarApplicationReader application) { - super(ASM_VERSION); - this.clazz = clazz; - this.registry = registry; - this.application = application; - } - - @Override - public void visitTypeInsn(int opcode, String name) { - DexType type = application.getTypeFromName(name); - if (opcode == org.objectweb.asm.Opcodes.NEW) { - registry.registerNewInstance(type); - } else if (opcode == Opcodes.CHECKCAST) { - registry.registerCheckCast(type); - } else { - registry.registerTypeReference(type); - } - } - - @Override - public void visitMultiANewArrayInsn(String desc, int dims) { - registry.registerTypeReference(application.getTypeFromDescriptor(desc)); - } - - @Override - public void visitLdcInsn(Object cst) { - if (cst instanceof Type) { - if (((Type) cst).getSort() == Type.METHOD) { - String descriptor = ((Type) cst).getDescriptor(); - assert descriptor.charAt(0) == '('; - registry.registerProto(application.getProto(descriptor)); - } else { - registry.registerConstClass(application.getType((Type) cst)); - } - } else if (cst instanceof ConstantDynamic) { - throw new CompilationError("Unsupported dynamic constant: " + cst.toString()); - } else if (cst instanceof Handle) { - DexMethodHandle handle = DexMethodHandle.fromAsmHandle((Handle) cst, application, clazz); - registry.registerMethodHandle( - handle, MethodHandleUse.NOT_ARGUMENT_TO_LAMBDA_METAFACTORY); - } - } - - @Override - public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { - DexType ownerType = application.getTypeFromName(owner); - DexMethod method = application.getMethod(ownerType, name, desc); - switch (opcode) { - case Opcodes.INVOKEVIRTUAL: - registry.registerInvokeVirtual(method); - break; - case Opcodes.INVOKESTATIC: - registry.registerInvokeStatic(method); - break; - case Opcodes.INVOKEINTERFACE: - registry.registerInvokeInterface(method); - break; - case Opcodes.INVOKESPECIAL: - if (name.equals(Constants.INSTANCE_INITIALIZER_NAME) || ownerType == clazz) { - registry.registerInvokeDirect(method); - } else { - registry.registerInvokeSuper(method); - } - break; - default: - throw new Unreachable("Unexpected opcode " + opcode); - } - } - - @Override - public void visitFieldInsn(int opcode, String owner, String name, String desc) { - DexField field = application.getField(owner, name, desc); - switch (opcode) { - case Opcodes.GETFIELD: - registry.registerInstanceFieldRead(field); - break; - case Opcodes.PUTFIELD: - registry.registerInstanceFieldWrite(field); - break; - case Opcodes.GETSTATIC: - registry.registerStaticFieldRead(field); - break; - case Opcodes.PUTSTATIC: - registry.registerStaticFieldWrite(field); - break; - default: - throw new Unreachable("Unexpected opcode " + opcode); - } - } - - @Override - public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) { - registry.registerCallSite( - DexCallSite.fromAsmInvokeDynamic(application, clazz, name, desc, bsm, bsmArgs)); - } - -}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java index ff97c60..c63f22e 100644 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
@@ -15,9 +15,7 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.naming.NamingLens; import com.android.tools.r8.shaking.AppInfoWithLiveness; -import java.util.ArrayList; import java.util.List; -import java.util.Map; import kotlinx.metadata.KmClass; import kotlinx.metadata.KmConstructor; import kotlinx.metadata.KmType; @@ -26,7 +24,7 @@ public class KotlinClass extends KotlinInfo<KotlinClassMetadata.Class> { - private KmClass kmClass; + KmClass kmClass; static KotlinClass fromKotlinClassMetadata( KotlinClassMetadata kotlinClassMetadata, DexClass clazz) { @@ -70,12 +68,12 @@ return; } List<KmConstructor> constructors = kmClass.getConstructors(); - List<KmConstructor> originalConstructors = new ArrayList<>(constructors); constructors.clear(); - for (Map.Entry<DexEncodedMethod, KmConstructor> entry : - clazz.kotlinConstructors(originalConstructors, appView).entrySet()) { - KmConstructor constructor = - toRenamedKmConstructor(entry.getKey(), entry.getValue(), appView, lens); + for (DexEncodedMethod method : clazz.directMethods()) { + if (!method.isInstanceInitializer()) { + continue; + } + KmConstructor constructor = toRenamedKmConstructor(method, appView, lens); if (constructor != null) { constructors.add(constructor); }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java index a9a4b9c..94a993b 100644 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
@@ -14,7 +14,7 @@ public final class KotlinClassPart extends KotlinInfo<KotlinClassMetadata.MultiFileClassPart> { - private KmPackage kmPackage; + KmPackage kmPackage; static KotlinClassPart fromKotlinClassMetadata( KotlinClassMetadata kotlinClassMetadata, DexClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java index 855a428..9eb70f8 100644 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
@@ -14,7 +14,7 @@ public final class KotlinFile extends KotlinInfo<KotlinClassMetadata.FileFacade> { - private KmPackage kmPackage; + KmPackage kmPackage; static KotlinFile fromKotlinClassMetadata( KotlinClassMetadata kotlinClassMetadata, DexClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java index 9cd71c3..ebd5524 100644 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java
@@ -4,7 +4,6 @@ package com.android.tools.r8.kotlin; -import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.isExtension; import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedKmFunction; import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedKmFunctionAsExtension; @@ -14,15 +13,12 @@ import com.android.tools.r8.naming.NamingLens; import com.android.tools.r8.shaking.AppInfoWithLiveness; import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; import kotlinx.metadata.KmDeclarationContainer; import kotlinx.metadata.KmFunction; -import kotlinx.metadata.KmProperty; import kotlinx.metadata.jvm.KotlinClassHeader; import kotlinx.metadata.jvm.KotlinClassMetadata; -// Provides access to kotlin information. +// Provides access to package/class-level Kotlin information. public abstract class KotlinInfo<MetadataKind extends KotlinClassMetadata> { final MetadataKind metadata; final DexClass clazz; @@ -91,6 +87,10 @@ return null; } + boolean hasDeclarations() { + return isClass() || isFile() || isClassPart(); + } + @Override public String toString() { return (clazz != null ? clazz.toSourceString() : "<null class?!>") @@ -106,30 +106,38 @@ assert clazz != null; List<KmFunction> functions = kmDeclarationContainer.getFunctions(); - List<KmFunction> originalFunctions = - functions.stream() - .filter(kmFunction -> !isExtension(kmFunction)) - .collect(Collectors.toList()); - List<KmFunction> originalExtensions = - functions.stream() - .filter(KotlinMetadataSynthesizer::isExtension) - .collect(Collectors.toList()); functions.clear(); + // TODO(b/70169921): clear property + for (DexEncodedMethod method : clazz.methods()) { + if (method.isInitializer()) { + continue; + } - List<KmProperty> properties = kmDeclarationContainer.getProperties(); - for (DexEncodedMethod method : clazz.kotlinFunctions(originalFunctions, properties, appView)) { - KmFunction function = toRenamedKmFunction(method, null, appView, lens); - if (function != null) { - functions.add(function); + if (method.isKotlinExtensionFunction()) { + KmFunction extension = toRenamedKmFunctionAsExtension(method, appView, lens); + if (extension != null) { + functions.add(extension); + } + continue; } - } - for (Map.Entry<DexEncodedMethod, KmFunction> entry : - clazz.kotlinExtensions(originalExtensions, appView).entrySet()) { - KmFunction extension = - toRenamedKmFunctionAsExtension(entry.getKey(), entry.getValue(), appView, lens); - if (extension != null) { - functions.add(extension); + if (method.isKotlinFunction()) { + KmFunction function = toRenamedKmFunction(method, appView, lens); + if (function != null) { + functions.add(function); + } + continue; } + if (method.isKotlinExtensionProperty()) { + // TODO(b/70169921): (extension) property + continue; + } + if (method.isKotlinProperty()) { + // TODO(b/70169921): (extension) property + continue; + } + + // TODO(b/70169921): What should we do for methods that fall into this category---no mark? } + // TODO(b/70169921): (extension) companion } }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java new file mode 100644 index 0000000..82bafe6 --- /dev/null +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java
@@ -0,0 +1,171 @@ +// 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; + +import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.toJvmFieldSignature; +import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.toJvmMethodSignature; +import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.isExtension; + +import com.android.tools.r8.errors.Unreachable; +import com.android.tools.r8.graph.DexClass; +import com.android.tools.r8.graph.DexEncodedField; +import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.KmFunctionProcessor; +import com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.KmPropertyProcessor; +import java.util.HashMap; +import java.util.Map; +import kotlinx.metadata.KmDeclarationContainer; +import kotlinx.metadata.KmFunction; +import kotlinx.metadata.KmProperty; + +// Provides access to field/method-level Kotlin information. +public class KotlinMemberInfo { + private static KotlinMemberInfo NO_KOTLIN_MEMBER_INFO = new KotlinMemberInfo(MemberKind.NONE, 0); + + public static KotlinMemberInfo getNoKotlinMemberInfo() { + return NO_KOTLIN_MEMBER_INFO; + } + + public final MemberKind memberKind; + public final int flag; + // TODO(b/70169921): for (extension) property, we may need to group/track the original shape of + // the property. Then, at then end, metadata for only live ones will be synthesized. + + private KotlinMemberInfo(MemberKind memberKind, int flag) { + this.memberKind = memberKind; + this.flag = flag; + } + + public enum MemberKind { + NONE, + + FUNCTION, + EXTENSION_FUNCTION, + + PROPERTY_BACKING_FIELD, + PROPERTY_GETTER, + PROPERTY_SETTER, + PROPERTY_ANNOTATIONS, + + // No backing field for extension property. + EXTENSION_PROPERTY_GETTER, + EXTENSION_PROPERTY_SETTER, + EXTENSION_PROPERTY_ANNOTATIONS; + + // TODO(b/70169921): (extension) companion + + public boolean isFunction() { + return this == FUNCTION || isExtensionFunction(); + } + + public boolean isExtensionFunction() { + return this == EXTENSION_FUNCTION; + } + + public boolean isBackingField() { + return this == PROPERTY_BACKING_FIELD; + } + + public boolean isProperty() { + return isBackingField() + || this == PROPERTY_GETTER + || this == PROPERTY_SETTER + || this == PROPERTY_ANNOTATIONS + || isExtensionProperty(); + } + + public boolean isExtensionProperty() { + return this == EXTENSION_PROPERTY_GETTER + || this == EXTENSION_PROPERTY_SETTER + || this == EXTENSION_PROPERTY_ANNOTATIONS; + } + } + + public static void markKotlinMemberInfo(DexClass clazz, KotlinInfo kotlinInfo) { + if (kotlinInfo == null || !kotlinInfo.hasDeclarations()) { + return; + } + if (kotlinInfo.isClass()) { + markKotlinMemberInfo(clazz, kotlinInfo.asClass().kmClass); + } else if (kotlinInfo.isFile()) { + markKotlinMemberInfo(clazz, kotlinInfo.asFile().kmPackage); + } else if (kotlinInfo.isClassPart()) { + markKotlinMemberInfo(clazz, kotlinInfo.asClassPart().kmPackage); + } else { + throw new Unreachable("Unexpected KotlinInfo: " + kotlinInfo); + } + } + + private static void markKotlinMemberInfo( + DexClass clazz, KmDeclarationContainer kmDeclarationContainer) { + Map<String, KmFunction> kmFunctionMap = new HashMap<>(); + Map<String, KmProperty> kmPropertyFieldMap = new HashMap<>(); + Map<String, KmProperty> kmPropertyGetterMap = new HashMap<>(); + Map<String, KmProperty> kmPropertySetterMap = new HashMap<>(); + + kmDeclarationContainer.getFunctions().forEach(kmFunction -> { + KmFunctionProcessor functionProcessor = new KmFunctionProcessor(kmFunction); + if (functionProcessor.signature() != null) { + kmFunctionMap.put(functionProcessor.signature().asString(), kmFunction); + } + }); + kmDeclarationContainer.getProperties().forEach(kmProperty -> { + KmPropertyProcessor propertyProcessor = new KmPropertyProcessor(kmProperty); + if (propertyProcessor.fieldSignature() != null) { + kmPropertyFieldMap.put(propertyProcessor.fieldSignature().asString(), kmProperty); + } + if (propertyProcessor.getterSignature() != null) { + kmPropertyGetterMap.put(propertyProcessor.getterSignature().asString(), kmProperty); + } + if (propertyProcessor.setterSignature() != null) { + kmPropertySetterMap.put(propertyProcessor.setterSignature().asString(), kmProperty); + } + // TODO(b/70169921): property annotations + }); + + for (DexEncodedField field : clazz.fields()) { + String key = toJvmFieldSignature(field.field).asString(); + if (kmPropertyFieldMap.containsKey(key)) { + KmProperty kmProperty = kmPropertyFieldMap.get(key); + field.setKotlinMemberInfo( + new KotlinMemberInfo(MemberKind.PROPERTY_BACKING_FIELD, kmProperty.getFlags())); + } + } + + for (DexEncodedMethod method : clazz.methods()) { + if (method.isInitializer()) { + continue; + } + String key = toJvmMethodSignature(method.method).asString(); + if (kmFunctionMap.containsKey(key)) { + KmFunction kmFunction = kmFunctionMap.get(key); + if (isExtension(kmFunction)) { + method.setKotlinMemberInfo( + new KotlinMemberInfo(MemberKind.EXTENSION_FUNCTION, kmFunction.getFlags())); + } else { + method.setKotlinMemberInfo( + new KotlinMemberInfo(MemberKind.FUNCTION, kmFunction.getFlags())); + } + } else if (kmPropertyGetterMap.containsKey(key)) { + KmProperty kmProperty = kmPropertyGetterMap.get(key); + if (isExtension(kmProperty)) { + method.setKotlinMemberInfo( + new KotlinMemberInfo(MemberKind.EXTENSION_PROPERTY_GETTER, kmProperty.getFlags())); + } else { + method.setKotlinMemberInfo( + new KotlinMemberInfo(MemberKind.PROPERTY_GETTER, kmProperty.getFlags())); + } + } else if (kmPropertySetterMap.containsKey(key)) { + KmProperty kmProperty = kmPropertySetterMap.get(key); + if (isExtension(kmProperty)) { + method.setKotlinMemberInfo( + new KotlinMemberInfo(MemberKind.EXTENSION_PROPERTY_SETTER, kmProperty.getFlags())); + } else { + method.setKotlinMemberInfo( + new KotlinMemberInfo(MemberKind.PROPERTY_SETTER, kmProperty.getFlags())); + } + } + } + } +}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java index 85e3540..bdd7f60 100644 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java
@@ -3,9 +3,9 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin; -import com.google.common.collect.ImmutableList; -import java.util.Arrays; -import java.util.List; +import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.DexType; import kotlinx.metadata.KmConstructor; import kotlinx.metadata.KmConstructorExtensionVisitor; import kotlinx.metadata.KmConstructorVisitor; @@ -24,57 +24,19 @@ class KotlinMetadataJvmExtensionUtils { - private static boolean isValidJvmMethodSignature(String desc) { - return desc != null - && !desc.isEmpty() - && desc.charAt(0) == '(' - && desc.lastIndexOf('(') == 0 - && desc.indexOf(')') != -1 - && desc.indexOf(')') == desc.lastIndexOf(')') - && desc.lastIndexOf(')') < desc.length(); + static JvmFieldSignature toJvmFieldSignature(DexField field) { + return new JvmFieldSignature(field.name.toString(), field.type.toDescriptorString()); } - /** - * Extract return type from {@link JvmMethodSignature}. - * - * Example of JVM signature is: `JvmMethodSignature("getX", "()Ljava/lang/Object;").` - * In this case, the return type is "Ljava/lang/Object;". - */ - static String returnTypeFromJvmMethodSignature(JvmMethodSignature signature) { - if (signature == null) { - return null; + static JvmMethodSignature toJvmMethodSignature(DexMethod method) { + StringBuilder descBuilder = new StringBuilder(); + descBuilder.append("("); + for (DexType argType : method.proto.parameters.values) { + descBuilder.append(argType.toDescriptorString()); } - String desc = signature.getDesc(); - if (!isValidJvmMethodSignature(desc)) { - return null; - } - int index = desc.lastIndexOf(')'); - assert desc.charAt(0) == '(' && 0 < index && index < desc.length() : signature.asString(); - return desc.substring(index + 1); - } - - /** - * Extract parameters from {@link JvmMethodSignature}. - * - * Example of JVM signature is: `JvmMethodSignature("setX", "(Ljava/lang/Object;)V").` - * In this case, the parameter is the list with "Ljava/lang/Object;" as the first element. - */ - static List<String> parameterTypesFromJvmMethodSignature(JvmMethodSignature signature) { - if (signature == null) { - return null; - } - String desc = signature.getDesc(); - if (!isValidJvmMethodSignature(desc)) { - return null; - } - int index = desc.lastIndexOf(')'); - assert desc.charAt(0) == '(' && 0 < index && index < desc.length() : signature.asString(); - String params = desc.substring(1, index); - if (params.isEmpty()) { - return ImmutableList.of(); - } else { - return Arrays.asList(params.split(",")); - } + descBuilder.append(")"); + descBuilder.append(method.proto.returnType.toDescriptorString()); + return new JvmMethodSignature(method.name.toString(), descBuilder.toString()); } static class KmConstructorProcessor {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java index 7e4f7ca..a3e62c5 100644 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
@@ -4,10 +4,8 @@ package com.android.tools.r8.kotlin; import static com.android.tools.r8.kotlin.Kotlin.addKotlinPrefix; -import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.parameterTypesFromJvmMethodSignature; -import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.returnTypeFromJvmMethodSignature; +import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.toJvmMethodSignature; import static com.android.tools.r8.utils.DescriptorUtils.descriptorToInternalName; -import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromKmType; import static kotlinx.metadata.FlagsKt.flagsOf; import com.android.tools.r8.graph.AppView; @@ -16,9 +14,6 @@ import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.KmConstructorProcessor; -import com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.KmFunctionProcessor; -import com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.KmPropertyProcessor; import com.android.tools.r8.naming.NamingLens; import com.android.tools.r8.shaking.AppInfoWithLiveness; import java.util.List; @@ -27,14 +22,18 @@ import kotlinx.metadata.KmProperty; import kotlinx.metadata.KmType; import kotlinx.metadata.KmValueParameter; -import kotlinx.metadata.jvm.JvmMethodSignature; +import kotlinx.metadata.jvm.JvmExtensionsKt; -public class KotlinMetadataSynthesizer { +class KotlinMetadataSynthesizer { static boolean isExtension(KmFunction kmFunction) { return kmFunction.getReceiverParameterType() != null; } + static boolean isExtension(KmProperty kmProperty) { + return kmProperty.getReceiverParameterType() != null; + } + static KmType toKmType(String descriptor) { KmType kmType = new KmType(flagsOf()); kmType.visitClass(descriptorToInternalName(descriptor)); @@ -72,185 +71,8 @@ return kmType; } - private static boolean isCompatible(String desc, DexType type) { - if (desc == null || type == null) { - return false; - } - return desc.equals(type.toDescriptorString()); - } - - private static boolean isCompatible(KmType kmType, DexType type, AppView<?> appView) { - if (kmType == null || type == null) { - return false; - } - String descriptor = null; - if (appView.dexItemFactory().kotlin.knownTypeConversion.containsKey(type)) { - DexType convertedType = appView.dexItemFactory().kotlin.knownTypeConversion.get(type); - descriptor = convertedType.toDescriptorString(); - } - if (descriptor == null) { - descriptor = type.toDescriptorString(); - } - assert descriptor != null; - return descriptor.equals(getDescriptorFromKmType(kmType)); - } - - private static boolean isCompatibleJvmMethodSignature( - JvmMethodSignature signature, DexEncodedMethod method) { - String methodName = method.method.name.toString(); - if (!signature.getName().equals(methodName)) { - return false; - } - if (!isCompatible( - returnTypeFromJvmMethodSignature(signature), method.method.proto.returnType)) { - return false; - } - List<String> parameterTypes = parameterTypesFromJvmMethodSignature(signature); - if (parameterTypes == null || parameterTypes.size() != method.method.proto.parameters.size()) { - return false; - } - for (int i = 0; i < method.method.proto.parameters.size(); i++) { - if (!isCompatible(parameterTypes.get(i), method.method.proto.parameters.values[i])) { - return false; - } - } - return true; - } - - public static boolean isCompatibleConstructor( - KmConstructor constructor, DexEncodedMethod method, AppView<?> appView) { - // Note that targets for @JvmName don't include constructor. So, it's not necessary to process - // JvmMethodSignature inside JvmConstructorExtension. - List<KmValueParameter> parameters = constructor.getValueParameters(); - if (method.method.proto.parameters.size() != parameters.size()) { - return false; - } - for (int i = 0; i < method.method.proto.parameters.size(); i++) { - KmType kmType = parameters.get(i).getType(); - if (!isCompatible(kmType, method.method.proto.parameters.values[i], appView)) { - return false; - } - } - return true; - } - - public static boolean isCompatibleFunction( - KmFunction function, DexEncodedMethod method, AppView<?> appView) { - // Check if a custom name is set to avoid name clash. - KmFunctionProcessor kmFunctionProcessor = new KmFunctionProcessor(function); - JvmMethodSignature jvmMethodSignature = kmFunctionProcessor.signature(); - if (jvmMethodSignature != null && isCompatibleJvmMethodSignature(jvmMethodSignature, method)) { - return true; - } - - if (!function.getName().equals(method.method.name.toString())) { - return false; - } - if (!isCompatible(function.getReturnType(), method.method.proto.returnType, appView)) { - return false; - } - List<KmValueParameter> parameters = function.getValueParameters(); - if (method.method.proto.parameters.size() != parameters.size()) { - return false; - } - for (int i = 0; i < method.method.proto.parameters.size(); i++) { - KmType kmType = parameters.get(i).getType(); - if (!isCompatible(kmType, method.method.proto.parameters.values[i], appView)) { - return false; - } - } - return true; - } - - public static boolean isCompatibleExtension( - KmFunction extension, DexEncodedMethod method, AppView<?> appView) { - // Check if a custom name is set to avoid name clash. - KmFunctionProcessor kmFunctionProcessor = new KmFunctionProcessor(extension); - JvmMethodSignature jvmMethodSignature = kmFunctionProcessor.signature(); - if (jvmMethodSignature != null && isCompatibleJvmMethodSignature(jvmMethodSignature, method)) { - return true; - } - - if (!extension.getName().equals(method.method.name.toString())) { - return false; - } - if (!isCompatible(extension.getReturnType(), method.method.proto.returnType, appView)) { - return false; - } - List<KmValueParameter> parameters = extension.getValueParameters(); - if (method.method.proto.parameters.size() != parameters.size() + 1) { - return false; - } - assert method.method.proto.parameters.size() > 0; - assert extension.getReceiverParameterType() != null; - if (!isCompatible( - extension.getReceiverParameterType(), method.method.proto.parameters.values[0], appView)) { - return false; - } - for (int i = 1; i < method.method.proto.parameters.size(); i++) { - KmType kmType = parameters.get(i - 1).getType(); - if (!isCompatible(kmType, method.method.proto.parameters.values[i], appView)) { - return false; - } - } - return true; - } - - public static boolean isCompatibleProperty( - KmProperty kmProperty, DexEncodedMethod method, AppView<?> appView) { - KmPropertyProcessor kmPropertyProcessor = new KmPropertyProcessor(kmProperty); - // Check if a custom getter is defined via @get:JvmName("myGetter"). - JvmMethodSignature getterSignature = kmPropertyProcessor.getterSignature(); - if (getterSignature != null && isCompatibleJvmMethodSignature(getterSignature, method)) { - return true; - } - // Check if a custom setter is defined via @set:JvmName("mySetter"). - JvmMethodSignature setterSignature = kmPropertyProcessor.setterSignature(); - if (setterSignature != null && isCompatibleJvmMethodSignature(setterSignature, method)) { - return true; - } - - // E.g., property `prop: T` is mapped to `getProp()T`, `setProp(T)V`, `prop$annotations()V`. - // For boolean property, though, getter is mapped to `isProp()Z`. - // TODO(b/70169921): Avoid decoding. - String methodName = method.method.name.toString(); - if (!methodName.startsWith("is") - && !methodName.startsWith("get") - && !methodName.startsWith("set") - && !methodName.endsWith("$annotations")) { - return false; - } - - String propertyName = kmProperty.getName(); - assert propertyName.length() > 0; - String annotations = propertyName + "$annotations"; - if (methodName.equals(annotations)) { - return true; - } - String capitalized = - Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1); - String returnTypeDescriptor = getDescriptorFromKmType(kmProperty.returnType); - String getterPrefix = - returnTypeDescriptor != null && returnTypeDescriptor.endsWith("Boolean;") ? "is" : "get"; - String getter = getterPrefix + capitalized; - if (methodName.equals(getter) - && method.method.proto.parameters.size() == 0 - && isCompatible(kmProperty.returnType, method.method.proto.returnType, appView)) { - return true; - } - String setter = "set" + capitalized; - if (methodName.equals(setter) - && method.method.proto.returnType.isVoidType() - && method.method.proto.parameters.size() == 1 - && isCompatible(kmProperty.returnType, method.method.proto.parameters.values[0], appView)) { - return true; - } - return false; - } - static KmConstructor toRenamedKmConstructor( DexEncodedMethod method, - KmConstructor original, AppView<AppInfoWithLiveness> appView, NamingLens lens) { // Make sure it is an instance initializer and live. @@ -258,17 +80,9 @@ || !appView.appInfo().liveMethods.contains(method.method)) { return null; } - // TODO(b/70169921): {@link KmConstructor.extensions} is private, i.e., no way to alter! - // Thus, we rely on original metadata for now. - KmConstructorProcessor kmConstructorProcessor = new KmConstructorProcessor(original); - JvmMethodSignature jvmMethodSignature = kmConstructorProcessor.signature(); - KmConstructor kmConstructor = - jvmMethodSignature != null - ? original - // TODO(b/70169921): Consult kotlinx.metadata.Flag.Constructor to set IS_PRIMARY. - : new KmConstructor(method.accessFlags.getAsKotlinFlags()); + KmConstructor kmConstructor = new KmConstructor(method.accessFlags.getAsKotlinFlags()); + JvmExtensionsKt.setSignature(kmConstructor, toJvmMethodSignature(method.method)); List<KmValueParameter> parameters = kmConstructor.getValueParameters(); - parameters.clear(); if (!populateKmValueParameters(parameters, method, appView, lens, false)) { return null; } @@ -277,23 +91,20 @@ static KmFunction toRenamedKmFunction( DexEncodedMethod method, - KmFunction original, AppView<AppInfoWithLiveness> appView, NamingLens lens) { - return toRenamedKmFunctionHelper(method, original, appView, lens, false); + return toRenamedKmFunctionHelper(method, appView, lens, false); } static KmFunction toRenamedKmFunctionAsExtension( DexEncodedMethod method, - KmFunction original, AppView<AppInfoWithLiveness> appView, NamingLens lens) { - return toRenamedKmFunctionHelper(method, original, appView, lens, true); + return toRenamedKmFunctionHelper(method, appView, lens, true); } private static KmFunction toRenamedKmFunctionHelper( DexEncodedMethod method, - KmFunction original, AppView<AppInfoWithLiveness> appView, NamingLens lens, boolean isExtension) { @@ -307,21 +118,17 @@ // For a library method override, we should not have renamed it. assert !method.isLibraryMethodOverride().isTrue() || renamedMethod == method.method : method.toSourceString() + " -> " + renamedMethod.toSourceString(); - // TODO(b/70169921): {@link KmFunction.extensions} is private, i.e., no way to alter! - // Thus, we rely on original metadata for now. - assert !isExtension || original != null; KmFunction kmFunction = - isExtension - ? original - // TODO(b/70169921): Consult kotlinx.metadata.Flag.Function for kind (e.g., suspend). - : new KmFunction(method.accessFlags.getAsKotlinFlags(), renamedMethod.name.toString()); + new KmFunction(method.accessFlags.getAsKotlinFlags(), renamedMethod.name.toString()); + JvmExtensionsKt.setSignature(kmFunction, toJvmMethodSignature(method.method)); KmType kmReturnType = toRenamedKmType(method.method.proto.returnType, appView, lens); if (kmReturnType == null) { return null; } kmFunction.setReturnType(kmReturnType); if (isExtension) { - assert method.method.proto.parameters.values.length > 0; + assert method.method.proto.parameters.values.length > 0 + : method.method.toSourceString(); KmType kmReceiverType = toRenamedKmType(method.method.proto.parameters.values[0], appView, lens); if (kmReceiverType == null) { @@ -330,7 +137,6 @@ kmFunction.setReceiverParameterType(kmReceiverType); } List<KmValueParameter> parameters = kmFunction.getValueParameters(); - parameters.clear(); if (!populateKmValueParameters(parameters, method, appView, lens, isExtension)) { return null; }
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java index d0240e9..d13cc3b 100644 --- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java +++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -13,6 +13,7 @@ import com.android.tools.r8.graph.DexClass; 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.DexProto; import com.android.tools.r8.graph.DexString; import com.android.tools.r8.graph.DexType; @@ -24,12 +25,14 @@ import com.android.tools.r8.utils.DescriptorUtils; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode; +import com.android.tools.r8.utils.IteratorUtils; import com.android.tools.r8.utils.Timing; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -132,7 +135,15 @@ timing.end(); timing.begin("rename-generic"); - new GenericSignatureRewriter(appView, renaming).run(classes); + new GenericSignatureRewriter(appView, renaming) + .run( + new Iterable<DexProgramClass>() { + @Override + public Iterator<DexProgramClass> iterator() { + return IteratorUtils.<DexClass, DexProgramClass>filter( + classes.iterator(), DexClass::isProgramClass); + } + }); timing.end(); timing.begin("rename-arrays");
diff --git a/src/main/java/com/android/tools/r8/naming/ClassRenamingMapper.java b/src/main/java/com/android/tools/r8/naming/ClassRenamingMapper.java deleted file mode 100644 index 8609fff..0000000 --- a/src/main/java/com/android/tools/r8/naming/ClassRenamingMapper.java +++ /dev/null
@@ -1,104 +0,0 @@ -// 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.naming; - -import com.android.tools.r8.utils.BiMapContainer; -import com.google.common.collect.ImmutableBiMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; -import java.util.Map; - -/** - * Provides a translation between class names based on a source and target proguard map. - * - * A mapping consists of: - * - * <ul> - * <li> {@link #translation} a bidirectional mapping between obfuscated names on the source - * proguard map to the corresponding class in the target proguard map - * <li> {@link #newClasses} a set of the unobfuscated names of classes that are in the source but - * not the target map - * <li> {@link #unusedNames} a set of names in the target map that are not used by the source map - * </ul> - */ -public class ClassRenamingMapper { - - public static ClassRenamingMapper from(ClassNameMapper originalMap, ClassNameMapper targetMap) { - ImmutableBiMap.Builder<String, String> translationBuilder = ImmutableBiMap.builder(); - ImmutableSet.Builder<String> newClasses = ImmutableSet.builder(); - - Map<String, String> sourceOriginalToObfuscated = - originalMap.getObfuscatedToOriginalMapping().inverse; - - BiMapContainer<String, String> targetMapping = targetMap.getObfuscatedToOriginalMapping(); - Map<String, String> targetOriginalToObfuscated = targetMapping.inverse; - - for (String originalName : sourceOriginalToObfuscated.keySet()) { - String sourceObfuscatedName = sourceOriginalToObfuscated.get(originalName); - String targetObfuscatedName = targetOriginalToObfuscated.get(originalName); - if (targetObfuscatedName == null) { - newClasses.add(originalName); - continue; - } - translationBuilder.put(sourceObfuscatedName, targetObfuscatedName); - } - - ImmutableBiMap<String, String> translation = translationBuilder.build(); - ImmutableSet<String> unusedNames = - ImmutableSet.copyOf(Sets.difference(targetMapping.original.keySet(), translation.values())); - - return new ClassRenamingMapper(translation, newClasses.build(), unusedNames); - } - - /** - * Mapping from obfuscated class names in the source map to their counterpart in the target name - * map. - */ - public final ImmutableBiMap<String, String> translation; - - /** - * Set of (unobfuscated) class names that are present in the source map but not in the target map. - */ - public final ImmutableSet<String> newClasses; - - /** - * Set of (obfuscated) class names that are present in the target map but not in the source map. - */ - public final ImmutableSet<String> unusedNames; - - private ClassRenamingMapper(ImmutableBiMap<String, String> translation, - ImmutableSet<String> newClasses, ImmutableSet<String> unusedNames) { - this.translation = translation; - this.newClasses = newClasses; - this.unusedNames = unusedNames; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("Translation:\n\n"); - for (String name : translation.keySet()) { - String newName = translation.get(name); - builder.append(name.equals(newName) ? " " : " --- ") - .append(name) - .append(" -> ") - .append(newName) - .append('\n'); - } - builder.append("\nNew classes:\n\n"); - for (String name : newClasses) { - builder.append(" ") - .append(name) - .append('\n'); - } - builder.append("\nUnused names:\n\n"); - for (String unused : unusedNames) { - builder.append(" ") - .append(unused) - .append('\n'); - } - return builder.toString(); - } -}
diff --git a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java index 8ea3d93..b95f778 100644 --- a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java +++ b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
@@ -16,6 +16,7 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.InnerClassAttribute; import com.android.tools.r8.graph.ResolutionResult; +import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult; import com.android.tools.r8.naming.ClassNameMinifier.ClassRenaming; import com.android.tools.r8.naming.FieldNameMinifier.FieldRenaming; import com.android.tools.r8.naming.MethodNameMinifier.MethodRenaming; @@ -175,24 +176,11 @@ if (holder == null || holder.isNotProgramClass()) { return true; } - // We don't know which invoke type this method is used for, so checks that it has been - // rebound either way. - DexEncodedMethod staticTarget = appView.appInfo().lookupStaticTarget(item); - DexEncodedMethod directTarget = appView.appInfo().lookupDirectTarget(item); - DexEncodedMethod virtualTarget = appView.appInfo().lookupVirtualTarget(item.holder, item); - DexClass staticTargetHolder = - staticTarget != null ? appView.definitionFor(staticTarget.method.holder) : null; - DexClass directTargetHolder = - directTarget != null ? appView.definitionFor(directTarget.method.holder) : null; - DexClass virtualTargetHolder = - virtualTarget != null ? appView.definitionFor(virtualTarget.method.holder) : null; - assert (directTarget == null && staticTarget == null && virtualTarget == null) - || (virtualTarget != null && virtualTarget.method == item) - || (directTarget != null && directTarget.method == item) - || (staticTarget != null && staticTarget.method == item) - || (directTargetHolder != null && directTargetHolder.isNotProgramClass()) - || (virtualTargetHolder != null && virtualTargetHolder.isNotProgramClass()) - || (staticTargetHolder != null && staticTargetHolder.isNotProgramClass()) + SingleResolutionResult resolution = + appView.appInfo().resolveMethod(item.holder, item).asSingleResolution(); + // The resolution is either unknown or resolved to the item or a visibility bridge. + assert resolution == null + || resolution.getResolvedMethod().method == item || appView.unneededVisibilityBridgeMethods().contains(item); return true; }
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java index fab5f50..4fd6274 100644 --- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java +++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -495,7 +495,7 @@ DexString reservedName = getReservedName(definition, name, holder); if (reservedName != null) { if (!isAvailable.test(reservedName, reference)) { - reportReservationError(definition.asDexReference(), reservedName); + reportReservationError(definition.toReference(), reservedName); } return reservedName; }
diff --git a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureAction.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureAction.java index 6753e06..7cb3506 100644 --- a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureAction.java +++ b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureAction.java
@@ -9,11 +9,17 @@ */ public interface GenericSignatureAction<T> { + enum ParserPosition { + CLASS_SUPER_OR_INTERFACE_ANNOTATION, + ENCLOSING_INNER_OR_TYPE_ANNOTATION, + MEMBER_ANNOTATION + } + public void parsedSymbol(char symbol); public void parsedIdentifier(String identifier); - public T parsedTypeName(String name); + public T parsedTypeName(String name, ParserPosition isTopLevel); public T parsedInnerTypeName(T enclosingType, String name);
diff --git a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureParser.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureParser.java index c1f6a7d..74b17d6 100644 --- a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureParser.java +++ b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureParser.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.naming.signature; +import com.android.tools.r8.naming.signature.GenericSignatureAction.ParserPosition; import java.lang.reflect.GenericSignatureFormatError; import java.nio.CharBuffer; @@ -50,7 +51,7 @@ */ public class GenericSignatureParser<T> { - private final GenericSignatureAction<T> actions; + private GenericSignatureAction<T> actions; /* * Parser: @@ -110,7 +111,7 @@ try { actions.start(); setInput(signature); - parseFieldTypeSignature(); + parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION); actions.stop(); } catch (GenericSignatureFormatError e) { throw e; @@ -141,11 +142,11 @@ parseOptFormalTypeParameters(); // SuperclassSignature ::= ClassTypeSignature. - parseClassTypeSignature(); + parseClassTypeSignature(ParserPosition.CLASS_SUPER_OR_INTERFACE_ANNOTATION); while (symbol > 0) { // SuperinterfaceSignature ::= ClassTypeSignature. - parseClassTypeSignature(); + parseClassTypeSignature(ParserPosition.CLASS_SUPER_OR_INTERFACE_ANNOTATION); } } @@ -178,28 +179,28 @@ expect(':'); if (symbol == 'L' || symbol == '[' || symbol == 'T') { - parseFieldTypeSignature(); + parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION); } while (symbol == ':') { // InterfaceBound ::= ":" FieldTypeSignature. actions.parsedSymbol(symbol); scanSymbol(); - parseFieldTypeSignature(); + parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION); } } - private void parseFieldTypeSignature() { + private void parseFieldTypeSignature(ParserPosition parserPosition) { // FieldTypeSignature ::= ClassTypeSignature | ArrayTypeSignature | TypeVariableSignature. switch (symbol) { case 'L': - parseClassTypeSignature(); + parseClassTypeSignature(parserPosition); break; case '[': - // ArrayTypeSignature ::= "[" TypSignature. + // ArrayTypeSignature ::= "[" TypeSignature. actions.parsedSymbol(symbol); scanSymbol(); - updateTypeSignature(); + updateTypeSignature(parserPosition); break; case 'T': updateTypeVariableSignature(); @@ -209,7 +210,7 @@ } } - private void parseClassTypeSignature() { + private void parseClassTypeSignature(ParserPosition parserPosition) { // ClassTypeSignature ::= "L" {Ident "/"} Ident OptTypeArguments {"." Ident OptTypeArguments} // ";". actions.parsedSymbol(symbol); @@ -226,12 +227,12 @@ } qualIdent.append(this.identifier); - T parsedEnclosingType = actions.parsedTypeName(qualIdent.toString()); + T parsedEnclosingType = actions.parsedTypeName(qualIdent.toString(), parserPosition); updateOptTypeArguments(); while (symbol == '.') { - // Deal with Member Classes: + // Deal with Member Classes. actions.parsedSymbol(symbol); scanSymbol(); scanIdentifier(); @@ -268,13 +269,13 @@ } else if (symbol == '+') { actions.parsedSymbol(symbol); scanSymbol(); - parseFieldTypeSignature(); + parseFieldTypeSignature(ParserPosition.ENCLOSING_INNER_OR_TYPE_ANNOTATION); } else if (symbol == '-') { actions.parsedSymbol(symbol); scanSymbol(); - parseFieldTypeSignature(); + parseFieldTypeSignature(ParserPosition.ENCLOSING_INNER_OR_TYPE_ANNOTATION); } else { - parseFieldTypeSignature(); + parseFieldTypeSignature(ParserPosition.ENCLOSING_INNER_OR_TYPE_ANNOTATION); } } @@ -291,7 +292,7 @@ expect(';'); } - private void updateTypeSignature() { + private void updateTypeSignature(ParserPosition parserPosition) { switch (symbol) { case 'B': case 'C': @@ -306,7 +307,7 @@ break; default: // Not an elementary type, but a FieldTypeSignature. - parseFieldTypeSignature(); + parseFieldTypeSignature(parserPosition); } } @@ -319,7 +320,7 @@ expect('('); while (symbol != ')' && (symbol > 0)) { - updateTypeSignature(); + updateTypeSignature(ParserPosition.MEMBER_ANNOTATION); } actions.parsedSymbol(symbol); @@ -336,7 +337,7 @@ if (symbol == 'T') { updateTypeVariableSignature(); } else { - parseClassTypeSignature(); + parseClassTypeSignature(ParserPosition.MEMBER_ANNOTATION); } } while (symbol == '^'); } @@ -345,7 +346,7 @@ private void updateReturnType() { // ReturnType ::= TypeSignature | "V". if (symbol != 'V') { - updateTypeSignature(); + updateTypeSignature(ParserPosition.MEMBER_ANNOTATION); } else { actions.parsedSymbol(symbol); scanSymbol();
diff --git a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java index c3b7188..45c59a8 100644 --- a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java +++ b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
@@ -10,8 +10,8 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexAnnotation; import com.android.tools.r8.graph.DexAnnotationSet; -import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexDefinition; +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.origin.Origin; @@ -24,6 +24,7 @@ import java.util.Map; import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.function.Predicate; import java.util.function.Supplier; public class GenericSignatureRewriter { @@ -43,15 +44,16 @@ this.reporter = appView.options().reporter; } - public void run(Iterable<? extends DexClass> classes) { + public void run(Iterable<? extends DexProgramClass> classes) { final GenericSignatureCollector genericSignatureCollector = new GenericSignatureCollector(); final GenericSignatureParser<DexType> genericSignatureParser = new GenericSignatureParser<>(genericSignatureCollector); - // classes may not be the same as appInfo().classes() if applymapping is used on classpath + // Classes may not be the same as appInfo().classes() if applymapping is used on classpath // arguments. If that is the case, the ProguardMapMinifier will pass in all classes that is // either ProgramClass or has a mapping. This is then transitively called inside the // ClassNameMinifier. - for (DexClass clazz : classes) { + for (DexProgramClass clazz : classes) { + genericSignatureCollector.setCurrentClassContext(clazz); clazz.annotations = rewriteGenericSignatures( clazz.annotations, @@ -152,13 +154,28 @@ private class GenericSignatureCollector implements GenericSignatureAction<DexType> { private StringBuilder renamedSignature; + private DexProgramClass currentClassContext; + private DexType lastWrittenType = null; - public String getRenamedSignature() { + String getRenamedSignature() { return renamedSignature.toString(); } + void setCurrentClassContext(DexProgramClass clazz) { + currentClassContext = clazz; + } + @Override public void parsedSymbol(char symbol) { + if (symbol == ';' && lastWrittenType == null) { + // The type was never written (maybe because it was merged with it's subtype). + return; + } + // If the super-class or interface has been merged, we will stop writing out type + // arguments, resulting in a signature on the form '<>' if we do not remove it. + if (symbol == '>' && removeWrittenCharacter(c -> c == '<')) { + return; + } renamedSignature.append(symbol); } @@ -168,19 +185,56 @@ } @Override - public DexType parsedTypeName(String name) { - DexType type = appView.dexItemFactory().createType(getDescriptorFromClassBinaryName(name)); - type = appView.graphLense().lookupType(type); + public DexType parsedTypeName(String name, ParserPosition parserPosition) { + if (parserPosition == ParserPosition.ENCLOSING_INNER_OR_TYPE_ANNOTATION + && lastWrittenType == null) { + // We are writing type-arguments for a merged class. + removeWrittenClassCharacter(); + return null; + } + String originalDescriptor = getDescriptorFromClassBinaryName(name); + DexType type = + appView.graphLense().lookupType(appView.dexItemFactory().createType(originalDescriptor)); if (appView.appInfo().wasPruned(type)) { type = appView.dexItemFactory().objectType; } DexString renamedDescriptor = renaming.getOrDefault(type, type.descriptor); + if (parserPosition == ParserPosition.CLASS_SUPER_OR_INTERFACE_ANNOTATION + && currentClassContext != null) { + // We may have merged the type down to the current class type. + DexString classDescriptor = currentClassContext.type.descriptor; + if (!originalDescriptor.equals(classDescriptor.toString()) + && renamedDescriptor.equals(classDescriptor)) { + lastWrittenType = null; + removeWrittenClassCharacter(); + return type; + } + } renamedSignature.append(getClassBinaryNameFromDescriptor(renamedDescriptor.toString())); + lastWrittenType = type; return type; } + private boolean removeWrittenCharacter(Predicate<Character> removeIf) { + int index = renamedSignature.length() - 1; + if (index < 0 || !removeIf.test(renamedSignature.charAt(index))) { + return false; + } + renamedSignature.deleteCharAt(index); + return true; + } + + private void removeWrittenClassCharacter() { + removeWrittenCharacter(c -> c == 'L'); + } + @Override public DexType parsedInnerTypeName(DexType enclosingType, String name) { + if (enclosingType == null) { + // We are writing inner type names + removeWrittenClassCharacter(); + return null; + } assert enclosingType.isClassType(); String enclosingDescriptor = enclosingType.toDescriptorString(); DexType type = @@ -197,6 +251,7 @@ type = appView.graphLense().lookupType(type); DexString renamedDescriptor = renaming.get(type); if (renamedDescriptor != null) { + // TODO(b/147504070): If this is a merged class equal to the class context, do not add. // Pick the renamed inner class from the fully renamed binary name. String fullRenamedBinaryName = getClassBinaryNameFromDescriptor(renamedDescriptor.toString());
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 33e4eed..ac4f95f 100644 --- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java +++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -29,6 +29,7 @@ import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement; import com.android.tools.r8.ir.code.Invoke.Type; import com.android.tools.r8.utils.CollectionUtils; +import com.android.tools.r8.utils.PredicateSet; import com.android.tools.r8.utils.SetUtils; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; @@ -139,7 +140,7 @@ /** All methods that may not have any unused arguments removed. */ public final Set<DexMethod> keepUnusedArguments; /** All types that should be inlined if possible due to a configuration directive. */ - public final Set<DexType> alwaysClassInline; + public final PredicateSet<DexType> alwaysClassInline; /** All types that *must* never be inlined due to a configuration directive (testing only). */ public final Set<DexType> neverClassInline; /** All types that *must* never be merged due to a configuration directive (testing only). */ @@ -207,7 +208,7 @@ Set<DexMethod> whyAreYouNotInlining, Set<DexMethod> keepConstantArguments, Set<DexMethod> keepUnusedArguments, - Set<DexType> alwaysClassInline, + PredicateSet<DexType> alwaysClassInline, Set<DexType> neverClassInline, Set<DexType> neverMerge, Set<DexReference> neverPropagateValue, @@ -286,7 +287,7 @@ Set<DexMethod> whyAreYouNotInlining, Set<DexMethod> keepConstantArguments, Set<DexMethod> keepUnusedArguments, - Set<DexType> alwaysClassInline, + PredicateSet<DexType> alwaysClassInline, Set<DexType> neverClassInline, Set<DexType> neverMerge, Set<DexReference> neverPropagateValue, @@ -496,7 +497,7 @@ .map(this::definitionFor) .filter(Objects::nonNull) .collect(Collectors.toList())); - this.alwaysClassInline = rewriteItems(previous.alwaysClassInline, lense::lookupType); + this.alwaysClassInline = previous.alwaysClassInline.rewriteItems(lense::lookupType); this.neverClassInline = rewriteItems(previous.neverClassInline, lense::lookupType); this.neverMerge = rewriteItems(previous.neverMerge, lense::lookupType); this.neverPropagateValue = lense.rewriteReferencesConservatively(previous.neverPropagateValue);
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 2884fcd..4a8cdf3 100644 --- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java +++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -612,7 +612,7 @@ bootstrapMethods.add(callSite.bootstrapMethod.asMethod()); } - LambdaDescriptor descriptor = LambdaDescriptor.tryInfer(callSite, appInfo); + LambdaDescriptor descriptor = LambdaDescriptor.tryInfer(callSite, appInfo, context.holder); if (descriptor == null) { return; }
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java index 8555b93..f27bd26 100644 --- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java +++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -32,6 +32,7 @@ import com.android.tools.r8.utils.Consumer3; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.MethodSignatureEquivalence; +import com.android.tools.r8.utils.PredicateSet; import com.android.tools.r8.utils.StringDiagnostic; import com.android.tools.r8.utils.ThreadUtils; import com.google.common.base.Equivalence.Wrapper; @@ -82,7 +83,7 @@ private final Set<DexMethod> whyAreYouNotInlining = Sets.newIdentityHashSet(); private final Set<DexMethod> keepParametersWithConstantValue = Sets.newIdentityHashSet(); private final Set<DexMethod> keepUnusedArguments = Sets.newIdentityHashSet(); - private final Set<DexType> alwaysClassInline = Sets.newIdentityHashSet(); + private final PredicateSet<DexType> alwaysClassInline = new PredicateSet<>(); private final Set<DexType> neverClassInline = Sets.newIdentityHashSet(); private final Set<DexType> neverMerge = Sets.newIdentityHashSet(); private final Set<DexReference> neverPropagateValue = Sets.newIdentityHashSet(); @@ -295,7 +296,12 @@ } if (appView.options().protoShrinking().enableGeneratedMessageLiteBuilderShrinking) { GeneratedMessageLiteBuilderShrinker.addInliningHeuristicsForBuilderInlining( - appView, alwaysInline, neverInline, bypassClinitforInlining); + appView, + alwaysClassInline, + neverMerge, + alwaysInline, + neverInline, + bypassClinitforInlining); } assert Sets.intersection(neverInline, alwaysInline).isEmpty() && Sets.intersection(neverInline, forceInline).isEmpty() @@ -1123,7 +1129,7 @@ } switch (classInlineRule.getType()) { case ALWAYS: - alwaysClassInline.add(item.asDexClass().type); + alwaysClassInline.addElement(item.asDexClass().type); break; case NEVER: neverClassInline.add(item.asDexClass().type); @@ -1202,7 +1208,7 @@ public final Set<DexMethod> whyAreYouNotInlining; public final Set<DexMethod> keepConstantArguments; public final Set<DexMethod> keepUnusedArguments; - public final Set<DexType> alwaysClassInline; + public final PredicateSet<DexType> alwaysClassInline; public final Set<DexType> neverClassInline; public final Set<DexType> neverMerge; public final Set<DexReference> neverPropagateValue; @@ -1229,7 +1235,7 @@ Set<DexMethod> whyAreYouNotInlining, Set<DexMethod> keepConstantArguments, Set<DexMethod> keepUnusedArguments, - Set<DexType> alwaysClassInline, + PredicateSet<DexType> alwaysClassInline, Set<DexType> neverClassInline, Set<DexType> neverMerge, Set<DexReference> neverPropagateValue,
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 69b77cd..62241d5 100644 --- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java +++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -383,7 +383,7 @@ } } if (clazz.getEnclosingMethod() != null || !clazz.getInnerClasses().isEmpty()) { - // TODO(herhut): Consider supporting merging of enclosing-method and inner-class attributes. + // TODO(b/147504070): Consider merging of enclosing-method and inner-class attributes. if (Log.ENABLED) { AbortReason.UNSUPPORTED_ATTRIBUTES.printLogMessageForClass(clazz); } @@ -440,7 +440,7 @@ return false; } if (targetClass.getEnclosingMethod() != null || !targetClass.getInnerClasses().isEmpty()) { - // TODO(herhut): Consider supporting merging of enclosing-method and inner-class attributes. + // TODO(b/147504070): Consider merging of enclosing-method and inner-class attributes. if (Log.ENABLED) { AbortReason.UNSUPPORTED_ATTRIBUTES.printLogMessageForClass(clazz); }
diff --git a/src/main/java/com/android/tools/r8/utils/ArchiveResourceProvider.java b/src/main/java/com/android/tools/r8/utils/ArchiveResourceProvider.java index 5a50f1b..a66d383 100644 --- a/src/main/java/com/android/tools/r8/utils/ArchiveResourceProvider.java +++ b/src/main/java/com/android/tools/r8/utils/ArchiveResourceProvider.java
@@ -28,6 +28,7 @@ import java.util.Collections; import java.util.Enumeration; import java.util.List; +import java.util.function.Consumer; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; @@ -136,4 +137,43 @@ private boolean isProgramResourceName(String name) { return ZipUtils.isClassFile(name) || (ZipUtils.isDexFile(name) && !ignoreDexInArchive); } + + public void accept(Consumer<ProgramResource> visitor) throws ResourceException { + try (ZipFile zipFile = + FileUtils.createZipFile(archive.getPath().toFile(), StandardCharsets.UTF_8)) { + final Enumeration<? extends ZipEntry> entries = zipFile.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + String name = entry.getName(); + if (archive.matchesFile(name) && isProgramResourceName(name)) { + Origin entryOrigin = new ArchiveEntryOrigin(name, origin); + try (InputStream stream = zipFile.getInputStream(entry)) { + if (ZipUtils.isDexFile(name)) { + OneShotByteResource resource = + OneShotByteResource.create( + Kind.DEX, entryOrigin, ByteStreams.toByteArray(stream), null); + visitor.accept(resource); + } else if (ZipUtils.isClassFile(name)) { + OneShotByteResource resource = + OneShotByteResource.create( + Kind.CF, + entryOrigin, + ByteStreams.toByteArray(stream), + Collections.singleton(DescriptorUtils.guessTypeDescriptor(name))); + visitor.accept(resource); + } + } + } + } + } catch (ZipException e) { + throw new ResourceException( + origin, + new CompilationError("Zip error while reading '" + archive + "': " + e.getMessage(), e)); + } catch (IOException e) { + throw new ResourceException( + origin, + new CompilationError( + "I/O exception while reading '" + archive + "': " + e.getMessage(), e)); + } + } }
diff --git a/src/main/java/com/android/tools/r8/utils/CfLineToMethodMapper.java b/src/main/java/com/android/tools/r8/utils/CfLineToMethodMapper.java index 94024ee..2e34cd7 100644 --- a/src/main/java/com/android/tools/r8/utils/CfLineToMethodMapper.java +++ b/src/main/java/com/android/tools/r8/utils/CfLineToMethodMapper.java
@@ -27,7 +27,7 @@ } public String lookupNameAndDescriptor(String binaryName, int lineNumber) - throws IOException, ResourceException { + throws ResourceException { if (sourceMethodMapping.isEmpty()) { readLineNumbersFromClassFiles(); } @@ -35,13 +35,35 @@ return lineMappings == null ? null : lineMappings.get(lineNumber); } - private void readLineNumbersFromClassFiles() throws ResourceException, IOException { + private void readLineNumbersFromClassFiles() throws ResourceException { ClassVisitor classVisitor = new ClassVisitor(); for (ProgramResourceProvider resourceProvider : inputApp.getProgramResourceProviders()) { - for (ProgramResource programResource : resourceProvider.getProgramResources()) { - if (programResource.getKind() == Kind.CF) { - new ClassReader(StreamUtils.StreamToByteArrayClose(programResource.getByteStream())) - .accept(classVisitor, ClassReader.SKIP_FRAMES); + if (resourceProvider instanceof ArchiveResourceProvider) { + ArchiveResourceProvider provider = (ArchiveResourceProvider) resourceProvider; + provider.accept( + programResource -> { + if (programResource.getKind() != Kind.CF) { + return; + } + try { + new ClassReader(StreamUtils.StreamToByteArrayClose(programResource.getByteStream())) + .accept(classVisitor, ClassReader.SKIP_FRAMES); + } catch (IOException | ResourceException e) { + // Intentionally left empty because the addition of inline info for kotlin inline + // functions is a best effort. + } + }); + } else { + for (ProgramResource programResource : resourceProvider.getProgramResources()) { + if (programResource.getKind() == Kind.CF) { + try { + new ClassReader(StreamUtils.StreamToByteArrayClose(programResource.getByteStream())) + .accept(classVisitor, ClassReader.SKIP_FRAMES); + } catch (IOException e) { + // Intentionally left empty because the addition of inline info for kotlin inline + // functions is a best effort. + } + } } } }
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 c233df4..d168a64 100644 --- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java +++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -76,6 +76,8 @@ public enum DesugarState { OFF, + // This is for use when desugar has run before, and backports have still not been desugared. + ONLY_BACKPORT_STATICS, ON } @@ -218,8 +220,7 @@ public boolean enablePropagationOfDynamicTypesAtCallSites = true; // TODO(b/69963623): enable if everything is ready, including signature rewriting at call sites. public boolean enablePropagationOfConstantsAtCallSites = false; - // TODO(b/70169921): enable after branching. - public boolean enableKotlinMetadataRewriting = false; + public boolean enableKotlinMetadataRewriting = true; public boolean encodeChecksums = false; public BiPredicate<String, Long> dexClassChecksumFilter = (name, checksum) -> true; public boolean enableCfInterfaceMethodDesugaring = false; @@ -246,8 +247,7 @@ public boolean enableInitializedClassesInInstanceMethodsAnalysis = true; public boolean enableRedundantFieldLoadElimination = true; public boolean enableValuePropagation = true; - // TODO(b/125282093): Enable member value propagation for instance fields. - public boolean enableValuePropagationForInstanceFields = false; + public boolean enableValuePropagationForInstanceFields = true; public boolean enableUninstantiatedTypeOptimization = true; // Currently disabled, see b/146957343. public boolean enableUninstantiatedTypeOptimizationForInterfaces = false; @@ -1083,13 +1083,6 @@ enablePropagationOfConstantsAtCallSites = true; } - // TODO(b/70169921): Remove this once enabled. - @VisibleForTesting - public void enableKotlinMetadataRewriting() { - assert !enableKotlinMetadataRewriting; - enableKotlinMetadataRewriting = true; - } - private boolean hasMinApi(AndroidApiLevel level) { assert isGeneratingDex(); return minApiLevel >= level.getLevel();
diff --git a/src/main/java/com/android/tools/r8/utils/IteratorUtils.java b/src/main/java/com/android/tools/r8/utils/IteratorUtils.java index 7ffce35..f38c02a 100644 --- a/src/main/java/com/android/tools/r8/utils/IteratorUtils.java +++ b/src/main/java/com/android/tools/r8/utils/IteratorUtils.java
@@ -16,7 +16,8 @@ public class IteratorUtils { - public static <T, S extends T> Iterator<S> filter(Iterator<T> iterator, Predicate<T> predicate) { + public static <T, S extends T> Iterator<S> filter( + Iterator<? extends T> iterator, Predicate<T> predicate) { return new Iterator<S>() { private S next = advance();
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java index c7324da..df13ac6 100644 --- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java +++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -48,7 +48,6 @@ import com.android.tools.r8.naming.Range; import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization; import com.google.common.base.Suppliers; -import java.io.IOException; import java.util.ArrayList; import java.util.IdentityHashMap; import java.util.List; @@ -120,7 +119,6 @@ private KotlinInlineFunctionPositionRemapper( AppView<?> appView, - AndroidApp inputApp, PositionRemapper baseRemapper, CfLineToMethodMapper lineToMethodMapper) { this.appView = appView; @@ -172,7 +170,7 @@ } // This is the same position, so we should really not mark this as an inline position. Fall // through to the default case. - } catch (IOException | ResourceException ignored) { + } catch (ResourceException ignored) { // Intentionally left empty. Remapping of kotlin functions utility is a best effort mapping. } return baseRemapper.createRemappedPosition(position); @@ -322,7 +320,7 @@ // remapper to allow for remapping original positions to kotlin inline positions. KotlinInlineFunctionPositionRemapper kotlinRemapper = new KotlinInlineFunctionPositionRemapper( - appView, inputApp, positionRemapper, cfLineToMethodMapper); + appView, positionRemapper, cfLineToMethodMapper); for (DexEncodedMethod method : methods) { kotlinRemapper.currentMethod = method;
diff --git a/src/main/java/com/android/tools/r8/utils/PredicateSet.java b/src/main/java/com/android/tools/r8/utils/PredicateSet.java new file mode 100644 index 0000000..da15974 --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/PredicateSet.java
@@ -0,0 +1,49 @@ +// 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 com.google.common.collect.Sets; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Predicate; + +public class PredicateSet<T> { + + private final Set<T> elements = Sets.newIdentityHashSet(); + private final List<Predicate<T>> predicates = new ArrayList<>(); + + public boolean addElement(T element) { + return elements.add(element); + } + + public void addPredicate(Predicate<T> predicate) { + predicates.add(predicate); + } + + public PredicateSet<T> rewriteItems(Function<T, T> mapping) { + PredicateSet<T> set = new PredicateSet<>(); + for (T item : elements) { + set.elements.add(mapping.apply(item)); + } + // It is assumed that the predicates do not need rewriting. Otherwise, this method must be + // overwritten. + set.predicates.addAll(predicates); + return set; + } + + public boolean contains(T element) { + if (elements.contains(element)) { + return true; + } + for (Predicate<T> predicate : predicates) { + if (predicate.test(element)) { + return true; + } + } + return false; + } +}
diff --git a/src/main/java/com/android/tools/r8/utils/SimpleHashMap.java b/src/main/java/com/android/tools/r8/utils/SimpleHashMap.java deleted file mode 100644 index 73f607a..0000000 --- a/src/main/java/com/android/tools/r8/utils/SimpleHashMap.java +++ /dev/null
@@ -1,87 +0,0 @@ -// Copyright (c) 2016, 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; - -abstract class SimpleHashMap { - - private int size = 0; // number of elements in this hash map. - private int limit; // resize when size reaches limit. - private int mask; // length - 1. - - // Constants for capacity. - static final int MIN_CAPACITY = 2; - static final int DEFAULT_CAPACITY = 50; - - // Constant for loadfactor. - static final double MIN_LOAD_FACTOR = 0.2; - static final double MAX_LOAD_FACTOR = 0.8; - static final double DEFAULT_LOAD_FACTOR = 0.6; - - SimpleHashMap() { - this(DEFAULT_CAPACITY); - } - - SimpleHashMap(int initialCapacity) { - this(initialCapacity, DEFAULT_LOAD_FACTOR); - } - - SimpleHashMap(int initialCapacity, double loadFactor) { - initialCapacity = Math.max(MIN_CAPACITY, initialCapacity); - loadFactor = Math.min(MAX_LOAD_FACTOR, Math.max(MIN_LOAD_FACTOR, loadFactor)); - final int initialLength = roundToPow2((int) ((double) initialCapacity / loadFactor)); - initialize(initialLength, (int) ((double) initialLength * loadFactor)); - } - - public int size() { - return size; - } - - @Override - public String toString() { - return this.getClass().getName() + ", " + size + "(length " + length() + ")"; - } - - int length() { - return mask + 1; - } - - void initialize(final int length, final int limit) { - size = 0; - mask = length - 1; - this.limit = limit; - } - - void resize() { - // Double length and limit. - initialize(length() << 1, limit << 1); - } - - void ensureCapacity() { - if (size >= limit) { - resize(); - } - } - - void incrementSize() { - size++; - } - - private int roundToPow2(int number) { - number--; - number |= number >> 1; - number |= number >> 2; - number |= number >> 4; - number |= number >> 8; - number |= number >> 16; - return number + 1; - } - - int firstProbe(final int hash) { - return hash & mask; - } - - int nextProbe(final int last, final int index) { - return (last + index) & mask; - } -}
diff --git a/src/test/examplesProto/proto2/HasFlaggedOffExtensionBuilderTestClass.java b/src/test/examplesProto/proto2/HasFlaggedOffExtensionBuilderTestClass.java new file mode 100644 index 0000000..9cc9e84 --- /dev/null +++ b/src/test/examplesProto/proto2/HasFlaggedOffExtensionBuilderTestClass.java
@@ -0,0 +1,45 @@ +// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package proto2; + +import com.android.tools.r8.proto2.Shrinking.HasFlaggedOffExtension; +import com.google.protobuf.ExtensionRegistryLite; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.WireFormat; +import java.nio.ByteBuffer; + +public class HasFlaggedOffExtensionBuilderTestClass { + + // A protobuf payload indicating that varint field 1 is set to 42. + // See https://developers.google.com/protocol-buffers/docs/encoding + // + // Since serialization and deserialization use the same schema (which we're modifying), testing + // against wire-format data is preferred. + private static final byte[] FIELD1_SET_TO_42 = + new byte[] {(1 << 3) | WireFormat.WIRETYPE_VARINT, 42}; + + // A protobuf payload indicating that field 10 is a message whose field 1 is set to 42. + private static final byte[] MESSAGE10_WITH_FIELD1_SET_TO_42 = + ByteBuffer.allocate(4) + .put( + new byte[] { + (10 << 3) | WireFormat.WIRETYPE_LENGTH_DELIMITED, (byte) FIELD1_SET_TO_42.length + }) + .put(FIELD1_SET_TO_42) + .array(); + + public static void main(String[] args) { + HasFlaggedOffExtension msg; + try { + msg = + HasFlaggedOffExtension.parseFrom( + MESSAGE10_WITH_FIELD1_SET_TO_42, ExtensionRegistryLite.getGeneratedRegistry()); + } catch (InvalidProtocolBufferException e) { + System.out.println("Unexpected exception: " + e); + throw new RuntimeException(e); + } + Printer.print(HasFlaggedOffExtension.newBuilder(msg).build()); + } +}
diff --git a/src/test/examplesProto/proto2/Printer.java b/src/test/examplesProto/proto2/Printer.java index fd87ef0..0d6f506 100644 --- a/src/test/examplesProto/proto2/Printer.java +++ b/src/test/examplesProto/proto2/Printer.java
@@ -4,20 +4,25 @@ package proto2; +import com.android.tools.r8.proto2.Shrinking.HasFlaggedOffExtension; import com.android.tools.r8.proto2.TestProto.Primitives; public class Printer { - static void print(Primitives primitives) { - System.out.println(primitives.hasFooInt32()); - System.out.println(primitives.getFooInt32()); - System.out.println(primitives.hasOneofString()); - System.out.println(primitives.getOneofString()); - System.out.println(primitives.hasOneofUint32()); - System.out.println(primitives.getOneofUint32()); - System.out.println(primitives.hasBarInt64()); - System.out.println(primitives.getBarInt64()); - System.out.println(primitives.hasQuxString()); - System.out.println(primitives.getQuxString()); + static void print(HasFlaggedOffExtension msg) { + System.out.println(msg.getSerializedSize()); + } + + static void print(Primitives msg) { + System.out.println(msg.hasFooInt32()); + System.out.println(msg.getFooInt32()); + System.out.println(msg.hasOneofString()); + System.out.println(msg.getOneofString()); + System.out.println(msg.hasOneofUint32()); + System.out.println(msg.getOneofUint32()); + System.out.println(msg.hasBarInt64()); + System.out.println(msg.getBarInt64()); + System.out.println(msg.hasQuxString()); + System.out.println(msg.getQuxString()); } }
diff --git a/src/test/java/com/android/tools/r8/VersionTests.java b/src/test/java/com/android/tools/r8/VersionTests.java index dffaa2b..9db9512 100644 --- a/src/test/java/com/android/tools/r8/VersionTests.java +++ b/src/test/java/com/android/tools/r8/VersionTests.java
@@ -61,7 +61,13 @@ @Test public void testDevelopmentPredicate() { - assertEquals(LABEL.equals("master") || LABEL.contains("-dev"), Version.isDevelopmentVersion()); + if (LABEL.equals("master") || LABEL.contains("-dev")) { + assertTrue(Version.isDevelopmentVersion()); + } else { + // This is a release branch, but Version.isDevelopmentVersion will still return true + // since this is not the release archive with the r8-version.properties file. + assertFalse(Version.isDevelopmentVersion(LABEL, false)); + } } @Test
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/DesugarStaticBackportsOnly.java b/src/test/java/com/android/tools/r8/desugar/backports/DesugarStaticBackportsOnly.java new file mode 100644 index 0000000..cd6d160 --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/backports/DesugarStaticBackportsOnly.java
@@ -0,0 +1,136 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.desugar.backports; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.StringContains.containsString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.android.tools.r8.CompilationFailedException; +import com.android.tools.r8.D8TestBuilder; +import com.android.tools.r8.Diagnostic; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.InternalOptions.DesugarState; +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.android.tools.r8.utils.codeinspector.DexInstructionSubject; +import java.util.Arrays; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class DesugarStaticBackportsOnly extends TestBase { + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withDexRuntimes().withAllApiLevels().build(); + } + + public DesugarStaticBackportsOnly(TestParameters parameters) { + this.parameters = parameters; + } + + private void checkLongHashCodeDesugared(CodeInspector inspector) { + ClassSubject classSubject = inspector.clazz(TestClass.class); + assertThat(classSubject, isPresent()); + assertEquals( + parameters.getApiLevel().isLessThan(AndroidApiLevel.N), + classSubject + .uniqueMethodWithName("main") + .streamInstructions() + .anyMatch( + instructionSubject -> + instructionSubject.isInvokeStatic() + && instructionSubject + .toString() + .contains("$r8$backportedMethods$utility$Long$1$hashCode"))); + assertEquals( + parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N), + classSubject + .uniqueMethodWithName("main") + .streamInstructions() + .anyMatch( + instructionSubject -> + instructionSubject.isInvokeStatic() + && instructionSubject.toString().contains("java/lang/Long"))); + } + + @Test + public void testBackportDesugared() throws Exception { + String expectedOutput = StringUtils.lines("1234"); + testForD8() + .addProgramClasses(TestClass.class) + .setMinApi(parameters.getApiLevel()) + .addOptionsModification( + options -> options.desugarState = DesugarState.ONLY_BACKPORT_STATICS) + .compile() + .inspect(this::checkLongHashCodeDesugared) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(expectedOutput); + } + + private void checkLambdaNotDesugared(CodeInspector inspector) { + ClassSubject classSubject = inspector.clazz(TestClassLambda.class); + assertThat(classSubject, isPresent()); + assertTrue( + classSubject + .uniqueMethodWithName("main") + .streamInstructions() + .anyMatch( + instructionSubject -> + ((DexInstructionSubject) instructionSubject).isInvokeCustom())); + } + + @Test + public void testLambdaNotDesugared() throws Exception { + D8TestBuilder builder = + testForD8() + .addProgramClasses(TestClassLambda.class) + .setMinApi(parameters.getApiLevel()) + .addOptionsModification( + options -> options.desugarState = DesugarState.ONLY_BACKPORT_STATICS); + if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.O)) { + builder.compile().inspect(this::checkLambdaNotDesugared); + } else { + try { + builder.compileWithExpectedDiagnostics( + diagnostics -> { + diagnostics.assertOnlyErrors(); + diagnostics.assertErrorsCount(1); + Diagnostic diagnostic = diagnostics.getErrors().get(0); + assertThat( + diagnostic.getDiagnosticMessage(), + containsString("Invoke-customs are only supported starting with Android O")); + }); + } catch (CompilationFailedException e) { + // Expected compilation failed. + return; + } + fail("Expected test to fail with CompilationFailedException"); + } + } + + static class TestClass { + public static void main(String[] args) { + System.out.println(Long.hashCode(1234)); + } + } + + static class TestClassLambda { + public static void main(String[] args) { + Arrays.asList(args).forEach(s -> System.out.println(s)); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java index e49d9a5..07db6bd 100644 --- a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java +++ b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
@@ -5,6 +5,7 @@ package com.android.tools.r8.graph; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -77,13 +78,17 @@ ); AndroidApp application = buildApplication(builder); - AppInfo appInfo = computeAppInfo(application); + AppInfoWithClassHierarchy appInfo = computeAppInfoWithClassHierarchy(application); CodeInspector inspector = new CodeInspector(appInfo.app()); DexEncodedMethod method = getMethod(inspector, DEFAULT_CLASS_NAME, "int", "x", ImmutableList.of()); - assertNull(appInfo.lookupVirtualTarget(method.method.holder, method.method)); - assertNull(appInfo.lookupDirectTarget(method.method)); - assertNotNull(appInfo.lookupStaticTarget(method.method)); + assertFalse( + appInfo + .resolveMethod(method.method.holder, method.method) + .getSingleTarget() + .isVirtualMethod()); + assertNull(appInfo.lookupDirectTarget(method.method, method.method.holder)); + assertNotNull(appInfo.lookupStaticTarget(method.method, method.method.holder)); if (ToolHelper.getDexVm().getVersion().isOlderThanOrEqual(DexVm.Version.V4_4_4)) { // Dalvik rejects at verification time instead of producing the @@ -147,7 +152,7 @@ ); AndroidApp application = buildApplication(builder); - AppInfo appInfo = computeAppInfo(application); + AppInfoWithClassHierarchy appInfo = computeAppInfoWithClassHierarchy(application); CodeInspector inspector = new CodeInspector(appInfo.app()); DexMethod methodXOnTestSuper = @@ -162,16 +167,20 @@ DexMethod methodXOnTest = appInfo.dexItemFactory().createMethod(classTest, methodXProto, methodXName); - assertNull(appInfo.lookupVirtualTarget(classTestSuper, methodXOnTestSuper)); - assertNull(appInfo.lookupVirtualTarget(classTest, methodXOnTestSuper)); - assertNull(appInfo.lookupVirtualTarget(classTest, methodXOnTest)); + assertFalse( + appInfo + .resolveMethod(classTestSuper, methodXOnTestSuper) + .getSingleTarget() + .isVirtualMethod()); + assertNull(appInfo.resolveMethod(classTest, methodXOnTestSuper).getSingleTarget()); + assertNull(appInfo.resolveMethod(classTest, methodXOnTest).getSingleTarget()); - assertNull(appInfo.lookupDirectTarget(methodXOnTestSuper)); - assertNull(appInfo.lookupDirectTarget(methodXOnTest)); + assertNull(appInfo.lookupDirectTarget(methodXOnTestSuper, methodXOnTestSuper.holder)); + assertNull(appInfo.lookupDirectTarget(methodXOnTest, methodXOnTest.holder)); - assertNotNull(appInfo.lookupStaticTarget(methodXOnTestSuper)); + assertNotNull(appInfo.lookupStaticTarget(methodXOnTestSuper, methodXOnTestSuper.holder)); // Accessing a private target on a different type will fail resolution outright. - assertNull(appInfo.lookupStaticTarget(methodXOnTest)); + assertNull(appInfo.lookupStaticTarget(methodXOnTest, methodXOnTest.holder)); assertEquals("OK", runArt(application)); } @@ -197,7 +206,7 @@ ); AndroidApp application = buildApplication(builder); - AppInfo appInfo = computeAppInfo(application); + AppInfoWithClassHierarchy appInfo = computeAppInfoWithClassHierarchy(application); DexItemFactory factory = appInfo.dexItemFactory(); DexField aFieldOnSubClass = factory
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java index c2a5c6a..cffd09c 100644 --- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java +++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
@@ -56,7 +56,7 @@ private void testVirtualLookup(DexProgramClass clazz, DexEncodedMethod method) { // Check lookup will produce the same result. DexMethod id = method.method; - assertEquals(appInfo.lookupVirtualTarget(id.holder, method.method), method); + assertEquals(appInfo.resolveMethod(id.holder, method.method).getSingleTarget(), method); // Check lookup targets with include method. Set<DexEncodedMethod> targets =
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java index 7a158d3..6bf5d8f 100644 --- a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java +++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
@@ -7,6 +7,7 @@ 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.assertTrue; import com.android.tools.r8.R8TestCompileResult; import com.android.tools.r8.TestParameters; @@ -14,6 +15,7 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; import com.google.common.collect.ImmutableList; import java.nio.file.Path; @@ -27,7 +29,6 @@ @RunWith(Parameterized.class) public class Proto2BuilderShrinkingTest extends ProtoShrinkingTestBase { - private static final String LITE_BUILDER = "com.google.protobuf.GeneratedMessageLite$Builder"; private static final String METHOD_TO_INVOKE_ENUM = "com.google.protobuf.GeneratedMessageLite$MethodToInvoke"; @@ -46,12 +47,14 @@ ImmutableList.of("proto2.BuilderWithProtoBuilderSetterTestClass"), ImmutableList.of("proto2.BuilderWithProtoSetterTestClass"), ImmutableList.of("proto2.BuilderWithReusedSettersTestClass"), + ImmutableList.of("proto2.HasFlaggedOffExtensionBuilderTestClass"), ImmutableList.of( "proto2.BuilderWithOneofSetterTestClass", "proto2.BuilderWithPrimitiveSettersTestClass", "proto2.BuilderWithProtoBuilderSetterTestClass", "proto2.BuilderWithProtoSetterTestClass", - "proto2.BuilderWithReusedSettersTestClass")), + "proto2.BuilderWithReusedSettersTestClass", + "proto2.HasFlaggedOffExtensionBuilderTestClass")), getTestParameters().withAllRuntimesAndApiLevels().build()); } @@ -69,11 +72,22 @@ .addKeepRuleFiles(PROTOBUF_LITE_PROGUARD_RULES) .addOptionsModification( options -> { + assert !options.applyInliningToInlinee; options.applyInliningToInlinee = true; + + assert !options.enableFieldBitAccessAnalysis; options.enableFieldBitAccessAnalysis = true; + + assert !options.protoShrinking().enableGeneratedExtensionRegistryShrinking; options.protoShrinking().enableGeneratedExtensionRegistryShrinking = true; + + assert !options.protoShrinking().enableGeneratedMessageLiteShrinking; options.protoShrinking().enableGeneratedMessageLiteShrinking = true; + + assert !options.protoShrinking().enableGeneratedMessageLiteBuilderShrinking; options.protoShrinking().enableGeneratedMessageLiteBuilderShrinking = true; + + assert !options.enableStringSwitchConversion; options.enableStringSwitchConversion = true; }) .allowAccessModification() @@ -153,6 +167,8 @@ "0", "true", "qux"); + case "proto2.HasFlaggedOffExtensionBuilderTestClass": + return StringUtils.lines("4"); default: throw new Unreachable(); } @@ -164,14 +180,15 @@ } private void verifyBuildersAreAbsent(CodeInspector outputInspector) { - boolean primitivesBuilderShouldBeLive = - mains.contains("proto2.BuilderWithReusedSettersTestClass"); assertThat( - outputInspector.clazz(LITE_BUILDER), - primitivesBuilderShouldBeLive ? isPresent() : not(isPresent())); + outputInspector.clazz( + "com.android.tools.r8.proto2.Shrinking$HasFlaggedOffExtension$Builder"), + mains.equals(ImmutableList.of("proto2.HasFlaggedOffExtensionBuilderTestClass")) + ? isPresent() + : not(isPresent())); assertThat( outputInspector.clazz("com.android.tools.r8.proto2.TestProto$Primitives$Builder"), - primitivesBuilderShouldBeLive ? isPresent() : not(isPresent())); + not(isPresent())); assertThat( outputInspector.clazz("com.android.tools.r8.proto2.TestProto$OuterMessage$Builder"), not(isPresent())); @@ -185,13 +202,14 @@ for (String main : mains) { MethodSubject mainMethodSubject = outputInspector.clazz(main).mainMethod(); assertThat(mainMethodSubject, isPresent()); - // TODO(christofferqa): Enable assertion. - // assertTrue( - // mainMethodSubject - // .streamInstructions() - // .filter(InstructionSubject::isStaticGet) - // .map(instruction -> instruction.getField().type) - // .noneMatch(methodToInvokeType::equals)); + assertTrue( + main, + mainMethodSubject + .streamInstructions() + .filter(InstructionSubject::isStaticGet) + .map(instruction -> instruction.getField().type) + .noneMatch(methodToInvokeType::equals)); + assertTrue(mainMethodSubject.streamInstructions().noneMatch(InstructionSubject::isSwitch)); } } }
diff --git a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java index 111e8f1..4b0d1ed 100644 --- a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java +++ b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
@@ -8,7 +8,7 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; -import com.android.tools.r8.graph.AppInfo; +import com.android.tools.r8.graph.AppInfoWithClassHierarchy; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexApplication; import com.android.tools.r8.ir.analysis.type.TypeLatticeElement; @@ -61,7 +61,8 @@ InternalOptions options = new InternalOptions(); DexApplication application = buildApplication(builder, options); - AppView<?> appView = AppView.createForD8(new AppInfo(application), options); + AppView<AppInfoWithClassHierarchy> appView = + AppView.createForD8(new AppInfoWithClassHierarchy(application), options); // Return the processed method for inspection. MethodSubject methodSubject = getMethodSubject(application, signature); @@ -177,7 +178,8 @@ InternalOptions options = new InternalOptions(); DexApplication application = buildApplication(builder, options); - AppView<?> appView = AppView.createForD8(new AppInfo(application), options); + AppView<AppInfoWithClassHierarchy> appView = + AppView.createForD8(new AppInfoWithClassHierarchy(application), options); // Return the processed method for inspection. MethodSubject methodSubject = getMethodSubject(application, signature); @@ -301,7 +303,8 @@ InternalOptions options = new InternalOptions(); DexApplication application = buildApplication(builder, options); - AppView<?> appView = AppView.createForD8(new AppInfo(application), options); + AppView<AppInfoWithClassHierarchy> appView = + AppView.createForD8(new AppInfoWithClassHierarchy(application), options); // Return the processed method for inspection. MethodSubject methodSubject = getMethodSubject(application, signature); @@ -425,7 +428,8 @@ InternalOptions options = new InternalOptions(); DexApplication application = buildApplication(builder, options); - AppView<?> appView = AppView.createForD8(new AppInfo(application), options); + AppView<AppInfoWithClassHierarchy> appView = + AppView.createForD8(new AppInfoWithClassHierarchy(application), options); // Return the processed method for inspection. MethodSubject methodSubject = getMethodSubject(application, signature);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/EffectivelyFinalFieldCanonicalizationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/EffectivelyFinalFieldCanonicalizationTest.java new file mode 100644 index 0000000..4e73445 --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/EffectivelyFinalFieldCanonicalizationTest.java
@@ -0,0 +1,84 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.ir.optimize.canonicalization; + +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.NeverClassInline; +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.android.tools.r8.utils.codeinspector.InstructionSubject; +import com.android.tools.r8.utils.codeinspector.MethodSubject; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class EffectivelyFinalFieldCanonicalizationTest extends TestBase { + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withDexRuntimes().withAllApiLevels().build(); + } + + public EffectivelyFinalFieldCanonicalizationTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(EffectivelyFinalFieldCanonicalizationTest.class) + .addKeepMainRule(TestClass.class) + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect(this::inspect) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutputLines("[R8] Foo", "[R8] Bar"); + } + + private void inspect(CodeInspector inspector) { + ClassSubject testClassSubject = inspector.clazz(TestClass.class); + assertThat(testClassSubject, isPresent()); + + MethodSubject mainMethodSubject = testClassSubject.mainMethod(); + assertThat(mainMethodSubject, isPresent()); + assertEquals( + 1, mainMethodSubject.streamInstructions().filter(InstructionSubject::isStaticGet).count()); + } + + static class TestClass { + + public static void main(String[] args) { + Printer.INSTANCE.print("Foo"); + Printer.INSTANCE.print("Bar"); + } + } + + @NeverClassInline + static class Printer { + + static Printer INSTANCE = new Printer("[R8]"); + + private String tag; + + Printer(String tag) { + this.tag = tag; + } + + @NeverInline + void print(String message) { + System.out.println(tag + " " + message); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/ifs/SemiTrivialPhiBranchTest.java b/src/test/java/com/android/tools/r8/ir/optimize/ifs/SemiTrivialPhiBranchTest.java index 8b84dab..a8bccea 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/ifs/SemiTrivialPhiBranchTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/ifs/SemiTrivialPhiBranchTest.java
@@ -5,6 +5,7 @@ package com.android.tools.r8.ir.optimize.ifs; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import com.android.tools.r8.NeverInline; @@ -50,8 +51,7 @@ assertThat(testClassSubject, isPresent()); assertThat(testClassSubject.mainMethod(), isPresent()); assertThat(testClassSubject.uniqueMethodWithName("live"), isPresent()); - // TODO(christofferqa): Should not be present. - assertThat(testClassSubject.uniqueMethodWithName("dead"), isPresent()); + assertThat(testClassSubject.uniqueMethodWithName("dead"), not(isPresent())); } static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/AssumeInstanceFieldValueTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/AssumeInstanceFieldValueTest.java new file mode 100644 index 0000000..32d6962 --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/AssumeInstanceFieldValueTest.java
@@ -0,0 +1,112 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.ir.optimize.membervaluepropagation; + +import 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.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.android.tools.r8.utils.codeinspector.FieldSubject; +import com.android.tools.r8.utils.codeinspector.InstructionSubject; +import com.android.tools.r8.utils.codeinspector.MethodSubject; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class AssumeInstanceFieldValueTest extends TestBase { + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public AssumeInstanceFieldValueTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(AssumeInstanceFieldValueTest.class) + .addKeepMainRule(TestClass.class) + .addKeepRules( + "-assumevalues class " + Config.class.getTypeName() + " {", + " boolean alwaysTrue return true;", + "}", + "-assumenosideeffects class " + Config.class.getTypeName() + " {", + " boolean alwaysTrueNoSideEffects return true;", + "}") + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect(this::inspect) + .run(parameters.getRuntime(), TestClass.class) + // TODO(b/147835946): Should be "Hello world!". + .assertSuccessWithOutput(""); + } + + private void inspect(CodeInspector inspector) { + ClassSubject configClassSubject = inspector.clazz(Config.class); + assertThat(configClassSubject, isPresent()); + + FieldSubject alwaysTrueFieldSubject = configClassSubject.uniqueFieldWithName("alwaysTrue"); + assertThat(alwaysTrueFieldSubject, isPresent()); + + FieldSubject alwaysTrueNoSideEffectsFieldSubject = + configClassSubject.uniqueFieldWithName("alwaysTrueNoSideEffects"); + assertThat(alwaysTrueNoSideEffectsFieldSubject, isPresent()); + + ClassSubject testClassSubject = inspector.clazz(TestClass.class); + assertThat(testClassSubject, isPresent()); + + MethodSubject mainMethodSubject = testClassSubject.mainMethod(); + assertThat(mainMethodSubject, isPresent()); + assertEquals( + 1, + mainMethodSubject + .streamInstructions() + .filter(InstructionSubject::isInstanceGet) + .map(InstructionSubject::getField) + .filter(alwaysTrueFieldSubject.getField().field::equals) + .count()); + // TODO(b/147835946): Should be true. + assertFalse( + mainMethodSubject + .streamInstructions() + .filter(InstructionSubject::isInstanceGet) + .map(InstructionSubject::getField) + .noneMatch(alwaysTrueNoSideEffectsFieldSubject.getField().field::equals)); + } + + static class TestClass { + + public static void main(String[] args) { + if (new Config().alwaysTrue) { + System.out.print("Hello"); + } + Config nullableConfig = System.currentTimeMillis() >= 0 ? new Config() : null; + if (nullableConfig.alwaysTrue) { + System.out.print(" world"); + } + if (nullableConfig.alwaysTrueNoSideEffects) { + System.out.println("!"); + } + } + } + + static class Config { + + boolean alwaysTrue; + boolean alwaysTrueNoSideEffects; + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FinalFieldWithDefaultValueAssignmentPropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FinalFieldWithDefaultValueAssignmentPropagationTest.java new file mode 100644 index 0000000..0f714ef --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FinalFieldWithDefaultValueAssignmentPropagationTest.java
@@ -0,0 +1,97 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.ir.optimize.membervaluepropagation; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; +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.TestParametersCollection; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.android.tools.r8.utils.codeinspector.InstructionSubject; +import com.android.tools.r8.utils.codeinspector.MethodSubject; +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 FinalFieldWithDefaultValueAssignmentPropagationTest extends TestBase { + + private final TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public FinalFieldWithDefaultValueAssignmentPropagationTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(FinalFieldWithDefaultValueAssignmentPropagationTest.class) + .addKeepMainRule(TestClass.class) + .enableInliningAnnotations() + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect(this::inspect) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutputLines("false"); + } + + private void inspect(CodeInspector inspector) { + ClassSubject testClassSubject = inspector.clazz(TestClass.class); + assertThat(testClassSubject, isPresent()); + // TODO(b/147799637): Should be absent. + assertThat(testClassSubject.uniqueMethodWithName("dead"), isPresent()); + + ClassSubject configClassSubject = inspector.clazz(Config.class); + assertThat(configClassSubject, isPresent()); + + MethodSubject configConstructorSubject = configClassSubject.init(); + assertThat(configConstructorSubject, isPresent()); + // TODO(b/147799637): Should be true. + assertFalse( + configConstructorSubject.streamInstructions().noneMatch(InstructionSubject::isInstancePut)); + } + + static class TestClass { + + public static void main(String[] args) { + if (new Config().alwaysFalse) { + dead(); + } + } + + @NeverInline + static void dead() { + System.out.println("Dead!"); + } + } + + static class Config { + + final boolean alwaysFalse; + + Config() { + // An instruction that causes alwaysFalse to be read. + System.out.println(this); + // Since alwaysFalse is final we can remove the assignment, even if the receiver escapes. + alwaysFalse = false; + } + + @Override + public String toString() { + return Boolean.toString(alwaysFalse); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationTest.java index 5b4c540..a74ac5f 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationTest.java
@@ -42,12 +42,6 @@ testForR8(parameters.getBackend()) .addInnerClasses(InstanceFieldValuePropagationTest.class) .addKeepMainRule(TestClass.class) - .addOptionsModification( - options -> { - // TODO(b/125282093): Remove options modification once landed. - assert !options.enableValuePropagationForInstanceFields; - options.enableValuePropagationForInstanceFields = true; - }) .enableNeverClassInliningAnnotations() .enableInliningAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonFinalFieldWithDefaultValueAssignmentPropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonFinalFieldWithDefaultValueAssignmentPropagationTest.java new file mode 100644 index 0000000..2a37168 --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonFinalFieldWithDefaultValueAssignmentPropagationTest.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.ir.optimize.membervaluepropagation; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertFalse; + +import com.android.tools.r8.NeverClassInline; +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.android.tools.r8.utils.codeinspector.InstructionSubject; +import com.android.tools.r8.utils.codeinspector.MethodSubject; +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 NonFinalFieldWithDefaultValueAssignmentPropagationTest extends TestBase { + + private final TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public NonFinalFieldWithDefaultValueAssignmentPropagationTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(NonFinalFieldWithDefaultValueAssignmentPropagationTest.class) + .addKeepMainRule(TestClass.class) + .enableNeverClassInliningAnnotations() + .enableInliningAnnotations() + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect(this::inspect) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutputLines("Hello world!"); + } + + private void inspect(CodeInspector inspector) { + ClassSubject testClassSubject = inspector.clazz(TestClass.class); + assertThat(testClassSubject, isPresent()); + // TODO(b/147799637): Should be absent. + assertThat(testClassSubject.uniqueMethodWithName("dead"), isPresent()); + + ClassSubject configClassSubject = inspector.clazz(Config.class); + assertThat(configClassSubject, isPresent()); + + MethodSubject configConstructorSubject = configClassSubject.init(); + assertThat(configConstructorSubject, isPresent()); + // TODO(b/147799637): Should be true. + assertFalse( + configConstructorSubject.streamInstructions().noneMatch(InstructionSubject::isInstancePut)); + } + + static class TestClass { + + public static void main(String[] args) { + if (new Config().alwaysFalse) { + dead(); + } + } + + @NeverInline + static void dead() { + System.out.println("Dead!"); + } + } + + @NeverClassInline + static class Config { + + boolean alwaysFalse; + + Config() { + // An instruction that cannot read alwaysFalse, because the receiver has not escaped + // (except into Object.<init>()). + System.out.println("Hello world!"); + // Since the receiver has not escaped, we can remove the assignment. + alwaysFalse = false; + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonFinalFieldWithNonDefaultValueAssignmentPropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonFinalFieldWithNonDefaultValueAssignmentPropagationTest.java new file mode 100644 index 0000000..c69ddd6 --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonFinalFieldWithNonDefaultValueAssignmentPropagationTest.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.ir.optimize.membervaluepropagation; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertFalse; + +import com.android.tools.r8.NeverClassInline; +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.android.tools.r8.utils.codeinspector.InstructionSubject; +import com.android.tools.r8.utils.codeinspector.MethodSubject; +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 NonFinalFieldWithNonDefaultValueAssignmentPropagationTest extends TestBase { + + private final TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public NonFinalFieldWithNonDefaultValueAssignmentPropagationTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(NonFinalFieldWithNonDefaultValueAssignmentPropagationTest.class) + .addKeepMainRule(TestClass.class) + .enableNeverClassInliningAnnotations() + .enableInliningAnnotations() + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect(this::inspect) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutputLines("Hello world!"); + } + + private void inspect(CodeInspector inspector) { + ClassSubject testClassSubject = inspector.clazz(TestClass.class); + assertThat(testClassSubject, isPresent()); + // TODO(b/147799637): Should be absent. + assertThat(testClassSubject.uniqueMethodWithName("dead"), isPresent()); + + ClassSubject configClassSubject = inspector.clazz(Config.class); + assertThat(configClassSubject, isPresent()); + + MethodSubject configConstructorSubject = configClassSubject.init(); + assertThat(configConstructorSubject, isPresent()); + // TODO(b/147799637): Should be true. + assertFalse( + configConstructorSubject.streamInstructions().noneMatch(InstructionSubject::isInstancePut)); + } + + static class TestClass { + + public static void main(String[] args) { + if (!new Config().alwaysTrue) { + dead(); + } + } + + @NeverInline + static void dead() { + System.out.println("Dead!"); + } + } + + @NeverClassInline + static class Config { + + boolean alwaysTrue; + + Config() { + // An instruction that cannot read alwaysTrue, because the receiver has not escaped + // (except into Object.<init>()). + System.out.println("Hello world!"); + // Since the receiver has not escaped, we are guaranteed that all reads will evaluate to true. + alwaysTrue = true; + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java index d0a4064..5ab6053 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
@@ -170,6 +170,7 @@ "STATIC: SimpleWithThrowingGetter SimpleWithThrowingGetter.getInstance()", "STATIC: SimpleWithThrowingGetter SimpleWithThrowingGetter.getInstance()", "STATIC: String TrivialTestClass.next()", + "SimpleWithThrowingGetter SimpleWithThrowingGetter.INSTANCE", "VIRTUAL: String SimpleWithThrowingGetter.bar(String)", "VIRTUAL: String SimpleWithThrowingGetter.foo()"), references(clazz, "testSimpleWithThrowingGetter", "void"));
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizerAnalysisTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizerAnalysisTest.java index f186803..e40b4e0 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizerAnalysisTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizerAnalysisTest.java
@@ -225,6 +225,21 @@ } @Test + public void testConditionalPhiWithoutAppend() throws Exception { + buildAndCheckIR( + "conditionalPhiWithoutAppend", + checkOptimizerStates(appView, optimizer -> { + assertEquals(1, optimizer.analysis.builderStates.size()); + for (Value builder : optimizer.analysis.builderStates.keySet()) { + Map<Instruction, BuilderState> perBuilderState = + optimizer.analysis.builderStates.get(builder); + checkBuilderState(optimizer, perBuilderState, null, true); + } + assertEquals(0, optimizer.analysis.simplifiedBuilders.size()); + })); + } + + @Test public void testLoop() throws Exception { buildAndCheckIR( "loop",
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatenationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatenationTest.java index 1b16aa6..0efe652 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatenationTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatenationTest.java
@@ -51,6 +51,8 @@ "Hello,R8", // phiWithDifferentInits "Hello,R8", + // conditionalPhiWithoutAppend + "initial:suffix", // loop "na;na;na;na;na;na;na;na;Batman!", // loopWithBuilder @@ -59,7 +61,7 @@ @Parameterized.Parameters(name = "{0}") public static TestParametersCollection data() { - return getTestParameters().withAllRuntimes().build(); + return getTestParameters().withAllRuntimes().withAllApiLevels().build(); } private final TestParameters parameters; @@ -152,6 +154,10 @@ assertThat(method, isPresent()); assertEquals(3, countConstString(method)); + method = mainClass.uniqueMethodWithName("conditionalPhiWithoutAppend"); + assertThat(method, isPresent()); + assertEquals(3, countConstString(method)); + method = mainClass.uniqueMethodWithName("loop"); assertThat(method, isPresent()); assertEquals(3, countConstString(method)); @@ -173,7 +179,7 @@ testForD8() .debug() .addProgramClasses(MAIN) - .setMinApi(parameters.getRuntime()) + .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN) .assertSuccessWithOutput(JAVA_OUTPUT); test(result, false, false); @@ -182,7 +188,7 @@ testForD8() .release() .addProgramClasses(MAIN) - .setMinApi(parameters.getRuntime()) + .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN) .assertSuccessWithOutput(JAVA_OUTPUT); test(result, false, true); @@ -198,7 +204,7 @@ .enableInliningAnnotations() .addKeepMainRule(MAIN) .noMinification() - .setMinApi(parameters.getRuntime()) + .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN) .assertSuccessWithOutput(JAVA_OUTPUT); test(result, true, true);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatenationTestClass.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatenationTestClass.java index 980148b..535fee9 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatenationTestClass.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatenationTestClass.java
@@ -138,6 +138,16 @@ } @NeverInline + public static void conditionalPhiWithoutAppend() { + StringBuilder b = new StringBuilder("initial"); + String suffix = "suffix"; + if (!suffix.isEmpty()) { + b.append(":").append(suffix); + } + System.out.println(b.toString()); + } + + @NeverInline public static void loop() { String r = ""; for (int i = 0; i < 8; i++) { @@ -169,6 +179,7 @@ simplePhi(); phiAtInit(); phiWithDifferentInits(); + conditionalPhiWithoutAppend(); loop(); loopWithBuilder(); }
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java index 555b94c..a449c5d 100644 --- a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java +++ b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
@@ -44,7 +44,7 @@ } @Override - public void replaceCurrentInstruction(Instruction newInstruction) { + public void replaceCurrentInstruction(Instruction newInstruction, Set<Value> affectedValues) { throw new Unimplemented(); }
diff --git a/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java b/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java index c55b3a5..b4dc614 100644 --- a/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java +++ b/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java
@@ -293,7 +293,8 @@ " invokespecial SubClass/<init>()V", " invokespecial SubClass/aMethod()V", " return"); - ensureIAE(builder); + ensureExceptionOrCompilerError(builder, IllegalAccessError.class, + compiler -> compiler.equals(CompilerUnderTest.R8)); } @Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInClasspathTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInClasspathTypeTest.java index ab702b1..e785965 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInClasspathTypeTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInClasspathTypeTest.java
@@ -4,7 +4,7 @@ package com.android.tools.r8.kotlin.metadata; import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; -import static com.android.tools.r8.utils.codeinspector.Matchers.isExtension; +import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed; import static org.hamcrest.CoreMatchers.not; @@ -16,7 +16,6 @@ import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.shaking.ProguardKeepAttributes; -import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.KmClassSubject; import com.android.tools.r8.utils.codeinspector.KmFunctionSubject; @@ -76,7 +75,6 @@ // to be called with Kotlin syntax from other kotlin code. .addKeepRules("-keep class **.ImplKt { <methods>; }") .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) - .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting) .compile(); String pkg = getClass().getPackage().getName(); final String implClassName = pkg + ".classpath_lib_ext.Impl"; @@ -142,7 +140,6 @@ // to be called with Kotlin syntax from other kotlin code. .addKeepRules("-keep class **.ImplKt { <methods>; }") .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) - .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting) .compile(); String pkg = getClass().getPackage().getName(); final String implClassName = pkg + ".classpath_lib_ext.Impl"; @@ -160,7 +157,7 @@ assertThat(kmPackage, isPresent()); KmFunctionSubject kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("fooExt"); - assertThat(kmFunction, isExtension()); + assertThat(kmFunction, isExtensionFunction()); ClassSubject extra = inspector.clazz(extraClassName); assertThat(extra, isPresent());
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionFunctionTest.java index 8e1e6b7..0f124e4 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionFunctionTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionFunctionTest.java
@@ -4,7 +4,7 @@ package com.android.tools.r8.kotlin.metadata; import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; -import static com.android.tools.r8.utils.codeinspector.Matchers.isExtension; +import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed; import static org.hamcrest.CoreMatchers.not; @@ -16,7 +16,6 @@ import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.shaking.ProguardKeepAttributes; -import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.KmClassSubject; import com.android.tools.r8.utils.codeinspector.KmFunctionSubject; @@ -69,7 +68,6 @@ // to be called with Kotlin syntax from other kotlin code. .addKeepRules("-keep class **.BKt { <methods>; }") .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) - .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting) .compile(); String pkg = getClass().getPackage().getName(); final String superClassName = pkg + ".extension_function_lib.Super"; @@ -96,7 +94,7 @@ assertThat(kmPackage, isPresent()); KmFunctionSubject kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("extension"); - assertThat(kmFunction, isExtension()); + assertThat(kmFunction, isExtensionFunction()); }); Path libJar = compileResult.writeToZip(); @@ -130,7 +128,6 @@ // to be called with Kotlin syntax from other kotlin code. .addKeepRules("-keep class **.BKt { <methods>; }") .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) - .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting) .compile(); String pkg = getClass().getPackage().getName(); final String superClassName = pkg + ".extension_function_lib.Super"; @@ -161,7 +158,7 @@ assertThat(kmPackage, isPresent()); KmFunctionSubject kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("extension"); - assertThat(kmFunction, isExtension()); + assertThat(kmFunction, isExtensionFunction()); }); Path libJar = compileResult.writeToZip();
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionPropertyTest.java index 0b327c9..7ffa9ac 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionPropertyTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionPropertyTest.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.kotlin.metadata; import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionProperty; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed; import static org.hamcrest.CoreMatchers.containsString; @@ -17,10 +18,10 @@ import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.ToolHelper.ProcessResult; import com.android.tools.r8.shaking.ProguardKeepAttributes; -import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.KmClassSubject; import com.android.tools.r8.utils.codeinspector.KmPackageSubject; +import com.android.tools.r8.utils.codeinspector.KmPropertySubject; import java.nio.file.Path; import java.util.Collection; import java.util.List; @@ -69,7 +70,6 @@ // to be called with Kotlin syntax from other kotlin code. .addKeepRules("-keep class **.BKt { <methods>; }") .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) - .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting) .compile(); String pkg = getClass().getPackage().getName(); final String superClassName = pkg + ".extension_property_lib.Super"; @@ -95,7 +95,8 @@ KmPackageSubject kmPackage = bKt.getKmPackage(); assertThat(kmPackage, isPresent()); - // TODO(b/70169921): test property details. + KmPropertySubject kmProperty = kmPackage.kmPropertyExtensionWithUniqueName("asI"); + assertThat(kmProperty, isExtensionProperty()); }); Path libJar = compileResult.writeToZip(); @@ -126,7 +127,6 @@ // to be called with Kotlin syntax from other kotlin code. .addKeepRules("-keep class **.BKt { <methods>; }") .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) - .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting) .compile(); String pkg = getClass().getPackage().getName(); final String superClassName = pkg + ".extension_property_lib.Super"; @@ -156,7 +156,8 @@ KmPackageSubject kmPackage = bKt.getKmPackage(); assertThat(kmPackage, isPresent()); - // TODO(b/70169921): test property details. + KmPropertySubject kmProperty = kmPackage.kmPropertyExtensionWithUniqueName("asI"); + assertThat(kmProperty, isExtensionProperty()); }); Path libJar = compileResult.writeToZip();
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInFunctionTest.java index c7e5db4..c0a1092 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInFunctionTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInFunctionTest.java
@@ -4,7 +4,7 @@ package com.android.tools.r8.kotlin.metadata; import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; -import static com.android.tools.r8.utils.codeinspector.Matchers.isExtension; +import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed; import static org.hamcrest.CoreMatchers.not; @@ -16,7 +16,6 @@ import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.shaking.ProguardKeepAttributes; -import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.KmClassSubject; import com.android.tools.r8.utils.codeinspector.KmFunctionSubject; @@ -68,7 +67,6 @@ // Keep the BKt method, which will be called from other kotlin code. .addKeepRules("-keep class **.BKt { <methods>; }") .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) - .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting) .compile(); String pkg = getClass().getPackage().getName(); final String superClassName = pkg + ".function_lib.Super"; @@ -96,7 +94,7 @@ KmFunctionSubject kmFunction = kmPackage.kmFunctionWithUniqueName("fun"); assertThat(kmFunction, isPresent()); - assertThat(kmFunction, not(isExtension())); + assertThat(kmFunction, not(isExtensionFunction())); }); Path libJar = compileResult.writeToZip(); @@ -129,7 +127,6 @@ // Keep the BKt method, which will be called from other kotlin code. .addKeepRules("-keep class **.BKt { <methods>; }") .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) - .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting) .compile(); String pkg = getClass().getPackage().getName(); final String superClassName = pkg + ".function_lib.Super"; @@ -160,7 +157,7 @@ KmFunctionSubject kmFunction = kmPackage.kmFunctionWithUniqueName("fun"); assertThat(kmFunction, isPresent()); - assertThat(kmFunction, not(isExtension())); + assertThat(kmFunction, not(isExtensionFunction())); }); Path libJar = compileResult.writeToZip();
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInLibraryTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInLibraryTypeTest.java index 603dd6e..8917ebe 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInLibraryTypeTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInLibraryTypeTest.java
@@ -15,7 +15,6 @@ import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.shaking.ProguardKeepAttributes; -import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.KmPackageSubject; import java.nio.file.Path; @@ -82,7 +81,6 @@ // Keep the main entry. .addKeepMainRule(main) .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) - .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting) // -dontoptimize so that basic code structure is kept. .noOptimization() .compile();
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInMultifileClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInMultifileClassTest.java index 80a2641..327878a 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInMultifileClassTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInMultifileClassTest.java
@@ -16,7 +16,6 @@ import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.ToolHelper.ProcessResult; import com.android.tools.r8.shaking.ProguardKeepAttributes; -import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.codeinspector.AnnotationSubject; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; @@ -66,7 +65,6 @@ .addKeepRules("-keep class **.UtilKt") .addKeepRules("-keepclassmembers class * { ** comma*Join*(...); }") .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) - .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting) .compile(); String pkg = getClass().getPackage().getName(); final String utilClassName = pkg + ".multifileclass_lib.UtilKt"; @@ -125,7 +123,6 @@ .addKeepRules( "-keepclassmembers,allowobfuscation class * { ** joinOf*(...); }") .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) - .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting) .compile(); String pkg = getClass().getPackage().getName(); final String utilClassName = pkg + ".multifileclass_lib.UtilKt";
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParameterTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParameterTypeTest.java index 61ede88..d75ec09 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParameterTypeTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParameterTypeTest.java
@@ -15,7 +15,6 @@ import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.shaking.ProguardKeepAttributes; -import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.KmClassSubject; import java.nio.file.Path; @@ -64,7 +63,6 @@ // Keep Itf, but allow minification. .addKeepRules("-keep,allowobfuscation class **.Itf") .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) - .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting) .compile(); String pkg = getClass().getPackage().getName(); final String itfClassName = pkg + ".parametertype_lib.Itf";
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTest.java index c671ea5..d0708f8 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTest.java
@@ -4,19 +4,22 @@ package com.android.tools.r8.kotlin.metadata; import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionProperty; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import com.android.tools.r8.R8TestCompileResult; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.shaking.ProguardKeepAttributes; -import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.KmClassSubject; +import com.android.tools.r8.utils.codeinspector.KmPropertySubject; import java.nio.file.Path; import java.util.Collection; import org.junit.BeforeClass; @@ -61,7 +64,6 @@ .addKeepRules("-keep class **.Person { <init>(...); }") .addKeepRules("-keepclassmembers class **.Person { *** get*(); }") .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) - .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting) .compile(); String pkg = getClass().getPackage().getName(); final String personClassName = pkg + ".fragile_property_lib.Person"; @@ -69,10 +71,35 @@ ClassSubject person = inspector.clazz(personClassName); assertThat(person, isPresent()); assertThat(person, not(isRenamed())); + // API entry is kept, hence the presence of Metadata. KmClassSubject kmClass = person.getKmClass(); assertThat(kmClass, isPresent()); - // TODO(b/70169921): test property details. + + KmPropertySubject name = kmClass.kmPropertyWithUniqueName("name"); + assertThat(name, isPresent()); + assertThat(name, not(isExtensionProperty())); + assertNotNull(name.fieldSignature()); + assertNotNull(name.getterSignature()); + // TODO(b/70169921): Can remove setter. + assertNotNull(name.setterSignature()); + + KmPropertySubject familyName = kmClass.kmPropertyWithUniqueName("familyName"); + assertThat(familyName, isPresent()); + assertThat(familyName, not(isExtensionProperty())); + // No backing field for property `familyName` + assertNull(familyName.fieldSignature()); + assertNotNull(familyName.getterSignature()); + // No setter for property `familyName` + assertNull(familyName.setterSignature()); + + KmPropertySubject age = kmClass.kmPropertyWithUniqueName("age"); + assertThat(age, isPresent()); + assertThat(age, not(isExtensionProperty())); + assertNotNull(age.fieldSignature()); + assertNotNull(age.getterSignature()); + // TODO(b/70169921): Can remove setter. + assertNotNull(name.setterSignature()); }); Path libJar = compileResult.writeToZip(); @@ -105,7 +132,6 @@ // Keep LibKt extension methods .addKeepRules("-keep class **.LibKt { <methods>; }") .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) - .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting) .compile(); String pkg = getClass().getPackage().getName(); final String personClassName = pkg + ".fragile_property_lib.Person"; @@ -116,7 +142,31 @@ // API entry is kept, hence the presence of Metadata. KmClassSubject kmClass = person.getKmClass(); assertThat(kmClass, isPresent()); - // TODO(b/70169921): test property details. + + KmPropertySubject name = kmClass.kmPropertyWithUniqueName("name"); + assertThat(name, isPresent()); + assertThat(name, not(isExtensionProperty())); + assertNotNull(name.fieldSignature()); + // TODO(b/70169921): Either remove getter or rewrite renamed signature. + assertNotNull(name.getterSignature()); + assertNotNull(name.setterSignature()); + + KmPropertySubject familyName = kmClass.kmPropertyWithUniqueName("familyName"); + assertThat(familyName, isPresent()); + assertThat(familyName, not(isExtensionProperty())); + // No backing field for property `familyName` + assertNull(familyName.fieldSignature()); + assertNotNull(familyName.getterSignature()); + // No setter for property `familyName` + assertNull(familyName.setterSignature()); + + KmPropertySubject age = kmClass.kmPropertyWithUniqueName("age"); + assertThat(age, isPresent()); + assertThat(age, not(isExtensionProperty())); + assertNotNull(age.fieldSignature()); + // TODO(b/70169921): Either remove getter or rewrite renamed signature. + assertNotNull(age.getterSignature()); + assertNotNull(name.setterSignature()); }); Path libJar = compileResult.writeToZip();
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTypeTest.java index 696cd03..60a9732 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTypeTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTypeTest.java
@@ -17,7 +17,6 @@ import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.ToolHelper.ProcessResult; import com.android.tools.r8.shaking.ProguardKeepAttributes; -import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.KmClassSubject; import java.nio.file.Path; @@ -64,7 +63,6 @@ // Keep non-private members of Impl .addKeepRules("-keep public class **.Impl { !private *; }") .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) - .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting) .compile(); String pkg = getClass().getPackage().getName(); final String itfClassName = pkg + ".propertytype_lib.Itf";
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturnTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturnTypeTest.java index 25ee2f8..4f24442 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturnTypeTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturnTypeTest.java
@@ -15,7 +15,6 @@ import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.shaking.ProguardKeepAttributes; -import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.KmClassSubject; import java.nio.file.Path; @@ -63,7 +62,6 @@ // Keep Itf, but allow minification. .addKeepRules("-keep,allowobfuscation class **.Itf") .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) - .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting) .compile(); String pkg = getClass().getPackage().getName(); final String itfClassName = pkg + ".returntype_lib.Itf";
diff --git a/src/test/java/com/android/tools/r8/naming/GenericSignatureParserTest.java b/src/test/java/com/android/tools/r8/naming/GenericSignatureParserTest.java index b264ef5..4489b75 100644 --- a/src/test/java/com/android/tools/r8/naming/GenericSignatureParserTest.java +++ b/src/test/java/com/android/tools/r8/naming/GenericSignatureParserTest.java
@@ -22,6 +22,7 @@ import org.junit.Test; public class GenericSignatureParserTest extends TestBase { + private static class ReGenerateGenericSignatureRewriter implements GenericSignatureAction<String> { @@ -42,7 +43,7 @@ } @Override - public String parsedTypeName(String name) { + public String parsedTypeName(String name, ParserPosition parserPosition) { renamedSignature.append(name); return name; } @@ -389,7 +390,7 @@ } @Override - public String parsedTypeName(String name) { + public String parsedTypeName(String name, ParserPosition parserPosition) { throw exceptionSupplier.get(); } }
diff --git a/src/test/java/com/android/tools/r8/naming/signature/SignatureOfMergedClassesTest.java b/src/test/java/com/android/tools/r8/naming/signature/SignatureOfMergedClassesTest.java new file mode 100644 index 0000000..01a95ba --- /dev/null +++ b/src/test/java/com/android/tools/r8/naming/signature/SignatureOfMergedClassesTest.java
@@ -0,0 +1,138 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.naming.signature; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; + +import com.android.tools.r8.CompilationFailedException; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestDiagnosticMessages; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.naming.signature.merging.I; +import com.android.tools.r8.naming.signature.merging.ImplI; +import com.android.tools.r8.naming.signature.merging.ImplK; +import com.android.tools.r8.naming.signature.merging.ImplL; +import com.android.tools.r8.naming.signature.merging.InterfaceToKeep; +import com.android.tools.r8.naming.signature.merging.J; +import com.android.tools.r8.naming.signature.merging.K; +import com.android.tools.r8.naming.signature.merging.L; +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.concurrent.ExecutionException; +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 SignatureOfMergedClassesTest extends TestBase { + + private final TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public SignatureOfMergedClassesTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testRemovalOfMergedInterfaceOnSameClass() + throws IOException, CompilationFailedException, ExecutionException { + testForR8(parameters.getBackend()) + .addProgramClasses( + ImplI.class, + ImplK.class, + I.class, + J.class, + InterfaceToKeep.class, + K.class, + Main.class, + L.class, + ImplL.class) + .addKeepMainRule(Main.class) + .addKeepClassRules(InterfaceToKeep.class) + .addKeepAttributes("Signature, InnerClasses, EnclosingMethod, *Annotation*") + .setMinApi(parameters.getApiLevel()) + .noMinification() + .addOptionsModification( + internalOptions -> { + internalOptions.enableUnusedInterfaceRemoval = false; + }) + .compileWithExpectedDiagnostics(TestDiagnosticMessages::assertNoMessages) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines( + "ImplI.foo", + "ImplI: com.android.tools.r8.naming.signature.merging.InterfaceToKeep<java.lang.Void>", + "K: com.android.tools.r8.naming.signature.merging.InterfaceToKeep<java.lang.Void>", + "ImplK.foo", + "ImplK.bar", + "ImplK: interface com.android.tools.r8.naming.signature.merging.K", + "ImplL.print") + .inspect( + codeInspector -> { + assertThat(codeInspector.clazz(I.class), not(isPresent())); + assertThat(codeInspector.clazz(J.class), not(isPresent())); + }); + } + + @Test + public void testKeepingOneSelfOnInterface() + throws ExecutionException, CompilationFailedException, IOException { + testForR8(parameters.getBackend()) + .addProgramClasses(Foo.class, InterfaceToKeep.class) + .addKeepMainRule(Foo.class) + .addKeepClassRules(InterfaceToKeep.class) + .addKeepAttributes("Signature, InnerClasses, EnclosingMethod, *Annotation*") + .setMinApi(parameters.getApiLevel()) + .noMinification() + .addOptionsModification( + internalOptions -> { + internalOptions.enableUnusedInterfaceRemoval = false; + }) + .run(parameters.getRuntime(), Foo.class) + .assertSuccessWithOutputLines( + "com.android.tools.r8.naming.signature.merging.InterfaceToKeep" + + "<com.android.tools.r8.naming.signature.SignatureOfMergedClassesTest$Foo>"); + } + + public static class Foo implements InterfaceToKeep<Foo> { + + public static void main(String[] args) { + for (Type genericInterface : Foo.class.getGenericInterfaces()) { + System.out.println(genericInterface); + } + } + } + + public static class Main { + + public static void main(String[] args) { + new ImplI().foo(); + for (Type genericInterface : ImplI.class.getGenericInterfaces()) { + System.out.println("ImplI: " + genericInterface); + } + for (Type genericInterface : K.class.getGenericInterfaces()) { + System.out.println("K: " + genericInterface); + } + K k = new ImplK(); + k.foo(); + k.bar(); + for (Type genericInterface : ImplK.class.getGenericInterfaces()) { + System.out.println("ImplK: " + genericInterface); + } + L<ImplL> l = new ImplL(); + l.print((ImplL) l); + for (Type genericInterface : ImplL.class.getGenericInterfaces()) { + System.out.println("ImplL: " + genericInterface); + } + } + } +}
diff --git a/src/test/java/com/android/tools/r8/naming/signature/merging/I.java b/src/test/java/com/android/tools/r8/naming/signature/merging/I.java new file mode 100644 index 0000000..237786c --- /dev/null +++ b/src/test/java/com/android/tools/r8/naming/signature/merging/I.java
@@ -0,0 +1,9 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.naming.signature.merging; + +public interface I { + void foo(); +}
diff --git a/src/test/java/com/android/tools/r8/naming/signature/merging/ImplI.java b/src/test/java/com/android/tools/r8/naming/signature/merging/ImplI.java new file mode 100644 index 0000000..b842386 --- /dev/null +++ b/src/test/java/com/android/tools/r8/naming/signature/merging/ImplI.java
@@ -0,0 +1,13 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.naming.signature.merging; + +public class ImplI implements InterfaceToKeep<Void>, I { + + @Override + public void foo() { + System.out.println("ImplI.foo"); + } +}
diff --git a/src/test/java/com/android/tools/r8/naming/signature/merging/ImplK.java b/src/test/java/com/android/tools/r8/naming/signature/merging/ImplK.java new file mode 100644 index 0000000..3c23d56 --- /dev/null +++ b/src/test/java/com/android/tools/r8/naming/signature/merging/ImplK.java
@@ -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.naming.signature.merging; + +public class ImplK implements K { + + @Override + public void foo() { + System.out.println("ImplK.foo"); + } + + @Override + public void bar() { + System.out.println("ImplK.bar"); + } +}
diff --git a/src/test/java/com/android/tools/r8/naming/signature/merging/ImplL.java b/src/test/java/com/android/tools/r8/naming/signature/merging/ImplL.java new file mode 100644 index 0000000..e50bdb3 --- /dev/null +++ b/src/test/java/com/android/tools/r8/naming/signature/merging/ImplL.java
@@ -0,0 +1,13 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.naming.signature.merging; + +public class ImplL implements L<ImplL> { + + @Override + public void print(ImplL implL) { + System.out.println("ImplL.print"); + } +}
diff --git a/src/test/java/com/android/tools/r8/naming/signature/merging/InterfaceToKeep.java b/src/test/java/com/android/tools/r8/naming/signature/merging/InterfaceToKeep.java new file mode 100644 index 0000000..6bebcd0 --- /dev/null +++ b/src/test/java/com/android/tools/r8/naming/signature/merging/InterfaceToKeep.java
@@ -0,0 +1,7 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.naming.signature.merging; + +public interface InterfaceToKeep<T> {}
diff --git a/src/test/java/com/android/tools/r8/naming/signature/merging/J.java b/src/test/java/com/android/tools/r8/naming/signature/merging/J.java new file mode 100644 index 0000000..444a5e8 --- /dev/null +++ b/src/test/java/com/android/tools/r8/naming/signature/merging/J.java
@@ -0,0 +1,9 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.naming.signature.merging; + +public interface J { + void foo(); +}
diff --git a/src/test/java/com/android/tools/r8/naming/signature/merging/K.java b/src/test/java/com/android/tools/r8/naming/signature/merging/K.java new file mode 100644 index 0000000..4b8ed50 --- /dev/null +++ b/src/test/java/com/android/tools/r8/naming/signature/merging/K.java
@@ -0,0 +1,9 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.naming.signature.merging; + +public interface K extends InterfaceToKeep<Void>, J { + void bar(); +}
diff --git a/src/test/java/com/android/tools/r8/naming/signature/merging/L.java b/src/test/java/com/android/tools/r8/naming/signature/merging/L.java new file mode 100644 index 0000000..65656dd --- /dev/null +++ b/src/test/java/com/android/tools/r8/naming/signature/merging/L.java
@@ -0,0 +1,10 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.naming.signature.merging; + +public interface L<T> { + + void print(T t); +}
diff --git a/src/test/java/com/android/tools/r8/resolution/ArrayTargetLookupTest.java b/src/test/java/com/android/tools/r8/resolution/ArrayTargetLookupTest.java index d8944f1..6162b15 100644 --- a/src/test/java/com/android/tools/r8/resolution/ArrayTargetLookupTest.java +++ b/src/test/java/com/android/tools/r8/resolution/ArrayTargetLookupTest.java
@@ -49,17 +49,23 @@ factory.createArrayType(2, fooType) }; DexEncodedMethod langObjectNotifyMethod = - appInfo.lookupVirtualTarget( - fooType, - factory.createMethod(fooType, factory.createProto(factory.voidType), "notify")); + appInfo + .resolveMethod( + fooType, + factory.createMethod(fooType, factory.createProto(factory.voidType), "notify")) + .getSingleTarget(); for (DexType arrType : arrayTypes) { assertNull( - appInfo.lookupVirtualTarget( - arrType, factory.createMethod(arrType, factory.createProto(arrType), "clone"))); + appInfo + .resolveMethod( + arrType, factory.createMethod(arrType, factory.createProto(arrType), "clone")) + .getSingleTarget()); DexEncodedMethod target = - appInfo.lookupVirtualTarget( - arrType, - factory.createMethod(arrType, factory.createProto(factory.voidType), "notify")); + appInfo + .resolveMethod( + arrType, + factory.createMethod(arrType, factory.createProto(factory.voidType), "notify")) + .getSingleTarget(); assertEquals(langObjectNotifyMethod, target); } }
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java b/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java index 8443f06..2f9a1ec 100644 --- a/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java +++ b/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
@@ -48,10 +48,6 @@ private void configure(InternalOptions options) { options.enableEnumValueOptimization = enableOptimization; - - // TODO(b/125282093): Remove options modification once landed. - assert !options.enableValuePropagationForInstanceFields; - options.enableValuePropagationForInstanceFields = true; } @Test
diff --git a/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalInstanceFieldsTest.java b/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalInstanceFieldsTest.java index 8ae759f..66c8152 100644 --- a/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalInstanceFieldsTest.java +++ b/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalInstanceFieldsTest.java
@@ -51,12 +51,6 @@ testForR8(parameters.getBackend()) .addInnerClasses(EffectivelyFinalInstanceFieldsTest.class) .addKeepMainRule(MAIN) - .addOptionsModification( - options -> { - // TODO(b/125282093): Remove options modification once landed. - assert !options.enableValuePropagationForInstanceFields; - options.enableValuePropagationForInstanceFields = true; - }) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableMergeAnnotations()
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java index 564902a..f28f242 100644 --- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java +++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java
@@ -40,12 +40,7 @@ TreeShaking18Test::unusedRemoved, null, null, - ImmutableList.of("src/test/examples/shaking18/keep-rules.txt"), - options -> { - // TODO(b/125282093): Remove options modification once landed. - assert !options.enableValuePropagationForInstanceFields; - options.enableValuePropagationForInstanceFields = true; - }); + ImmutableList.of("src/test/examples/shaking18/keep-rules.txt")); } private static void unusedRemoved(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptSingletonIsNotCyclicTest.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptSingletonIsNotCyclicTest.java index ba1c576..1a61008 100644 --- a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptSingletonIsNotCyclicTest.java +++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptSingletonIsNotCyclicTest.java
@@ -12,12 +12,10 @@ import com.android.tools.r8.NeverClassInline; import com.android.tools.r8.NeverInline; import com.android.tools.r8.NeverPropagateValue; -import com.android.tools.r8.R8FullTestBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersBuilder; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.ThrowableConsumer; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.references.ClassReference; import com.android.tools.r8.references.MethodReference; @@ -51,28 +49,24 @@ @Test public void testStaticMethod() throws Exception { - test(FooStaticMethod.class, TestStaticMethod.class, null); + test(FooStaticMethod.class, TestStaticMethod.class); } @Test public void testStaticField() throws Exception { - test( - FooStaticField.class, - TestStaticField.class, - builder -> builder.enableInliningAnnotations().enableMemberValuePropagationAnnotations()); + test(FooStaticField.class, TestStaticField.class); } - private void test( - Class<?> fooClass, Class<?> testClass, ThrowableConsumer<R8FullTestBuilder> configuration) - throws Exception { + private void test(Class<?> fooClass, Class<?> testClass) throws Exception { WhyAreYouKeepingConsumer whyAreYouKeepingConsumer = new WhyAreYouKeepingConsumer(null); GraphInspector inspector = testForR8(parameters.getBackend()) + .enableInliningAnnotations() + .enableMemberValuePropagationAnnotations() .enableNeverClassInliningAnnotations() .enableGraphInspector(whyAreYouKeepingConsumer) .addProgramClasses(testClass, fooClass) .addKeepMainRule(testClass) - .apply(configuration) .run(parameters.getRuntime(), testClass) .assertSuccessWithOutput(EXPECTED) .graphInspector(); @@ -122,6 +116,8 @@ private FooStaticMethod() {} + @NeverInline + @NeverPropagateValue @Override public String toString() { return "Foo!"; @@ -130,6 +126,8 @@ @NeverClassInline public static class TestStaticMethod { + + @NeverPropagateValue public FooStaticMethod foo; public TestStaticMethod() { @@ -159,6 +157,8 @@ @NeverClassInline public static class TestStaticField { + + @NeverPropagateValue public FooStaticField foo; public TestStaticField() {
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptSubclassKeepsSuperTest.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptSubclassKeepsSuperTest.java index 72bd54e..5e7786f 100644 --- a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptSubclassKeepsSuperTest.java +++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptSubclassKeepsSuperTest.java
@@ -5,7 +5,10 @@ import static org.junit.Assert.assertEquals; +import com.android.tools.r8.NeverClassInline; +import com.android.tools.r8.NeverInline; import com.android.tools.r8.NeverMerge; +import com.android.tools.r8.NeverPropagateValue; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersBuilder; @@ -42,7 +45,10 @@ GraphInspector inspector = testForR8(parameters.getBackend()) .enableGraphInspector() + .enableInliningAnnotations() + .enableMemberValuePropagationAnnotations() .enableMergeAnnotations() + .enableNeverClassInliningAnnotations() .addProgramClasses(CLASS, Foo.class, Bar.class) .addKeepMainRule(CLASS) .run(parameters.getRuntime(), CLASS) @@ -63,6 +69,7 @@ @NeverMerge public abstract static class Bar {} + @NeverClassInline public static final class Foo extends Bar { static final Foo INSTANCE = new Foo(); @@ -73,6 +80,8 @@ private Foo() {} + @NeverInline + @NeverPropagateValue @Override public String toString() { return "Foo!";
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmClassSubject.java index 212dd9f..6a2f504 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmClassSubject.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmClassSubject.java
@@ -69,6 +69,21 @@ } @Override + public KmPropertySubject kmPropertyWithUniqueName(String name) { + return null; + } + + @Override + public KmPropertySubject kmPropertyExtensionWithUniqueName(String name) { + return null; + } + + @Override + public List<KmPropertySubject> getProperties() { + return null; + } + + @Override public List<ClassSubject> getReturnTypesInProperties() { return null; }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmFunctionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmFunctionSubject.java index fb7466b..e869207 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmFunctionSubject.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmFunctionSubject.java
@@ -3,6 +3,8 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.utils.codeinspector; +import kotlinx.metadata.jvm.JvmMethodSignature; + public class AbsentKmFunctionSubject extends KmFunctionSubject { @Override @@ -24,4 +26,9 @@ public boolean isExtension() { return false; } + + @Override + public JvmMethodSignature signature() { + return null; + } }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmPackageSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmPackageSubject.java index 1fd6506..aef78fc 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmPackageSubject.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmPackageSubject.java
@@ -69,6 +69,21 @@ } @Override + public KmPropertySubject kmPropertyWithUniqueName(String name) { + return null; + } + + @Override + public KmPropertySubject kmPropertyExtensionWithUniqueName(String name) { + return null; + } + + @Override + public List<KmPropertySubject> getProperties() { + return null; + } + + @Override public List<ClassSubject> getReturnTypesInProperties() { return null; }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmPropertySubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmPropertySubject.java new file mode 100644 index 0000000..70f6e52 --- /dev/null +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmPropertySubject.java
@@ -0,0 +1,45 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.utils.codeinspector; + +import kotlinx.metadata.jvm.JvmFieldSignature; +import kotlinx.metadata.jvm.JvmMethodSignature; + +public class AbsentKmPropertySubject extends KmPropertySubject { + + @Override + public boolean isPresent() { + return false; + } + + @Override + public boolean isRenamed() { + return false; + } + + @Override + public boolean isSynthetic() { + return false; + } + + @Override + public boolean isExtension() { + return false; + } + + @Override + public JvmFieldSignature fieldSignature() { + return null; + } + + @Override + public JvmMethodSignature getterSignature() { + return null; + } + + @Override + public JvmMethodSignature setterSignature() { + return null; + } +}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java index 4aec544..1a4dcc16 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -423,7 +423,7 @@ } @Override - public String parsedTypeName(String name) { + public String parsedTypeName(String name, ParserPosition parserPosition) { String type = name; if (obfuscatedToOriginalMapping != null) { String original = mapType(obfuscatedToOriginalMapping, name);
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java index af63ce8..e487726 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
@@ -46,6 +46,8 @@ import com.android.tools.r8.code.IgetWide; import com.android.tools.r8.code.InstanceOf; import com.android.tools.r8.code.Instruction; +import com.android.tools.r8.code.InvokeCustom; +import com.android.tools.r8.code.InvokeCustomRange; import com.android.tools.r8.code.InvokeDirect; import com.android.tools.r8.code.InvokeDirectRange; import com.android.tools.r8.code.InvokeInterface; @@ -197,6 +199,10 @@ return false; } + public boolean isInvokeCustom() { + return instruction instanceof InvokeCustom || instruction instanceof InvokeCustomRange; + } + public boolean isInvokeSuper() { return instruction instanceof InvokeSuper || instruction instanceof InvokeSuperRange; }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmDeclarationContainerSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmDeclarationContainerSubject.java index f30a0e5..34bc771 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmDeclarationContainerSubject.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmDeclarationContainerSubject.java
@@ -15,10 +15,15 @@ import kotlinx.metadata.KmFunction; import kotlinx.metadata.KmFunctionExtensionVisitor; import kotlinx.metadata.KmFunctionVisitor; +import kotlinx.metadata.KmProperty; +import kotlinx.metadata.KmPropertyExtensionVisitor; +import kotlinx.metadata.KmPropertyVisitor; import kotlinx.metadata.KmType; import kotlinx.metadata.KmTypeVisitor; +import kotlinx.metadata.jvm.JvmFieldSignature; import kotlinx.metadata.jvm.JvmFunctionExtensionVisitor; import kotlinx.metadata.jvm.JvmMethodSignature; +import kotlinx.metadata.jvm.JvmPropertyExtensionVisitor; public interface FoundKmDeclarationContainerSubject extends KmDeclarationContainerSubject { @@ -74,7 +79,7 @@ // TODO(b/145824437): This is a dup of KotlinMetadataJvmExtensionUtils$KmFunctionProcessor class KmFunctionProcessor { // Custom name via @JvmName("..."). Otherwise, null. - private JvmMethodSignature signature = null; + JvmMethodSignature signature = null; KmFunctionProcessor(KmFunction kmFunction) { kmFunction.accept(new KmFunctionVisitor() { @@ -93,26 +98,28 @@ } }); } - - JvmMethodSignature signature() { - return signature; - } } default KmFunctionSubject kmFunctionOrExtensionWithUniqueName(String name, boolean isExtension) { + KmFunction foundFunction = null; for (KmFunction kmFunction : getKmDeclarationContainer().getFunctions()) { if (KmFunctionSubject.isExtension(kmFunction) != isExtension) { continue; } if (kmFunction.getName().equals(name)) { - return new FoundKmFunctionSubject(codeInspector(), kmFunction); + foundFunction = kmFunction; + break; } KmFunctionProcessor kmFunctionProcessor = new KmFunctionProcessor(kmFunction); - if (kmFunctionProcessor.signature() != null - && kmFunctionProcessor.signature().getName().equals(name)) { - return new FoundKmFunctionSubject(codeInspector(), kmFunction); + if (kmFunctionProcessor.signature != null + && kmFunctionProcessor.signature.getName().equals(name)) { + foundFunction = kmFunction; + break; } } + if (foundFunction != null) { + return new FoundKmFunctionSubject(codeInspector(), foundFunction); + } return new AbsentKmFunctionSubject(); } @@ -159,6 +166,91 @@ .collect(Collectors.toList()); } + // TODO(b/145824437): This is a dup of KotlinMetadataJvmExtensionUtils$KmPropertyProcessor + static class KmPropertyProcessor { + JvmFieldSignature fieldSignature = null; + // Custom getter via @get:JvmName("..."). Otherwise, null. + JvmMethodSignature getterSignature = null; + // Custom getter via @set:JvmName("..."). Otherwise, null. + JvmMethodSignature setterSignature = null; + + KmPropertyProcessor(KmProperty kmProperty) { + kmProperty.accept(new KmPropertyVisitor() { + @Override + public KmPropertyExtensionVisitor visitExtensions(KmExtensionType type) { + if (type != JvmPropertyExtensionVisitor.TYPE) { + return null; + } + return new JvmPropertyExtensionVisitor() { + @Override + public void visit( + int flags, + JvmFieldSignature fieldDesc, + JvmMethodSignature getterDesc, + JvmMethodSignature setterDesc) { + assert fieldSignature == null : fieldSignature.asString(); + fieldSignature = fieldDesc; + assert getterSignature == null : getterSignature.asString(); + getterSignature = getterDesc; + assert setterSignature == null : setterSignature.asString(); + setterSignature = setterDesc; + } + }; + } + }); + } + } + + default KmPropertySubject kmPropertyOrExtensionWithUniqueName(String name, boolean isExtension) { + KmProperty foundProperty = null; + for (KmProperty kmProperty : getKmDeclarationContainer().getProperties()) { + if (KmPropertySubject.isExtension(kmProperty) != isExtension) { + continue; + } + if (kmProperty.getName().equals(name)) { + foundProperty = kmProperty; + break; + } + KmPropertyProcessor kmPropertyProcessor = new KmPropertyProcessor(kmProperty); + if (kmPropertyProcessor.fieldSignature != null + && kmPropertyProcessor.fieldSignature.getName().equals(name)) { + foundProperty = kmProperty; + break; + } + if (kmPropertyProcessor.getterSignature != null + && kmPropertyProcessor.getterSignature.getName().equals(name)) { + foundProperty = kmProperty; + break; + } + if (kmPropertyProcessor.setterSignature != null + && kmPropertyProcessor.setterSignature.getName().equals(name)) { + foundProperty = kmProperty; + break; + } + } + if (foundProperty != null) { + return new FoundKmPropertySubject(codeInspector(), foundProperty); + } + return new AbsentKmPropertySubject(); + } + + @Override + default KmPropertySubject kmPropertyWithUniqueName(String name) { + return kmPropertyOrExtensionWithUniqueName(name, false); + } + + @Override + default KmPropertySubject kmPropertyExtensionWithUniqueName(String name) { + return kmPropertyOrExtensionWithUniqueName(name, true); + } + + @Override + default List<KmPropertySubject> getProperties() { + return getKmDeclarationContainer().getProperties().stream() + .map(kmProperty -> new FoundKmPropertySubject(codeInspector(), kmProperty)) + .collect(Collectors.toList()); + } + @Override default List<ClassSubject> getReturnTypesInProperties() { return getKmDeclarationContainer().getProperties().stream()
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmFunctionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmFunctionSubject.java index 4ee0f82..7c970c2 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmFunctionSubject.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmFunctionSubject.java
@@ -3,15 +3,20 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.utils.codeinspector; +import com.android.tools.r8.utils.codeinspector.FoundKmDeclarationContainerSubject.KmFunctionProcessor; import kotlinx.metadata.KmFunction; +import kotlinx.metadata.jvm.JvmMethodSignature; public class FoundKmFunctionSubject extends KmFunctionSubject { private final CodeInspector codeInspector; private final KmFunction kmFunction; + private final JvmMethodSignature signature; FoundKmFunctionSubject(CodeInspector codeInspector, KmFunction kmFunction) { this.codeInspector = codeInspector; this.kmFunction = kmFunction; + KmFunctionProcessor kmFunctionProcessor = new KmFunctionProcessor(kmFunction); + this.signature = kmFunctionProcessor.signature; } @Override @@ -36,4 +41,9 @@ public boolean isExtension() { return isExtension(kmFunction); } + + @Override + public JvmMethodSignature signature() { + return signature; + } }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmPropertySubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmPropertySubject.java new file mode 100644 index 0000000..4f9cc94 --- /dev/null +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmPropertySubject.java
@@ -0,0 +1,65 @@ +// 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.codeinspector; + +import com.android.tools.r8.utils.codeinspector.FoundKmDeclarationContainerSubject.KmPropertyProcessor; +import kotlinx.metadata.KmProperty; +import kotlinx.metadata.jvm.JvmFieldSignature; +import kotlinx.metadata.jvm.JvmMethodSignature; + +public class FoundKmPropertySubject extends KmPropertySubject { + private final CodeInspector codeInspector; + private final KmProperty kmProperty; + private final JvmFieldSignature fieldSignature; + private final JvmMethodSignature getterSignature; + private final JvmMethodSignature setterSignature; + + FoundKmPropertySubject(CodeInspector codeInspector, KmProperty kmProperty) { + this.codeInspector = codeInspector; + this.kmProperty = kmProperty; + KmPropertyProcessor kmPropertyProcessor = new KmPropertyProcessor(kmProperty); + this.fieldSignature = kmPropertyProcessor.fieldSignature; + this.getterSignature = kmPropertyProcessor.getterSignature; + this.setterSignature = kmPropertyProcessor.setterSignature; + } + + @Override + public boolean isPresent() { + return true; + } + + @Override + public boolean isRenamed() { + // TODO(b/70169921): How to determine it is renamed? + // backing field renamed? If no backing field exists, then examine getter/setter? + return false; + } + + @Override + public boolean isSynthetic() { + // TODO(b/70169921): This should return `true` conditionally if we start synthesizing @Metadata + // from scratch. + return false; + } + + @Override + public boolean isExtension() { + return isExtension(kmProperty); + } + + @Override + public JvmFieldSignature fieldSignature() { + return fieldSignature; + } + + @Override + public JvmMethodSignature getterSignature() { + return getterSignature; + } + + @Override + public JvmMethodSignature setterSignature() { + return setterSignature; + } +}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/KmDeclarationContainerSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/KmDeclarationContainerSubject.java index 677ad3d..0893077 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/KmDeclarationContainerSubject.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/KmDeclarationContainerSubject.java
@@ -22,5 +22,11 @@ List<ClassSubject> getReturnTypesInFunctions(); + KmPropertySubject kmPropertyWithUniqueName(String name); + + KmPropertySubject kmPropertyExtensionWithUniqueName(String name); + + List<KmPropertySubject> getProperties(); + List<ClassSubject> getReturnTypesInProperties(); }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/KmFunctionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/KmFunctionSubject.java index ca91912..2e9eeda 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/KmFunctionSubject.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/KmFunctionSubject.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.utils.codeinspector; import kotlinx.metadata.KmFunction; +import kotlinx.metadata.jvm.JvmMethodSignature; public abstract class KmFunctionSubject extends Subject { // TODO(b/145824437): This is a dup of KotlinMetadataSynthesizer#isExtension @@ -12,4 +13,6 @@ } public abstract boolean isExtension(); + + public abstract JvmMethodSignature signature(); }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/KmPropertySubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/KmPropertySubject.java new file mode 100644 index 0000000..9fb5a0a --- /dev/null +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/KmPropertySubject.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.utils.codeinspector; + +import kotlinx.metadata.KmProperty; +import kotlinx.metadata.jvm.JvmFieldSignature; +import kotlinx.metadata.jvm.JvmMethodSignature; + +public abstract class KmPropertySubject extends Subject { + // TODO(b/145824437): This is a dup of KotlinMetadataSynthesizer#isExtension + static boolean isExtension(KmProperty kmProperty) { + return kmProperty.getReceiverParameterType() != null; + } + + public abstract boolean isExtension(); + + public abstract JvmFieldSignature fieldSignature(); + + public abstract JvmMethodSignature getterSignature(); + + public abstract JvmMethodSignature setterSignature(); +}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java index b1c6958..d8d219e 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
@@ -65,6 +65,8 @@ type = "@Metadata.KmPackage"; } else if (subject instanceof KmFunctionSubject) { type = "@Metadata.KmFunction"; + } else if (subject instanceof KmPropertySubject) { + type = "@Metadata.KmProperty"; } return type; } @@ -85,6 +87,8 @@ name = ((KmPackageSubject) subject).getDexClass().toSourceString(); } else if (subject instanceof KmFunctionSubject) { name = ((KmFunctionSubject) subject).toString(); + } else if (subject instanceof KmPropertySubject) { + name = ((KmPropertySubject) subject).toString(); } return name; } @@ -365,7 +369,7 @@ }; } - public static Matcher<KmFunctionSubject> isExtension() { + public static Matcher<KmFunctionSubject> isExtensionFunction() { return new TypeSafeMatcher<KmFunctionSubject>() { @Override protected boolean matchesSafely(KmFunctionSubject kmFunction) { @@ -388,6 +392,29 @@ }; } + public static Matcher<KmPropertySubject> isExtensionProperty() { + return new TypeSafeMatcher<KmPropertySubject>() { + @Override + protected boolean matchesSafely(KmPropertySubject kmProperty) { + return kmProperty.isPresent() && kmProperty.isExtension(); + } + + @Override + public void describeTo(Description description) { + description.appendText("is extension property"); + } + + @Override + public void describeMismatchSafely( + final KmPropertySubject kmProperty, Description description) { + description + .appendText("kmProperty ") + .appendValue(kmProperty) + .appendText(" was not"); + } + }; + } + public static Matcher<RetraceMethodResult> isInlineFrame() { return new TypeSafeMatcher<RetraceMethodResult>() { @Override
diff --git a/tools/r8_release.py b/tools/r8_release.py index a694ae0..f74fd57 100755 --- a/tools/r8_release.py +++ b/tools/r8_release.py
@@ -19,7 +19,7 @@ import update_prebuilds_in_android import utils -R8_DEV_BRANCH = '2.0' +R8_DEV_BRANCH = '2.1' R8_VERSION_FILE = os.path.join( 'src', 'main', 'java', 'com', 'android', 'tools', 'r8', 'Version.java') THIS_FILE_RELATIVE = os.path.join('tools', 'r8_release.py')