blob: 1cddb8f957d4cbfaf68739b4a9244d9b09c67369 [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 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;
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.graph.ProgramMethod;
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;
public abstract class CfFrameState extends AbstractState<CfFrameState> {
public static BottomCfFrameState bottom() {
return BottomCfFrameState.getInstance();
}
public static ErroneousCfFrameState error() {
return ErroneousCfFrameState.getInstance();
}
@Override
public CfFrameState asAbstractState() {
return this;
}
public boolean isBottom() {
return false;
}
public boolean isConcrete() {
return false;
}
public ConcreteCfFrameState asConcrete() {
return null;
}
public boolean isError() {
return false;
}
public abstract CfFrameState check(AppView<?> appView, CfFrame frame);
public abstract CfFrameState clear();
public abstract CfFrameState markInitialized(
FrameType uninitializedType, DexType initializedType);
public abstract CfFrameState pop();
public abstract CfFrameState pop(BiFunction<CfFrameState, FrameType, CfFrameState> fn);
public abstract CfFrameState popAndInitialize(
AppView<?> appView, DexMethod constructor, ProgramMethod context);
public final CfFrameState popInitialized(AppView<?> appView, DexType expectedType) {
return popInitialized(appView, expectedType, FunctionUtils::getFirst);
}
public abstract CfFrameState popInitialized(
AppView<?> appView,
DexType expectedType,
BiFunction<CfFrameState, FrameType, 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, FrameType, CfFrameState> fn) {
return popInitialized(appView, valueType.toDexType(appView.dexItemFactory()), fn);
}
public final CfFrameState popObject(BiFunction<CfFrameState, FrameType, CfFrameState> fn) {
return pop((state, head) -> head.isObject() ? fn.apply(state, head) : error());
}
@SuppressWarnings("InconsistentOverloads")
public final CfFrameState popObject(
AppView<?> appView,
DexType expectedType,
ProgramMethod context,
BiFunction<CfFrameState, FrameType, CfFrameState> fn) {
return pop(
(state, head) ->
head.isObject()
&& CfAssignability.isAssignable(
head.getObjectType(context), expectedType, appView)
? fn.apply(state, head)
: error());
}
public final CfFrameState popSingle() {
return popSingle((state, single) -> state);
}
public final CfFrameState popSingle(BiFunction<CfFrameState, FrameType, CfFrameState> fn) {
return pop((state, single) -> single.isSingle() ? fn.apply(state, single) : error());
}
public final CfFrameState popSingles(
TriFunction<CfFrameState, FrameType, FrameType, CfFrameState> fn) {
return popSingle(
(state1, single1) ->
state1.popSingle((state2, single2) -> fn.apply(state2, single2, single1)));
}
public final CfFrameState popSingleOrWide(
BiFunction<CfFrameState, FrameType, CfFrameState> singleFn,
BiFunction<CfFrameState, FrameType, CfFrameState> wideFn) {
return pop(
(state, head) -> head.isSingle() ? singleFn.apply(state, head) : wideFn.apply(state, head));
}
public final CfFrameState popSingleSingleOrWide(
TriFunction<CfFrameState, FrameType, FrameType, CfFrameState> singleSingleFn,
BiFunction<CfFrameState, FrameType, CfFrameState> wideFn) {
return popSingleOrWide(
(state1, single1) ->
state1.popSingle((state2, single2) -> singleSingleFn.apply(state2, single2, single1)),
wideFn);
}
// TODO(b/214496607): Pushing a value should return an error if the stack grows larger than the
// max stack height.
public abstract CfFrameState push(DexType type);
// TODO(b/214496607): Pushing a value should return an error if the stack grows larger than the
// max stack height.
public abstract CfFrameState push(FrameType frameType);
public final CfFrameState push(FrameType frameType, FrameType frameType2) {
return push(frameType).push(frameType2);
}
public final CfFrameState push(FrameType frameType, FrameType frameType2, FrameType frameType3) {
return push(frameType).push(frameType2).push(frameType3);
}
public final CfFrameState push(
FrameType frameType, FrameType frameType2, FrameType frameType3, FrameType frameType4) {
return push(frameType).push(frameType2).push(frameType3).push(frameType4);
}
public final CfFrameState push(
FrameType frameType,
FrameType frameType2,
FrameType frameType3,
FrameType frameType4,
FrameType frameType5) {
return push(frameType).push(frameType2).push(frameType3).push(frameType4).push(frameType5);
}
public final CfFrameState push(
FrameType frameType,
FrameType frameType2,
FrameType frameType3,
FrameType frameType4,
FrameType frameType5,
FrameType frameType6) {
return push(frameType)
.push(frameType2)
.push(frameType3)
.push(frameType4)
.push(frameType5)
.push(frameType6);
}
public final CfFrameState push(AppView<?> appView, MemberType memberType) {
return push(FrameType.fromPreciseMemberType(memberType, appView.dexItemFactory()));
}
public final CfFrameState push(AppView<?> appView, NumericType numericType) {
return push(numericType.toDexType(appView.dexItemFactory()));
}
public final CfFrameState push(AppView<?> appView, ValueType valueType) {
return push(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);
public final CfFrameState storeLocal(
int localIndex, PrimitiveTypeElement primitiveType, AppView<?> appView) {
assert primitiveType.isInt()
|| primitiveType.isFloat()
|| primitiveType.isLong()
|| primitiveType.isDouble();
return storeLocal(
localIndex, FrameType.initialized(primitiveType.toDexType(appView.dexItemFactory())));
}
@Override
public final CfFrameState join(CfFrameState state) {
if (state.isBottom() || isError()) {
return this;
}
if (isBottom() || state.isError()) {
return state;
}
assert isConcrete();
assert state.isConcrete();
return asConcrete().join(state.asConcrete());
}
@Override
public abstract boolean equals(Object other);
@Override
public abstract int hashCode();
}