Add message to ErroneousCfFrameState
Change-Id: Id6a38aa1739d9c8499271ca9bc8da0b1c3887346
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldWrite.java b/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldWrite.java
index ea0d0f8..5695d7e 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldWrite.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldWrite.java
@@ -4,8 +4,10 @@
package com.android.tools.r8.cf.code;
+import static com.android.tools.r8.optimize.interfaces.analysis.ErroneousCfFrameState.formatActual;
import static com.android.tools.r8.utils.BiPredicateUtils.or;
+import com.android.tools.r8.cf.code.CfFrame.FrameType;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.DexClassAndMethod;
@@ -21,6 +23,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
+import com.android.tools.r8.optimize.interfaces.analysis.ErroneousCfFrameState;
import java.util.ListIterator;
import org.objectweb.asm.Opcodes;
@@ -94,6 +97,14 @@
appView,
getField().getHolderType(),
context,
- (state, head) -> head.isUninitializedNew() ? CfFrameState.error() : state);
+ (state, head) -> head.isUninitializedNew() ? error(head) : state);
+ }
+
+ private ErroneousCfFrameState error(FrameType objectType) {
+ return CfFrameState.error(
+ "Frame type "
+ + formatActual(objectType)
+ + " is not assignable to "
+ + getField().getHolderType().getTypeName());
}
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java b/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java
index ab730fb..a338a14 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java
@@ -100,7 +100,7 @@
ProgramMethod context,
AppView<?> appView,
DexItemFactory dexItemFactory) {
- return CfFrameState.error();
+ return CfFrameState.error("Unexpected JSR/RET instruction");
}
public int getLocal() {
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index 466e1cf..921d0a6 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -269,6 +269,10 @@
return toSourceString(false, false);
}
+ public String toSourceStringWithoutReturnType() {
+ return toSourceString(true, false);
+ }
+
private String toSourceString(boolean includeHolder, boolean includeReturnType) {
StringBuilder builder = new StringBuilder();
if (includeReturnType) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/BooleanTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/BooleanTypeElement.java
index eea19ef..e00ecf9 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/BooleanTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/BooleanTypeElement.java
@@ -12,6 +12,11 @@
}
@Override
+ public String getTypeName() {
+ return "boolean";
+ }
+
+ @Override
boolean isBoolean() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ByteTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ByteTypeElement.java
index c9e5e9f..849f508 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ByteTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ByteTypeElement.java
@@ -12,6 +12,11 @@
}
@Override
+ public String getTypeName() {
+ return "byte";
+ }
+
+ @Override
boolean isByte() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/CharTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/CharTypeElement.java
index 0d33bfd..5a47cda 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/CharTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/CharTypeElement.java
@@ -12,6 +12,11 @@
}
@Override
+ public String getTypeName() {
+ return "char";
+ }
+
+ @Override
boolean isChar() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/DoubleTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/DoubleTypeElement.java
index e9632a8..85a501c 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/DoubleTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/DoubleTypeElement.java
@@ -12,6 +12,11 @@
}
@Override
+ public String getTypeName() {
+ return "double";
+ }
+
+ @Override
public boolean isDouble() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/FloatTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/FloatTypeElement.java
index 386981d..9efadf8 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/FloatTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/FloatTypeElement.java
@@ -11,6 +11,11 @@
}
@Override
+ public String getTypeName() {
+ return "float";
+ }
+
+ @Override
public boolean isFloat() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/IntTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/IntTypeElement.java
index 5ecb417..881cb19 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/IntTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/IntTypeElement.java
@@ -11,6 +11,11 @@
}
@Override
+ public String getTypeName() {
+ return "int";
+ }
+
+ @Override
public boolean isInt() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/LongTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/LongTypeElement.java
index caf2ed3..7f71468 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/LongTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/LongTypeElement.java
@@ -12,6 +12,11 @@
}
@Override
+ public String getTypeName() {
+ return "long";
+ }
+
+ @Override
public boolean isLong() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/PrimitiveTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/PrimitiveTypeElement.java
index 4e9b5f1a..9797274 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/PrimitiveTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/PrimitiveTypeElement.java
@@ -12,6 +12,8 @@
/** A {@link TypeElement} that abstracts primitive types. */
public abstract class PrimitiveTypeElement extends TypeElement {
+ public abstract String getTypeName();
+
@Override
public Nullability nullability() {
return Nullability.definitelyNotNull();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ShortTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ShortTypeElement.java
index 61b8514..fd7d65a 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ShortTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ShortTypeElement.java
@@ -12,6 +12,11 @@
}
@Override
+ public String getTypeName() {
+ return "short";
+ }
+
+ @Override
boolean isShort() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/SinglePrimitiveTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/SinglePrimitiveTypeElement.java
index 40436d1..5d3d16b 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/SinglePrimitiveTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/SinglePrimitiveTypeElement.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.analysis.type;
+import com.android.tools.r8.errors.Unreachable;
+
/** A {@link TypeElement} that abstracts primitive types, which fit in 32 bits. */
public class SinglePrimitiveTypeElement extends PrimitiveTypeElement {
@@ -17,6 +19,11 @@
}
@Override
+ public String getTypeName() {
+ throw new Unreachable("Unexpected attempt to get type name of " + this);
+ }
+
+ @Override
public boolean isSinglePrimitive() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/WidePrimitiveTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/WidePrimitiveTypeElement.java
index 0ae7426..f13800d 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/WidePrimitiveTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/WidePrimitiveTypeElement.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.analysis.type;
+import com.android.tools.r8.errors.Unreachable;
+
/** A {@link TypeElement} that abstracts primitive types, which fit in 64 bits. */
public class WidePrimitiveTypeElement extends PrimitiveTypeElement {
@@ -17,6 +19,11 @@
}
@Override
+ public String getTypeName() {
+ throw new Unreachable("Unexpected attempt to get type name of " + this);
+ }
+
+ @Override
public boolean isWidePrimitive() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/BottomCfFrameState.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/BottomCfFrameState.java
index a536102..61ba967 100644
--- a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/BottomCfFrameState.java
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/BottomCfFrameState.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.optimize.interfaces.analysis;
-import com.android.tools.r8.cf.code.CfAssignability;
import com.android.tools.r8.cf.code.CfFrame;
import com.android.tools.r8.cf.code.CfFrame.FrameType;
import com.android.tools.r8.graph.AppView;
@@ -31,11 +30,7 @@
@Override
public CfFrameState check(AppView<?> appView, CfFrame frame) {
- if (CfAssignability.isFrameAssignable(new CfFrame(), frame, appView).isFailed()) {
- return error();
- }
- CfFrame frameCopy = frame.mutableCopy();
- return new ConcreteCfFrameState(frameCopy.getMutableLocals(), frameCopy.getMutableStack());
+ return new ConcreteCfFrameState().check(appView, frame);
}
@Override
@@ -45,36 +40,37 @@
@Override
public CfFrameState markInitialized(FrameType uninitializedType, DexType initializedType) {
- return error();
+ // Initializing an uninitialized type is a no-op when the frame is empty.
+ return this;
}
@Override
- public CfFrameState pop() {
- return error();
+ public ErroneousCfFrameState pop() {
+ return error("Unexpected pop from empty stack");
}
@Override
- public CfFrameState pop(BiFunction<CfFrameState, FrameType, CfFrameState> fn) {
- return error();
+ public ErroneousCfFrameState pop(BiFunction<CfFrameState, FrameType, CfFrameState> fn) {
+ return pop();
}
@Override
- public CfFrameState popAndInitialize(
+ public ErroneousCfFrameState popAndInitialize(
AppView<?> appView, DexMethod constructor, ProgramMethod context) {
- return error();
+ return pop();
}
@Override
- public CfFrameState popInitialized(
+ public ErroneousCfFrameState popInitialized(
AppView<?> appView,
DexType expectedType,
BiFunction<CfFrameState, FrameType, CfFrameState> fn) {
- return error();
+ return pop();
}
@Override
public CfFrameState popInitialized(AppView<?> appView, DexType... expectedTypes) {
- return expectedTypes.length == 0 ? this : error();
+ return expectedTypes.length == 0 ? this : pop();
}
@Override
@@ -88,12 +84,12 @@
}
@Override
- public CfFrameState readLocal(
+ public ErroneousCfFrameState readLocal(
AppView<?> appView,
int localIndex,
ValueType expectedType,
BiFunction<CfFrameState, FrameType, CfFrameState> fn) {
- return error();
+ return error("Unexpected local read from empty frame");
}
@Override
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfFrameState.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfFrameState.java
index 0b297a9..91923f3 100644
--- a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfFrameState.java
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfFrameState.java
@@ -4,6 +4,9 @@
package com.android.tools.r8.optimize.interfaces.analysis;
+import static com.android.tools.r8.optimize.interfaces.analysis.ErroneousCfFrameState.formatActual;
+import static com.android.tools.r8.optimize.interfaces.analysis.ErroneousCfFrameState.formatExpected;
+
import com.android.tools.r8.cf.code.CfAssignability;
import com.android.tools.r8.cf.code.CfFrame;
import com.android.tools.r8.cf.code.CfFrame.FrameType;
@@ -28,8 +31,39 @@
return BottomCfFrameState.getInstance();
}
- public static ErroneousCfFrameState error() {
- return ErroneousCfFrameState.getInstance();
+ public static ErroneousCfFrameState error(String message) {
+ return new ErroneousCfFrameState(message);
+ }
+
+ public static ErroneousCfFrameState errorUnexpectedLocal(
+ FrameType frameType, ValueType expectedType, int localIndex) {
+ return internalError(
+ formatActual(frameType), formatExpected(expectedType), "at local index " + localIndex);
+ }
+
+ public static ErroneousCfFrameState errorUnexpectedStack(
+ FrameType frameType, DexType expectedType) {
+ return internalErrorUnexpectedStack(formatActual(frameType), formatExpected(expectedType));
+ }
+
+ public static ErroneousCfFrameState errorUnexpectedStack(
+ FrameType frameType, FrameType expectedType) {
+ return internalErrorUnexpectedStack(formatActual(frameType), formatExpected(expectedType));
+ }
+
+ public static ErroneousCfFrameState errorUnexpectedStack(
+ FrameType frameType, ValueType expectedType) {
+ return internalErrorUnexpectedStack(formatActual(frameType), formatExpected(expectedType));
+ }
+
+ private static ErroneousCfFrameState internalErrorUnexpectedStack(
+ String actual, String expected) {
+ return internalError(actual, expected, "on stack");
+ }
+
+ private static ErroneousCfFrameState internalError(
+ String actual, String expected, String location) {
+ return error("Expected " + expected + " " + location + ", but was " + actual);
}
@Override
@@ -62,6 +96,10 @@
return false;
}
+ public ErroneousCfFrameState asError() {
+ return null;
+ }
+
public abstract CfFrameState check(AppView<?> appView, CfFrame frame);
public abstract CfFrameState clear();
@@ -111,7 +149,9 @@
}
public final CfFrameState popObject(BiFunction<CfFrameState, FrameType, CfFrameState> fn) {
- return pop((state, head) -> head.isObject() ? fn.apply(state, head) : error());
+ return pop(
+ (state, head) ->
+ head.isObject() ? fn.apply(state, head) : errorUnexpectedStack(head, ValueType.OBJECT));
}
@SuppressWarnings("InconsistentOverloads")
@@ -126,7 +166,7 @@
&& CfAssignability.isAssignable(
head.getObjectType(context), expectedType, appView)
? fn.apply(state, head)
- : error());
+ : errorUnexpectedStack(head, expectedType));
}
public final CfFrameState popSingle() {
@@ -134,7 +174,11 @@
}
public final CfFrameState popSingle(BiFunction<CfFrameState, FrameType, CfFrameState> fn) {
- return pop((state, single) -> single.isSingle() ? fn.apply(state, single) : error());
+ return pop(
+ (state, single) ->
+ single.isSingle()
+ ? fn.apply(state, single)
+ : errorUnexpectedStack(single, FrameType.oneWord()));
}
public final CfFrameState popSingles(
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ConcreteCfFrameState.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ConcreteCfFrameState.java
index 1e958b8..55469be 100644
--- a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ConcreteCfFrameState.java
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ConcreteCfFrameState.java
@@ -5,8 +5,10 @@
package com.android.tools.r8.optimize.interfaces.analysis;
import static com.android.tools.r8.cf.code.CfFrame.getInitializedFrameType;
+import static com.android.tools.r8.optimize.interfaces.analysis.ErroneousCfFrameState.formatActual;
import com.android.tools.r8.cf.code.CfAssignability;
+import com.android.tools.r8.cf.code.CfAssignability.AssignabilityResult;
import com.android.tools.r8.cf.code.CfFrame;
import com.android.tools.r8.cf.code.CfFrame.FrameType;
import com.android.tools.r8.cf.code.frame.SingleFrameType;
@@ -16,6 +18,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.utils.FunctionUtils;
import com.google.common.collect.Iterables;
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
@@ -61,8 +64,10 @@
@Override
public CfFrameState check(AppView<?> appView, CfFrame frame) {
CfFrame currentFrame = CfFrame.builder().setLocals(locals).setStack(stack).build();
- if (CfAssignability.isFrameAssignable(currentFrame, frame, appView).isFailed()) {
- return error();
+ AssignabilityResult assignabilityResult =
+ CfAssignability.isFrameAssignable(currentFrame, frame, appView);
+ if (assignabilityResult.isFailed()) {
+ return error(assignabilityResult.asFailed().getMessage());
}
CfFrame frameCopy = frame.mutableCopy();
return new ConcreteCfFrameState(frameCopy.getMutableLocals(), frameCopy.getMutableStack());
@@ -76,7 +81,7 @@
@Override
public CfFrameState markInitialized(FrameType uninitializedType, DexType initializedType) {
if (uninitializedType.isInitialized()) {
- return error();
+ return error("Unexpected attempt to initialize already initialized type");
}
for (Int2ObjectMap.Entry<FrameType> entry : locals.int2ObjectEntrySet()) {
FrameType frameType = entry.getValue();
@@ -97,17 +102,14 @@
@Override
public CfFrameState pop() {
- if (stack.isEmpty()) {
- return error();
- }
- stack.removeLast();
- return this;
+ return pop(FunctionUtils::getFirst);
}
@Override
public CfFrameState pop(BiFunction<CfFrameState, FrameType, CfFrameState> fn) {
if (stack.isEmpty()) {
- return error();
+ // Return the same error as when popping from the bottom state.
+ return bottom().pop();
}
FrameType frameType = stack.removeLast();
return fn.apply(this, frameType);
@@ -118,35 +120,60 @@
AppView<?> appView, DexMethod constructor, ProgramMethod context) {
return pop(
(state, frameType) -> {
- if (frameType.isUninitializedThis()) {
- if (constructor.getHolderType() == context.getHolderType()
- || constructor.getHolderType() == context.getHolder().getSuperType()) {
- return state.markInitialized(frameType, context.getHolderType());
+ if (frameType.isUninitializedObject()) {
+ if (frameType.isUninitializedThis()) {
+ if (constructor.getHolderType() == context.getHolderType()
+ || constructor.getHolderType() == context.getHolder().getSuperType()) {
+ return state.markInitialized(frameType, context.getHolderType());
+ }
+ } else if (frameType.isUninitializedNew()) {
+ DexType uninitializedNewType = frameType.getUninitializedNewType();
+ if (constructor.getHolderType() == uninitializedNewType) {
+ return state.markInitialized(frameType, uninitializedNewType);
+ }
}
- } else if (frameType.isUninitializedNew()) {
- DexType uninitializedNewType = frameType.getUninitializedNewType();
- if (constructor.getHolderType() == uninitializedNewType) {
- return state.markInitialized(frameType, uninitializedNewType);
- }
+ return popAndInitializeConstructorMismatchError(frameType, constructor, context);
}
- return error();
+ return popAndInitializeInitializedObjectError(frameType);
});
}
+ private ErroneousCfFrameState popAndInitializeConstructorMismatchError(
+ FrameType frameType, DexMethod constructor, ProgramMethod context) {
+ assert frameType.isUninitializedObject();
+ StringBuilder message = new StringBuilder("Constructor mismatch, expected ");
+ if (frameType.isUninitializedNew()) {
+ message.append(frameType.getUninitializedNewType().getTypeName());
+ } else {
+ assert frameType.isUninitializedThis();
+ message
+ .append(context.getHolderType().getTypeName())
+ .append(" or ")
+ .append(context.getHolder().getSuperType().getTypeName());
+ }
+ message.append(" constructor, but was ").append(constructor.toSourceStringWithoutReturnType());
+ return error(message.toString());
+ }
+
+ private ErroneousCfFrameState popAndInitializeInitializedObjectError(FrameType frameType) {
+ return error("Unexpected attempt to initialize " + formatActual(frameType));
+ }
+
@Override
public CfFrameState popInitialized(
AppView<?> appView,
DexType expectedType,
BiFunction<CfFrameState, FrameType, CfFrameState> fn) {
return pop(
- (state, frameType) ->
- frameType.isInitialized()
- && CfAssignability.isAssignable(
- frameType.getInitializedType(appView.dexItemFactory()),
- expectedType,
- appView)
- ? fn.apply(state, frameType)
- : error());
+ (state, frameType) -> {
+ if (frameType.isInitialized()) {
+ DexType initializedType = frameType.getInitializedType(appView.dexItemFactory());
+ if (CfAssignability.isAssignable(initializedType, expectedType, appView)) {
+ return fn.apply(state, frameType);
+ }
+ }
+ return errorUnexpectedStack(frameType, FrameType.initialized(expectedType));
+ });
}
@Override
@@ -177,17 +204,17 @@
BiFunction<CfFrameState, FrameType, CfFrameState> fn) {
FrameType frameType = locals.get(localIndex);
if (frameType == null) {
- return error();
+ return error("Unexpected read of missing local at index " + localIndex);
}
- if (frameType.isInitialized()
- && CfAssignability.isAssignable(
- frameType.getInitializedType(appView.dexItemFactory()), expectedType, appView)) {
+ if (frameType.isInitialized()) {
+ if (CfAssignability.isAssignable(
+ frameType.getInitializedType(appView.dexItemFactory()), expectedType, appView)) {
+ return fn.apply(this, frameType);
+ }
+ } else if (frameType.isUninitializedObject() && expectedType.isObject()) {
return fn.apply(this, frameType);
}
- if (frameType.isUninitializedObject() && expectedType.isObject()) {
- return fn.apply(this, frameType);
- }
- return error();
+ return errorUnexpectedLocal(frameType, expectedType, localIndex);
}
@Override
@@ -431,29 +458,52 @@
private ErroneousCfFrameState joinStack(Deque<FrameType> stack, CfFrame.Builder builder) {
Iterator<FrameType> iterator = this.stack.iterator();
Iterator<FrameType> otherIterator = stack.iterator();
+ int stackIndex = 0;
while (iterator.hasNext() && otherIterator.hasNext()) {
FrameType frameType = iterator.next();
FrameType otherFrameType = otherIterator.next();
if (frameType.isSingle() != otherFrameType.isSingle()) {
- return error();
+ return error(
+ "Cannot join stacks, expected frame types at stack index "
+ + stackIndex
+ + " to have the same width, but was: "
+ + formatActual(frameType)
+ + " and "
+ + formatActual(otherFrameType));
}
if (frameType.isSingle()) {
SingleFrameType join = frameType.asSingle().join(otherFrameType.asSingle());
if (join.isOneWord()) {
- return error();
+ return joinStackImpreciseJoinError(stackIndex, frameType, otherFrameType);
}
builder.push(join.asFrameType());
} else {
WideFrameType join = frameType.asWide().join(otherFrameType.asWide());
if (join.isTwoWord()) {
- return error();
+ return joinStackImpreciseJoinError(stackIndex, frameType, otherFrameType);
}
builder.push(join.asFrameType());
}
+ stackIndex++;
+ }
+ if (iterator.hasNext() || otherIterator.hasNext()) {
+ return error("Cannot join stacks of different size");
}
return null;
}
+ private ErroneousCfFrameState joinStackImpreciseJoinError(
+ int stackIndex, FrameType first, FrameType second) {
+ return error(
+ "Cannot join stacks, expected frame types at stack index "
+ + stackIndex
+ + " to join to a precise (non-top) type, but types "
+ + formatActual(first)
+ + " and "
+ + formatActual(second)
+ + " do not");
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ErroneousCfFrameState.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ErroneousCfFrameState.java
index 23a6351..f08458f 100644
--- a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ErroneousCfFrameState.java
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ErroneousCfFrameState.java
@@ -16,12 +16,90 @@
/** An analysis state representing that the code does not type check. */
public class ErroneousCfFrameState extends CfFrameState {
- private static final ErroneousCfFrameState INSTANCE = new ErroneousCfFrameState();
+ private enum FormatKind {
+ ACTUAL,
+ EXPECTED
+ }
- private ErroneousCfFrameState() {}
+ private final String message;
- static ErroneousCfFrameState getInstance() {
- return INSTANCE;
+ ErroneousCfFrameState(String message) {
+ this.message = message;
+ }
+
+ public static String formatExpected(DexType type) {
+ return format(type);
+ }
+
+ private static String format(DexType type) {
+ if (type.isArrayType() || type.isClassType()) {
+ return type.getTypeName();
+ } else if (type.isNullValueType()) {
+ return "null";
+ } else {
+ assert type.isPrimitiveType();
+ return "primitive " + type.getTypeName();
+ }
+ }
+
+ public static String formatActual(FrameType frameType) {
+ return format(frameType, FormatKind.ACTUAL);
+ }
+
+ public static String formatExpected(FrameType frameType) {
+ return format(frameType, FormatKind.EXPECTED);
+ }
+
+ private static String format(FrameType frameType, FormatKind formatKind) {
+ if (frameType.isInitialized()) {
+ if (frameType.isObject()) {
+ DexType initializedType = frameType.asSingleInitializedType().getInitializedType();
+ if (initializedType.isArrayType()) {
+ return initializedType.getTypeName();
+ } else if (initializedType.isClassType()) {
+ return "initialized " + initializedType.getTypeName();
+ } else {
+ assert initializedType.isNullValueType();
+ return "null";
+ }
+ } else {
+ assert frameType.isPrimitive();
+ return "primitive " + frameType.asPrimitive().getTypeName();
+ }
+ } else if (frameType.isUninitializedObject()) {
+ if (frameType.isUninitializedNew()) {
+ DexType uninitializedNewType = frameType.getUninitializedNewType();
+ if (uninitializedNewType != null) {
+ return "uninitialized " + uninitializedNewType.getTypeName();
+ }
+ return "uninitialized-new";
+ } else {
+ return "uninitialized-this";
+ }
+ } else {
+ assert frameType.isOneWord() || frameType.isTwoWord();
+ if (formatKind == FormatKind.ACTUAL) {
+ return "top";
+ } else {
+ return frameType.isOneWord() ? "a single width value" : "a double width value";
+ }
+ }
+ }
+
+ public static String formatExpected(ValueType valueType) {
+ return format(valueType);
+ }
+
+ private static String format(ValueType valueType) {
+ if (valueType.isObject()) {
+ return "object";
+ } else {
+ return "primitive " + valueType.toPrimitiveType().getTypeName();
+ }
+ }
+
+ public String getMessage() {
+ return message;
}
@Override
@@ -30,6 +108,11 @@
}
@Override
+ public ErroneousCfFrameState asError() {
+ return this;
+ }
+
+ @Override
public CfFrameState check(AppView<?> appView, CfFrame frame) {
return this;
}