More detailed enum unboxing diagnostics for debugging
Change-Id: I5b0491ca0953741a82e0935c6ea68a4a9ec23b85
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
index 6ab995d..58862d7 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
@@ -48,6 +48,10 @@
return getReference().asMethodReference();
}
+ public DexType getParameter(int index) {
+ return getReference().getParameter(index);
+ }
+
public DexTypeList getParameters() {
return getReference().getParameters();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Invoke.java b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
index 5793ec1..6fe8c6f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Invoke.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
@@ -25,6 +25,7 @@
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.utils.BooleanUtils;
import java.util.List;
import java.util.Set;
import org.objectweb.asm.Opcodes;
@@ -160,6 +161,11 @@
return arguments().get(index);
}
+ public Value getArgumentForParameter(int index) {
+ int offset = BooleanUtils.intValue(!isInvokeStatic());
+ return getArgument(index + offset);
+ }
+
public Value getFirstArgument() {
return getArgument(0);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index b637199..3a65557 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -77,6 +77,13 @@
import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldOrdinalData;
import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldUnknownData;
import com.android.tools.r8.ir.optimize.enums.eligibility.Reason;
+import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.IllegalInvokeWithImpreciseParameterTypeReason;
+import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.MissingContentsForEnumValuesArrayReason;
+import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.MissingEnumStaticFieldValuesReason;
+import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.MissingInstanceFieldValueForEnumInstanceReason;
+import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.MissingObjectStateForEnumInstanceReason;
+import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.UnsupportedInstanceFieldValueForEnumInstanceReason;
+import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.UnsupportedLibraryInvokeReason;
import com.android.tools.r8.ir.optimize.info.MutableFieldOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback.OptimizationInfoFixer;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
@@ -84,7 +91,6 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
import com.android.tools.r8.shaking.KeepInfoCollection;
-import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.collections.ImmutableInt2ReferenceSortedMap;
@@ -338,7 +344,7 @@
if (singleTarget != null && singleTarget.getReference() == factory.enumMembers.valueOf) {
// The name data is required for the correct mapping from the enum name to the ordinal in
// the valueOf utility method.
- addRequiredNameData(enumType);
+ addRequiredNameData(enumClass);
continue;
}
}
@@ -348,9 +354,9 @@
eligibleEnums.add(enumType);
}
- private void addRequiredNameData(DexType enumType) {
+ private void addRequiredNameData(DexProgramClass enumClass) {
enumUnboxingCandidatesInfo.addRequiredEnumInstanceFieldData(
- enumType, factory.enumMembers.nameField);
+ enumClass, factory.enumMembers.nameField);
}
private boolean isUnboxableNameMethod(DexMethod method) {
@@ -507,10 +513,9 @@
private EnumDataMap analyzeEnumInstances() {
ImmutableMap.Builder<DexType, EnumData> builder = ImmutableMap.builder();
enumUnboxingCandidatesInfo.forEachCandidateAndRequiredInstanceFieldData(
- (enumClass, fields) -> {
- EnumData data = buildData(enumClass, fields);
+ (enumClass, instanceFields) -> {
+ EnumData data = buildData(enumClass, instanceFields);
if (data == null) {
- markEnumAsUnboxable(Reason.MISSING_INSTANCE_FIELD_DATA, enumClass);
return;
}
if (!debugLogEnabled || !debugLogs.containsKey(enumClass.getType())) {
@@ -521,7 +526,7 @@
return new EnumDataMap(builder.build());
}
- private EnumData buildData(DexProgramClass enumClass, Set<DexField> fields) {
+ private EnumData buildData(DexProgramClass enumClass, Set<DexField> instanceFields) {
// This map holds all the accessible fields to their unboxed value, so we can remap the field
// read to the unboxed value.
ImmutableMap.Builder<DexField, Integer> unboxedValues = ImmutableMap.builder();
@@ -535,6 +540,7 @@
EnumStaticFieldValues enumStaticFieldValues = staticFieldValuesMap.get(enumClass.type);
if (enumStaticFieldValues == null) {
+ reportFailure(enumClass, new MissingEnumStaticFieldValuesReason());
return null;
}
@@ -550,10 +556,16 @@
continue;
}
// We could not track the content of that field. We bail out.
+ reportFailure(
+ enumClass, new MissingObjectStateForEnumInstanceReason(staticField.getReference()));
return null;
}
OptionalInt optionalOrdinal = getOrdinal(enumState);
if (!optionalOrdinal.isPresent()) {
+ reportFailure(
+ enumClass,
+ new MissingInstanceFieldValueForEnumInstanceReason(
+ staticField.getReference(), factory.enumMembers.ordinalField));
return null;
}
int ordinal = optionalOrdinal.getAsInt();
@@ -570,6 +582,8 @@
// We could not track the content of that field. We bail out.
// We could not track the content of that field, and the field could be a values field.
// We conservatively bail out.
+ reportFailure(
+ enumClass, new MissingContentsForEnumValuesArrayReason(staticField.getReference()));
return null;
}
assert valuesState.isEnumValuesObjectState();
@@ -600,25 +614,41 @@
// The ordinalToObjectState map may have holes at this point, if some enum instances are never
// used ($VALUES unused or removed, and enum instance field unused or removed), it contains
// only data for reachable enum instance, that is what we're interested in.
- ImmutableMap.Builder<DexField, EnumInstanceFieldKnownData> instanceFieldBuilder =
- ImmutableMap.builder();
- for (DexField instanceField : fields) {
- EnumInstanceFieldData fieldData =
- computeEnumFieldData(instanceField, enumClass, ordinalToObjectState);
- if (fieldData.isUnknown()) {
- return null;
- }
- instanceFieldBuilder.put(instanceField, fieldData.asEnumFieldKnownData());
+ ImmutableMap<DexField, EnumInstanceFieldKnownData> instanceFieldsData =
+ computeRequiredEnumInstanceFieldsData(enumClass, instanceFields, ordinalToObjectState);
+ if (instanceFieldsData == null) {
+ return null;
}
return new EnumData(
- instanceFieldBuilder.build(),
+ instanceFieldsData,
unboxedValues.build(),
valuesField.build(),
valuesContents == null ? EnumData.INVALID_VALUES_SIZE : valuesContents.getEnumValuesSize());
}
- private EnumInstanceFieldData computeEnumFieldData(
+ private ImmutableMap<DexField, EnumInstanceFieldKnownData> computeRequiredEnumInstanceFieldsData(
+ DexProgramClass enumClass,
+ Set<DexField> instanceFields,
+ Int2ReferenceMap<ObjectState> ordinalToObjectState) {
+ ImmutableMap.Builder<DexField, EnumInstanceFieldKnownData> builder = ImmutableMap.builder();
+ for (DexField instanceField : instanceFields) {
+ EnumInstanceFieldData fieldData =
+ computeRequiredEnumInstanceFieldData(instanceField, enumClass, ordinalToObjectState);
+ if (fieldData.isUnknown()) {
+ if (!debugLogEnabled) {
+ return null;
+ }
+ builder = null;
+ }
+ if (builder != null) {
+ builder.put(instanceField, fieldData.asEnumFieldKnownData());
+ }
+ }
+ return builder != null ? builder.build() : null;
+ }
+
+ private EnumInstanceFieldData computeRequiredEnumInstanceFieldData(
DexField instanceField,
DexProgramClass enumClass,
Int2ReferenceMap<ObjectState> ordinalToObjectState) {
@@ -631,7 +661,15 @@
for (Integer ordinal : ordinalToObjectState.keySet()) {
ObjectState state = ordinalToObjectState.get(ordinal);
AbstractValue fieldValue = state.getAbstractFieldValue(encodedInstanceField);
+ if (!fieldValue.isSingleValue()) {
+ reportFailure(
+ enumClass, new MissingInstanceFieldValueForEnumInstanceReason(ordinal, instanceField));
+ return EnumInstanceFieldUnknownData.getInstance();
+ }
if (!(fieldValue.isSingleNumberValue() || fieldValue.isSingleStringValue())) {
+ reportFailure(
+ enumClass,
+ new UnsupportedInstanceFieldValueForEnumInstanceReason(ordinal, instanceField));
return EnumInstanceFieldUnknownData.getInstance();
}
data.put(ordinalToUnboxedInt(ordinal), fieldValue);
@@ -1096,7 +1134,7 @@
Value enumValue) {
assert instanceGet.getField().holder == enumClass.type;
DexField field = instanceGet.getField();
- enumUnboxingCandidatesInfo.addRequiredEnumInstanceFieldData(enumClass.type, field);
+ enumUnboxingCandidatesInfo.addRequiredEnumInstanceFieldData(enumClass, field);
return Reason.ELIGIBLE;
}
@@ -1118,10 +1156,11 @@
if (singleTarget == null) {
return Reason.INVALID_INVOKE;
}
- DexClass dexClass = singleTarget.getHolder();
- if (dexClass.isProgramClass()) {
- if (dexClass.isEnum() && singleTarget.getDefinition().isInstanceInitializer()) {
- if (code.method().getHolderType() == dexClass.type && code.method().isClassInitializer()) {
+ DexMethod singleTargetReference = singleTarget.getReference();
+ DexClass targetHolder = singleTarget.getHolder();
+ if (targetHolder.isProgramClass()) {
+ if (targetHolder.isEnum() && singleTarget.getDefinition().isInstanceInitializer()) {
+ if (code.context().getHolder() == targetHolder && code.method().isClassInitializer()) {
// The enum instance initializer is allowed to be called only from the enum clinit.
return Reason.ELIGIBLE;
} else {
@@ -1130,28 +1169,28 @@
}
// Check that the enum-value only flows into parameters whose type exactly matches the
// enum's type.
- int offset = BooleanUtils.intValue(!singleTarget.getDefinition().isStatic());
- for (int i = 0; i < singleTarget.getReference().getParameters().size(); i++) {
- if (invoke.getArgument(offset + i) == enumValue) {
- if (singleTarget.getReference().getParameter(i).toBaseType(factory) != enumClass.type) {
- return Reason.GENERIC_INVOKE;
- }
+ for (int i = 0; i < singleTarget.getParameters().size(); i++) {
+ if (invoke.getArgumentForParameter(i) == enumValue
+ && singleTarget.getParameter(i).toBaseType(factory) != enumClass.getType()) {
+ return new IllegalInvokeWithImpreciseParameterTypeReason(singleTargetReference);
}
}
if (invoke.isInvokeMethodWithReceiver()) {
Value receiver = invoke.asInvokeMethodWithReceiver().getReceiver();
- if (receiver == enumValue && dexClass.isInterface()) {
+ if (receiver == enumValue && targetHolder.isInterface()) {
return Reason.DEFAULT_METHOD_INVOKE;
}
}
return Reason.ELIGIBLE;
}
- if (dexClass.isClasspathClass()) {
- return Reason.INVALID_INVOKE;
+
+ if (targetHolder.isClasspathClass()) {
+ return Reason.INVALID_INVOKE_CLASSPATH;
}
- assert dexClass.isLibraryClass();
- DexMethod singleTargetReference = singleTarget.getReference();
- if (dexClass.type != factory.enumType) {
+
+ assert targetHolder.isLibraryClass();
+
+ if (targetHolder.getType() != factory.enumType) {
// System.identityHashCode(Object) is supported for proto enums.
// Object#getClass without outValue and Objects.requireNonNull are supported since R8
// rewrites explicit null checks to such instructions.
@@ -1159,7 +1198,7 @@
return Reason.ELIGIBLE;
}
if (singleTargetReference == factory.stringMembers.valueOf) {
- addRequiredNameData(enumClass.type);
+ addRequiredNameData(enumClass);
return Reason.ELIGIBLE;
}
if (singleTargetReference == factory.objectMembers.getClass
@@ -1171,7 +1210,7 @@
|| singleTargetReference == factory.objectsMethods.requireNonNullWithMessage) {
return Reason.ELIGIBLE;
}
- return Reason.UNSUPPORTED_LIBRARY_CALL;
+ return new UnsupportedLibraryInvokeReason(singleTargetReference);
}
// TODO(b/147860220): EnumSet and EnumMap may be interesting to model.
if (singleTargetReference == factory.enumMembers.compareTo) {
@@ -1181,7 +1220,7 @@
} else if (singleTargetReference == factory.enumMembers.nameMethod
|| singleTargetReference == factory.enumMembers.toString) {
assert invoke.asInvokeMethodWithReceiver().getReceiver() == enumValue;
- addRequiredNameData(enumClass.type);
+ addRequiredNameData(enumClass);
return Reason.ELIGIBLE;
} else if (singleTargetReference == factory.enumMembers.ordinalMethod) {
return Reason.ELIGIBLE;
@@ -1189,12 +1228,11 @@
return Reason.ELIGIBLE;
} else if (singleTargetReference == factory.enumMembers.constructor) {
// Enum constructor call is allowed only if called from an enum initializer.
- if (code.method().isInstanceInitializer()
- && code.method().getHolderType() == enumClass.type) {
+ if (code.method().isInstanceInitializer() && code.context().getHolder() == enumClass) {
return Reason.ELIGIBLE;
}
}
- return Reason.UNSUPPORTED_LIBRARY_CALL;
+ return new UnsupportedLibraryInvokeReason(singleTargetReference);
}
// Return is used for valueOf methods.
@@ -1271,24 +1309,27 @@
sb.append(System.lineSeparator());
// Print information about how often a given Reason kind prevents enum unboxing.
- Object2IntMap<Reason> reasonCount = new Object2IntOpenHashMap<>();
+ Object2IntMap<Object> reasonKindCount = new Object2IntOpenHashMap<>();
debugLogs.forEach(
(type, reasons) ->
- reasons.forEach(reason -> reasonCount.put(reason, reasonCount.getInt(reason) + 1)));
- List<Reason> differentReasons = new ArrayList<>(reasonCount.keySet());
- differentReasons.sort(
- (x, y) -> {
- int freq = reasonCount.getInt(x) - reasonCount.getInt(y);
- return freq != 0 ? freq : System.identityHashCode(x) - System.identityHashCode(y);
+ reasons.forEach(
+ reason ->
+ reasonKindCount.put(reason.getKind(), reasonKindCount.getInt(reason) + 1)));
+ List<Object> differentReasonKinds = new ArrayList<>(reasonKindCount.keySet());
+ differentReasonKinds.sort(
+ (reasonKind, other) -> {
+ int freq = reasonKindCount.getInt(reasonKind) - reasonKindCount.getInt(other);
+ return freq != 0
+ ? freq
+ : System.identityHashCode(reasonKind) - System.identityHashCode(other);
});
- differentReasons.forEach(
- x -> {
- sb.append(x)
- .append(" (")
- .append(reasonCount.getInt(x))
- .append(")")
- .append(System.lineSeparator());
- });
+ differentReasonKinds.forEach(
+ reasonKind ->
+ sb.append(reasonKind)
+ .append(" (")
+ .append(reasonKindCount.getInt(reasonKind))
+ .append(")")
+ .append(System.lineSeparator()));
reporter.info(new StringDiagnostic(sb.toString()));
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
index c45e71a9..62b05a9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.optimize.enums.eligibility.Reason;
+import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.UnsupportedStaticFieldReason;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.KeepInfoCollection;
@@ -70,9 +71,6 @@
result = false;
}
if (!enumHasBasicStaticFields(clazz)) {
- if (!enumUnboxer.reportFailure(clazz, Reason.UNEXPECTED_STATIC_FIELD)) {
- return false;
- }
result = false;
}
return result;
@@ -81,32 +79,37 @@
// The enum should have the $VALUES static field and only fields directly referencing the enum
// instances.
private boolean enumHasBasicStaticFields(DexProgramClass clazz) {
+ boolean result = true;
for (DexEncodedField staticField : clazz.staticFields()) {
- if (isEnumField(staticField, clazz.type)) {
+ if (isEnumField(staticField, clazz)) {
// Enum field, valid, do nothing.
- } else if (matchesValuesField(staticField, clazz.type, factory)) {
+ } else if (matchesValuesField(staticField, clazz, factory)) {
// Field $VALUES, valid, do nothing.
} else if (appView.appInfo().isFieldRead(staticField)) {
// Only non read static fields are valid, and they are assumed unused.
- return false;
+ if (!enumUnboxer.reportFailure(
+ clazz, new UnsupportedStaticFieldReason(staticField.getReference()))) {
+ return false;
+ }
+ result = false;
}
}
- return true;
+ return result;
}
- static boolean isEnumField(DexEncodedField staticField, DexType enumType) {
- return staticField.getReference().type == enumType
- && staticField.accessFlags.isEnum()
- && staticField.accessFlags.isFinal();
+ static boolean isEnumField(DexEncodedField staticField, DexProgramClass enumClass) {
+ return staticField.getType() == enumClass.getType()
+ && staticField.isEnum()
+ && staticField.isFinal();
}
static boolean matchesValuesField(
- DexEncodedField staticField, DexType enumType, DexItemFactory factory) {
- return staticField.getReference().type.isArrayType()
- && staticField.getReference().type.toArrayElementType(factory) == enumType
- && staticField.accessFlags.isSynthetic()
- && staticField.accessFlags.isFinal()
- && staticField.getReference().name == factory.enumValuesFieldName;
+ DexEncodedField staticField, DexProgramClass enumClass, DexItemFactory factory) {
+ return staticField.getType().isArrayType()
+ && staticField.getType().toArrayElementType(factory) == enumClass.getType()
+ && staticField.isSynthetic()
+ && staticField.isFinal()
+ && staticField.getName() == factory.enumValuesFieldName;
}
private void removeEnumsInAnnotations() {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java
index f36474d..7d85b8d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java
@@ -81,11 +81,11 @@
info.addMethodDependency(programMethod);
}
- public void addRequiredEnumInstanceFieldData(DexType enumType, DexField field) {
+ public void addRequiredEnumInstanceFieldData(DexProgramClass enumClass, DexField field) {
// The enumType may be removed concurrently map from enumTypeToInfo. It means in that
// case the enum is no longer a candidate, and dependencies don't need to be recorded
// anymore.
- EnumUnboxingCandidateInfo info = enumTypeToInfo.get(enumType);
+ EnumUnboxingCandidateInfo info = enumTypeToInfo.get(enumClass.getType());
if (info == null) {
return;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java
index 2cd2098..5aef5cc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java
@@ -4,46 +4,238 @@
package com.android.tools.r8.ir.optimize.enums.eligibility;
-public class Reason {
- public static final Reason ELIGIBLE = new Reason("ELIGIBLE");
- public static final Reason ACCESSIBILITY = new Reason("ACCESSIBILITY");
- public static final Reason ANNOTATION = new Reason("ANNOTATION");
- public static final Reason PINNED = new Reason("PINNED");
- public static final Reason DOWN_CAST = new Reason("DOWN_CAST");
- public static final Reason SUBTYPES = new Reason("SUBTYPES");
- public static final Reason MANY_INSTANCE_FIELDS = new Reason("MANY_INSTANCE_FIELDS");
- public static final Reason GENERIC_INVOKE = new Reason("GENERIC_INVOKE");
- public static final Reason DEFAULT_METHOD_INVOKE = new Reason("DEFAULT_METHOD_INVOKE");
- public static final Reason UNEXPECTED_STATIC_FIELD = new Reason("UNEXPECTED_STATIC_FIELD");
- public static final Reason UNRESOLVABLE_FIELD = new Reason("UNRESOLVABLE_FIELD");
- public static final Reason CONST_CLASS = new Reason("CONST_CLASS");
- public static final Reason INVALID_PHI = new Reason("INVALID_PHI");
- public static final Reason NO_INIT = new Reason("NO_INIT");
- public static final Reason INVALID_INIT = new Reason("INVALID_INIT");
- public static final Reason INVALID_CLINIT = new Reason("INVALID_CLINIT");
- public static final Reason INVALID_INVOKE = new Reason("INVALID_INVOKE");
- public static final Reason INVALID_INVOKE_ON_ARRAY = new Reason("INVALID_INVOKE_ON_ARRAY");
- public static final Reason IMPLICIT_UP_CAST_IN_RETURN = new Reason("IMPLICIT_UP_CAST_IN_RETURN");
- public static final Reason UNSUPPORTED_LIBRARY_CALL = new Reason("UNSUPPORTED_LIBRARY_CALL");
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.google.common.collect.ImmutableList;
+
+public abstract class Reason {
+ public static final Reason ELIGIBLE = new StringReason("ELIGIBLE");
+ public static final Reason ACCESSIBILITY = new StringReason("ACCESSIBILITY");
+ public static final Reason ANNOTATION = new StringReason("ANNOTATION");
+ public static final Reason PINNED = new StringReason("PINNED");
+ public static final Reason DOWN_CAST = new StringReason("DOWN_CAST");
+ public static final Reason SUBTYPES = new StringReason("SUBTYPES");
+ public static final Reason MANY_INSTANCE_FIELDS = new StringReason("MANY_INSTANCE_FIELDS");
+ public static final Reason DEFAULT_METHOD_INVOKE = new StringReason("DEFAULT_METHOD_INVOKE");
+ public static final Reason UNRESOLVABLE_FIELD = new StringReason("UNRESOLVABLE_FIELD");
+ public static final Reason CONST_CLASS = new StringReason("CONST_CLASS");
+ public static final Reason INVALID_PHI = new StringReason("INVALID_PHI");
+ public static final Reason NO_INIT = new StringReason("NO_INIT");
+ public static final Reason INVALID_INIT = new StringReason("INVALID_INIT");
+ public static final Reason INVALID_CLINIT = new StringReason("INVALID_CLINIT");
+ public static final Reason INVALID_INVOKE = new StringReason("INVALID_INVOKE");
+ public static final Reason INVALID_INVOKE_CLASSPATH =
+ new StringReason("INVALID_INVOKE_CLASSPATH");
+ public static final Reason INVALID_INVOKE_ON_ARRAY = new StringReason("INVALID_INVOKE_ON_ARRAY");
+ public static final Reason IMPLICIT_UP_CAST_IN_RETURN =
+ new StringReason("IMPLICIT_UP_CAST_IN_RETURN");
public static final Reason MISSING_INSTANCE_FIELD_DATA =
- new Reason("MISSING_INSTANCE_FIELD_DATA");
- public static final Reason INVALID_FIELD_PUT = new Reason("INVALID_FIELD_PUT");
- public static final Reason INVALID_ARRAY_PUT = new Reason("INVALID_ARRAY_PUT");
- public static final Reason TYPE_MISMATCH_FIELD_PUT = new Reason("TYPE_MISMATCH_FIELD_PUT");
- public static final Reason INVALID_IF_TYPES = new Reason("INVALID_IF_TYPES");
+ new StringReason("MISSING_INSTANCE_FIELD_DATA");
+ public static final Reason INVALID_FIELD_PUT = new StringReason("INVALID_FIELD_PUT");
+ public static final Reason INVALID_ARRAY_PUT = new StringReason("INVALID_ARRAY_PUT");
+ public static final Reason TYPE_MISMATCH_FIELD_PUT = new StringReason("TYPE_MISMATCH_FIELD_PUT");
+ public static final Reason INVALID_IF_TYPES = new StringReason("INVALID_IF_TYPES");
public static final Reason ENUM_METHOD_CALLED_WITH_NULL_RECEIVER =
- new Reason("ENUM_METHOD_CALLED_WITH_NULL_RECEIVER");
+ new StringReason("ENUM_METHOD_CALLED_WITH_NULL_RECEIVER");
public static final Reason OTHER_UNSUPPORTED_INSTRUCTION =
- new Reason("OTHER_UNSUPPORTED_INSTRUCTION");
+ new StringReason("OTHER_UNSUPPORTED_INSTRUCTION");
- private final String message;
-
- public Reason(String message) {
- this.message = message;
- }
+ public abstract Object getKind();
@Override
- public String toString() {
- return message;
+ public abstract String toString();
+
+ public static class StringReason extends Reason {
+
+ private final String message;
+
+ public StringReason(String message) {
+ this.message = message;
+ }
+
+ @Override
+ public Object getKind() {
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return message;
+ }
+ }
+
+ public static class IllegalInvokeWithImpreciseParameterTypeReason extends Reason {
+
+ private final DexMethod invokedMethod;
+
+ public IllegalInvokeWithImpreciseParameterTypeReason(DexMethod invokedMethod) {
+ this.invokedMethod = invokedMethod;
+ }
+
+ @Override
+ public Object getKind() {
+ return getClass();
+ }
+
+ @Override
+ public String toString() {
+ return "IllegalInvokeWithImpreciseParameterType(" + invokedMethod.toSourceString() + ")";
+ }
+ }
+
+ public static class MissingEnumStaticFieldValuesReason extends Reason {
+
+ @Override
+ public Object getKind() {
+ return getClass();
+ }
+
+ @Override
+ public String toString() {
+ return "MissingEnumStaticFieldValues";
+ }
+ }
+
+ public static class MissingContentsForEnumValuesArrayReason extends Reason {
+
+ private final DexField valuesField;
+
+ public MissingContentsForEnumValuesArrayReason(DexField valuesField) {
+ this.valuesField = valuesField;
+ }
+
+ @Override
+ public Object getKind() {
+ return getClass();
+ }
+
+ @Override
+ public String toString() {
+ return "MissingContentsForEnumValuesArray(" + valuesField.toSourceString() + ")";
+ }
+ }
+
+ public static class MissingInstanceFieldValueForEnumInstanceReason extends Reason {
+
+ private final DexField enumField;
+ private final int ordinal;
+ private final DexField instanceField;
+
+ public MissingInstanceFieldValueForEnumInstanceReason(
+ DexField enumField, DexField instanceField) {
+ this.enumField = enumField;
+ this.ordinal = -1;
+ this.instanceField = instanceField;
+ }
+
+ public MissingInstanceFieldValueForEnumInstanceReason(int ordinal, DexField instanceField) {
+ this.enumField = null;
+ this.ordinal = ordinal;
+ this.instanceField = instanceField;
+ }
+
+ @Override
+ public Object getKind() {
+ return getClass();
+ }
+
+ @Override
+ public String toString() {
+ if (enumField != null) {
+ return "MissingInstanceFieldValueForEnumInstance(enum field="
+ + enumField.toSourceString()
+ + ", instance field="
+ + instanceField.toSourceString()
+ + ")";
+ }
+ assert ordinal >= 0;
+ return "MissingInstanceFieldValueForEnumInstance(ordinal="
+ + ordinal
+ + ", instance field="
+ + instanceField.toSourceString()
+ + ")";
+ }
+ }
+
+ public static class MissingObjectStateForEnumInstanceReason extends Reason {
+
+ private final DexField enumField;
+
+ public MissingObjectStateForEnumInstanceReason(DexField enumField) {
+ this.enumField = enumField;
+ }
+
+ @Override
+ public Object getKind() {
+ return getClass();
+ }
+
+ @Override
+ public String toString() {
+ return "MissingObjectStateForEnumInstance(" + enumField + ")";
+ }
+ }
+
+ public static class UnsupportedInstanceFieldValueForEnumInstanceReason extends Reason {
+
+ private final int ordinal;
+ private final DexField instanceField;
+
+ public UnsupportedInstanceFieldValueForEnumInstanceReason(int ordinal, DexField instanceField) {
+ this.ordinal = ordinal;
+ this.instanceField = instanceField;
+ }
+
+ @Override
+ public Object getKind() {
+ return getClass();
+ }
+
+ @Override
+ public String toString() {
+ return "UnsupportedInstanceFieldValueForEnumInstance(ordinal="
+ + ordinal
+ + ", instance field="
+ + instanceField.toSourceString()
+ + ")";
+ }
+ }
+
+ public static class UnsupportedLibraryInvokeReason extends Reason {
+
+ private final DexMethod invokedMethod;
+
+ public UnsupportedLibraryInvokeReason(DexMethod invokedMethod) {
+ this.invokedMethod = invokedMethod;
+ }
+
+ @Override
+ public Object getKind() {
+ return ImmutableList.of(getClass(), invokedMethod);
+ }
+
+ @Override
+ public String toString() {
+ return "UnsupportedLibraryInvoke(" + invokedMethod.toSourceString() + ")";
+ }
+ }
+
+ public static class UnsupportedStaticFieldReason extends Reason {
+
+ private final DexField field;
+
+ public UnsupportedStaticFieldReason(DexField field) {
+ this.field = field;
+ }
+
+ @Override
+ public Object getKind() {
+ return getClass();
+ }
+
+ @Override
+ public String toString() {
+ return "UnsupportedStaticField(" + field.toSourceString() + ")";
+ }
}
}