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..77c4895 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')