blob: dfa38f590ee959e39b0c2b4f81faf9bab5768678 [file] [log] [blame]
// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.optimize.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.frame.FrameType;
import com.android.tools.r8.cf.code.frame.PreciseFrameType;
import com.android.tools.r8.cf.code.frame.UninitializedFrameType;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractState;
import com.android.tools.r8.ir.analysis.type.PrimitiveTypeElement;
import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.utils.FunctionUtils;
import com.android.tools.r8.utils.TriFunction;
import java.util.function.BiFunction;
import java.util.function.UnaryOperator;
public abstract class CfFrameState extends AbstractState<CfFrameState> {
public static BottomCfFrameState bottom() {
return BottomCfFrameState.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 errorUnexpectedStack(frameType, formatExpected(expectedType));
}
public static ErroneousCfFrameState errorUnexpectedStack(
FrameType frameType, FrameType expectedType) {
return errorUnexpectedStack(frameType, formatExpected(expectedType));
}
public static ErroneousCfFrameState errorUnexpectedStack(
FrameType frameType, ValueType expectedType) {
return errorUnexpectedStack(frameType, formatExpected(expectedType));
}
public static ErroneousCfFrameState errorUnexpectedStack(FrameType frameType, String expected) {
return internalError(formatActual(frameType), expected, "on stack");
}
private static ErroneousCfFrameState internalError(
String actual, String expected, String location) {
return error("Expected " + expected + " " + location + ", but was " + actual);
}
@Override
public CfFrameState asAbstractState() {
return this;
}
@Override
public boolean isGreaterThanOrEquals(CfFrameState state) {
if (this == state) {
return true;
}
CfFrameState leastUpperBound = join(state, UnaryOperator.identity());
return equals(leastUpperBound);
}
public boolean isBottom() {
return false;
}
public boolean isConcrete() {
return false;
}
public ConcreteCfFrameState asConcrete() {
return null;
}
public boolean isError() {
return false;
}
public ErroneousCfFrameState asError() {
return null;
}
public abstract CfFrameState check(AppView<?> appView, CfFrame frame);
public abstract CfFrameState checkLocals(AppView<?> appView, CfFrame frame);
public abstract CfFrameState checkStack(AppView<?> appView, CfFrame frame);
public abstract CfFrameState clear();
public abstract CfFrameState markInitialized(
UninitializedFrameType uninitializedType, DexType initializedType);
public abstract CfFrameState pop();
public abstract CfFrameState pop(BiFunction<CfFrameState, PreciseFrameType, CfFrameState> fn);
public abstract CfFrameState popAndInitialize(
AppView<?> appView, DexMethod constructor, CfAnalysisConfig config);
public abstract CfFrameState popArray(AppView<?> appView);
public final CfFrameState popInitialized(AppView<?> appView, DexType expectedType) {
return popInitialized(appView, expectedType, FunctionUtils::getFirst);
}
public abstract CfFrameState popInitialized(
AppView<?> appView,
DexType expectedType,
BiFunction<CfFrameState, PreciseFrameType, CfFrameState> fn);
public abstract CfFrameState popInitialized(AppView<?> appView, DexType... expectedTypes);
public final CfFrameState popInitialized(AppView<?> appView, MemberType memberType) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
return popInitialized(
appView,
FrameType.fromPreciseMemberType(memberType, dexItemFactory)
.getInitializedType(dexItemFactory));
}
public final CfFrameState popInitialized(AppView<?> appView, NumericType expectedType) {
return popInitialized(appView, expectedType.toDexType(appView.dexItemFactory()));
}
public final CfFrameState popInitialized(AppView<?> appView, ValueType valueType) {
return popInitialized(appView, valueType, FunctionUtils::getFirst);
}
public final CfFrameState popInitialized(
AppView<?> appView,
ValueType valueType,
BiFunction<CfFrameState, PreciseFrameType, CfFrameState> fn) {
return popInitialized(appView, valueType.toDexType(appView.dexItemFactory()), fn);
}
public final CfFrameState popObject(BiFunction<CfFrameState, PreciseFrameType, CfFrameState> fn) {
return pop(
(state, head) ->
head.isObject() ? fn.apply(state, head) : errorUnexpectedStack(head, ValueType.OBJECT));
}
@SuppressWarnings("InconsistentOverloads")
public final CfFrameState popObject(
AppView<?> appView,
DexType expectedType,
CfAnalysisConfig config,
BiFunction<CfFrameState, PreciseFrameType, CfFrameState> fn) {
return pop(
(state, head) ->
head.isObject()
&& CfAssignability.isAssignable(
head.getObjectType(config.getCurrentContext().getHolderType()),
expectedType,
appView)
? fn.apply(state, head)
: errorUnexpectedStack(head, expectedType));
}
public final CfFrameState popSingle() {
return popSingle((state, single) -> state);
}
public final CfFrameState popSingle(BiFunction<CfFrameState, PreciseFrameType, CfFrameState> fn) {
return pop(
(state, single) ->
single.isSingle()
? fn.apply(state, single)
: errorUnexpectedStack(single, FrameType.oneWord()));
}
public final CfFrameState popSingles(
TriFunction<CfFrameState, PreciseFrameType, PreciseFrameType, CfFrameState> fn) {
return popSingle(
(state1, single1) ->
state1.popSingle((state2, single2) -> fn.apply(state2, single2, single1)));
}
public final CfFrameState popSingleOrWide(
BiFunction<CfFrameState, PreciseFrameType, CfFrameState> singleFn,
BiFunction<CfFrameState, PreciseFrameType, CfFrameState> wideFn) {
return pop(
(state, head) -> head.isSingle() ? singleFn.apply(state, head) : wideFn.apply(state, head));
}
public final CfFrameState popSingleSingleOrWide(
TriFunction<CfFrameState, PreciseFrameType, PreciseFrameType, CfFrameState> singleSingleFn,
BiFunction<CfFrameState, PreciseFrameType, CfFrameState> wideFn) {
return popSingleOrWide(
(state1, single1) ->
state1.popSingle((state2, single2) -> singleSingleFn.apply(state2, single2, single1)),
wideFn);
}
public abstract CfFrameState push(CfAnalysisConfig config, DexType type);
public abstract CfFrameState push(CfAnalysisConfig config, PreciseFrameType frameType);
public final CfFrameState push(
CfAnalysisConfig config, PreciseFrameType frameType, PreciseFrameType frameType2) {
return push(config, frameType).push(config, frameType2);
}
public final CfFrameState push(
CfAnalysisConfig config,
PreciseFrameType frameType,
PreciseFrameType frameType2,
PreciseFrameType frameType3) {
return push(config, frameType).push(config, frameType2).push(config, frameType3);
}
public final CfFrameState push(
CfAnalysisConfig config,
PreciseFrameType frameType,
PreciseFrameType frameType2,
PreciseFrameType frameType3,
PreciseFrameType frameType4) {
return push(config, frameType)
.push(config, frameType2)
.push(config, frameType3)
.push(config, frameType4);
}
public final CfFrameState push(
CfAnalysisConfig config,
PreciseFrameType frameType,
PreciseFrameType frameType2,
PreciseFrameType frameType3,
PreciseFrameType frameType4,
PreciseFrameType frameType5) {
return push(config, frameType)
.push(config, frameType2)
.push(config, frameType3)
.push(config, frameType4)
.push(config, frameType5);
}
public final CfFrameState push(
CfAnalysisConfig config,
PreciseFrameType frameType,
PreciseFrameType frameType2,
PreciseFrameType frameType3,
PreciseFrameType frameType4,
PreciseFrameType frameType5,
PreciseFrameType frameType6) {
return push(config, frameType)
.push(config, frameType2)
.push(config, frameType3)
.push(config, frameType4)
.push(config, frameType5)
.push(config, frameType6);
}
@SuppressWarnings("InconsistentOverloads")
public final CfFrameState push(
AppView<?> appView, CfAnalysisConfig config, MemberType memberType) {
return push(config, FrameType.fromPreciseMemberType(memberType, appView.dexItemFactory()));
}
@SuppressWarnings("InconsistentOverloads")
public final CfFrameState push(
AppView<?> appView, CfAnalysisConfig config, NumericType numericType) {
return push(config, numericType.toDexType(appView.dexItemFactory()));
}
@SuppressWarnings("InconsistentOverloads")
public final CfFrameState push(AppView<?> appView, CfAnalysisConfig config, ValueType valueType) {
return push(config, valueType.toDexType(appView.dexItemFactory()));
}
public abstract CfFrameState readLocal(
AppView<?> appView,
int localIndex,
ValueType expectedType,
BiFunction<CfFrameState, FrameType, CfFrameState> consumer);
public abstract CfFrameState storeLocal(
int localIndex, FrameType frameType, CfAnalysisConfig config);
public final CfFrameState storeLocal(
int localIndex,
PrimitiveTypeElement primitiveType,
AppView<?> appView,
CfAnalysisConfig config) {
assert primitiveType.isInt()
|| primitiveType.isFloat()
|| primitiveType.isLong()
|| primitiveType.isDouble();
return storeLocal(
localIndex,
FrameType.initialized(primitiveType.toDexType(appView.dexItemFactory())),
config);
}
@Override
public final CfFrameState join(CfFrameState state) {
return join(
state, frameType -> frameType.isSingle() ? FrameType.oneWord() : FrameType.twoWord());
}
public final CfFrameState join(
CfFrameState state, UnaryOperator<FrameType> joinWithMissingLocal) {
if (state.isBottom() || isError()) {
return this;
}
if (isBottom() || state.isError()) {
return state;
}
assert isConcrete();
assert state.isConcrete();
return asConcrete().join(state.asConcrete(), joinWithMissingLocal);
}
@Override
public abstract boolean equals(Object other);
@Override
public abstract int hashCode();
}