package com.oracle.graal.pointsto;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.graalvm.compiler.options.OptionValues;
import com.oracle.graal.pointsto.flow.AbstractSpecialInvokeTypeFlow;
import com.oracle.graal.pointsto.flow.AbstractVirtualInvokeTypeFlow;
import com.oracle.graal.pointsto.flow.ActualReturnTypeFlow;
import com.oracle.graal.pointsto.flow.MethodFlowsGraph;
import com.oracle.graal.pointsto.flow.MethodTypeFlow;
import com.oracle.graal.pointsto.flow.TypeFlow;
import com.oracle.graal.pointsto.flow.context.AnalysisContext;
import com.oracle.graal.pointsto.flow.context.BytecodeLocation;
import com.oracle.graal.pointsto.flow.context.free.DefaultAnalysisContextPolicy;
import com.oracle.graal.pointsto.flow.context.object.AnalysisObject;
import com.oracle.graal.pointsto.meta.AnalysisField;
import com.oracle.graal.pointsto.meta.AnalysisMethod;
import com.oracle.graal.pointsto.meta.AnalysisType;
import com.oracle.graal.pointsto.meta.AnalysisUniverse;
import com.oracle.graal.pointsto.typestate.TypeState;
import com.oracle.graal.pointsto.typestore.ArrayElementsTypeStore;
import com.oracle.graal.pointsto.typestore.FieldTypeStore;
import com.oracle.graal.pointsto.typestore.UnifiedArrayElementsTypeStore;
import com.oracle.graal.pointsto.typestore.UnifiedFieldTypeStore;
import jdk.vm.ci.code.BytecodePosition;
import jdk.vm.ci.meta.JavaConstant;
public class DefaultAnalysisPolicy extends AnalysisPolicy {
private DefaultAnalysisContextPolicy contextPolicy;
public DefaultAnalysisPolicy(OptionValues options) {
super(options);
this.contextPolicy = new DefaultAnalysisContextPolicy();
}
@Override
public DefaultAnalysisContextPolicy contextPolicy() {
return contextPolicy;
}
@Override
public boolean needsConstantCache() {
return false;
}
@Override
public boolean isSummaryObject(AnalysisObject object) {
return object.isContextInsensitiveObject();
}
@Override
public boolean isMergingEnabled() {
return false;
}
@Override
public void noteMerge(BigBang bb, TypeState t) {
}
@Override
public void noteMerge(BigBang bb, AnalysisObject... a) {
}
@Override
public void noteMerge(BigBang bb, AnalysisObject a) {
}
@Override
public boolean isContextSensitiveAllocation(BigBang bb, AnalysisType type, AnalysisContext allocationContext) {
return false;
}
@Override
public AnalysisObject createHeapObject(BigBang bb, AnalysisType type, BytecodeLocation allocationSite, AnalysisContext allocationContext) {
return type.getContextInsensitiveAnalysisObject();
}
@Override
public AnalysisObject createConstantObject(BigBang bb, JavaConstant constant, AnalysisType exactType) {
return exactType.getContextInsensitiveAnalysisObject();
}
@Override
public BytecodeLocation createAllocationSite(BigBang bb, int bci, AnalysisMethod method) {
return BytecodeLocation.create(bci, method);
}
@Override
public FieldTypeStore createFieldTypeStore(AnalysisObject object, AnalysisField field, AnalysisUniverse universe) {
return new UnifiedFieldTypeStore(field, object);
}
@Override
public ArrayElementsTypeStore createArrayElementsTypeStore(AnalysisObject object, AnalysisUniverse universe) {
if (object.type().isArray()) {
if (aliasArrayTypeFlows) {
if (object.type().getComponentType().isJavaLangObject()) {
return new UnifiedArrayElementsTypeStore(object);
}
return universe.objectType().getArrayClass().getContextInsensitiveAnalysisObject().getArrayElementsTypeStore();
}
return new UnifiedArrayElementsTypeStore(object);
} else {
return null;
}
}
@Override
public AbstractVirtualInvokeTypeFlow createVirtualInvokeTypeFlow(BytecodePosition invokeLocation, AnalysisType receiverType, AnalysisMethod targetMethod,
TypeFlow<?>[] actualParameters, ActualReturnTypeFlow actualReturn, BytecodeLocation location) {
return new DefaultVirtualInvokeTypeFlow(invokeLocation, receiverType, targetMethod, actualParameters, actualReturn, location);
}
@Override
public AbstractSpecialInvokeTypeFlow createSpecialInvokeTypeFlow(BytecodePosition invokeLocation, AnalysisType receiverType, AnalysisMethod targetMethod,
TypeFlow<?>[] actualParameters, ActualReturnTypeFlow actualReturn, BytecodeLocation location) {
return new DefaultSpecialInvokeTypeFlow(invokeLocation, receiverType, targetMethod, actualParameters, actualReturn, location);
}
private static class DefaultVirtualInvokeTypeFlow extends AbstractVirtualInvokeTypeFlow {
private TypeState seenReceiverTypes = TypeState.forEmpty();
protected DefaultVirtualInvokeTypeFlow(BytecodePosition invokeLocation, AnalysisType receiverType, AnalysisMethod targetMethod,
TypeFlow<?>[] actualParameters, ActualReturnTypeFlow actualReturn, BytecodeLocation location) {
super(invokeLocation, receiverType, targetMethod, actualParameters, actualReturn, location);
}
protected DefaultVirtualInvokeTypeFlow(BigBang bb, MethodFlowsGraph methodFlows, DefaultVirtualInvokeTypeFlow original) {
super(bb, methodFlows, original);
}
@Override
public TypeFlow<BytecodePosition> copy(BigBang bb, MethodFlowsGraph methodFlows) {
return new DefaultVirtualInvokeTypeFlow(bb, methodFlows, this);
}
@Override
public void onObservedUpdate(BigBang bb) {
assert this.isClone() || this.isContextInsensitive();
if (isSaturated()) {
return;
}
TypeState receiverState = getReceiver().getState();
if (receiverState.isUnknown()) {
bb.reportIllegalUnknownUse(graphRef.getMethod(), source, "Illegal: Invoke on UnknownTypeState objects. Invoke: " + this);
return;
}
if (!isContextInsensitive()) {
receiverState = filterReceiverState(bb, receiverState);
}
for (AnalysisType type : receiverState.types()) {
if (isSaturated()) {
return;
}
if (seenReceiverTypes.containsType(type)) {
continue;
}
AnalysisMethod method = type.resolveConcreteMethod(getTargetMethod());
if (method == null || Modifier.isAbstract(method.getModifiers())) {
continue;
}
assert !Modifier.isAbstract(method.getModifiers());
MethodTypeFlow callee = method.getTypeFlow();
MethodFlowsGraph calleeFlows = callee.addContext(bb, bb.contextPolicy().emptyContext(), this);
assert callee.getContexts()[0] == bb.contextPolicy().emptyContext();
if (addCallee(callee.getMethod())) {
linkCallee(bb, false, calleeFlows);
}
updateReceiver(bb, calleeFlows, TypeState.forExactType(bb, type, false));
}
seenReceiverTypes = receiverState;
}
@Override
public void onObservedSaturated(BigBang bb, TypeFlow<?> observed) {
assert this.isClone() && !this.isContextInsensitive();
setSaturated();
getReceiver().removeObserver(this);
for (AnalysisMethod callee : super.getCallees()) {
MethodFlowsGraph calleeFlows = callee.getTypeFlow().getFlows(bb.contextPolicy().emptyContext());
for (int i = 0; i < actualParameters.length; i++) {
TypeFlow<?> formalParam = calleeFlows.getParameter(i);
if (actualParameters[i] != null && formalParam != null) {
actualParameters[i].removeUse(formalParam);
}
}
if (actualReturn != null && calleeFlows.getResult() != null) {
calleeFlows.getResult().removeUse(actualReturn);
}
}
AbstractVirtualInvokeTypeFlow contextInsensitiveInvoke = (AbstractVirtualInvokeTypeFlow) targetMethod.initAndGetContextInsensitiveInvoke(bb, source);
contextInsensitiveInvoke.addInvokeLocation(getSource());
for (int i = 1; i < actualParameters.length; i++) {
if (actualParameters[i] != null) {
actualParameters[i].addUse(bb, contextInsensitiveInvoke.getActualParameter(i));
}
}
if (actualReturn != null) {
contextInsensitiveInvoke.getActualReturn().addUse(bb, actualReturn);
}
}
@Override
public void setSaturated() {
super.setSaturated();
if (this.isClone()) {
originalInvoke.setSaturated();
}
}
@Override
public final Collection<AnalysisMethod> getCallees() {
if (isSaturated()) {
return targetMethod.getContextInsensitiveInvoke().getCallees();
} else {
return super.getCallees();
}
}
@Override
public Collection<MethodFlowsGraph> getCalleesFlows(BigBang bb) {
Collection<AnalysisMethod> calleesList = getCallees();
List<MethodFlowsGraph> methodFlowsGraphs = new ArrayList<>(calleesList.size());
for (AnalysisMethod method : calleesList) {
methodFlowsGraphs.add(method.getTypeFlow().getFlows(bb.contextPolicy().emptyContext()));
}
return methodFlowsGraphs;
}
}
private static final class DefaultSpecialInvokeTypeFlow extends AbstractSpecialInvokeTypeFlow {
MethodFlowsGraph calleeFlows = null;
DefaultSpecialInvokeTypeFlow(BytecodePosition invokeLocation, AnalysisType receiverType, AnalysisMethod targetMethod,
TypeFlow<?>[] actualParameters, ActualReturnTypeFlow actualReturn, BytecodeLocation location) {
super(invokeLocation, receiverType, targetMethod, actualParameters, actualReturn, location);
}
private DefaultSpecialInvokeTypeFlow(BigBang bb, MethodFlowsGraph methodFlows, DefaultSpecialInvokeTypeFlow original) {
super(bb, methodFlows, original);
}
@Override
public TypeFlow<BytecodePosition> copy(BigBang bb, MethodFlowsGraph methodFlows) {
return new DefaultSpecialInvokeTypeFlow(bb, methodFlows, this);
}
@Override
public void onObservedUpdate(BigBang bb) {
assert this.isClone();
initCallee();
if (calleeFlows == null) {
calleeFlows = callee.addContext(bb, bb.contextPolicy().emptyContext(), this);
linkCallee(bb, false, calleeFlows);
}
TypeState invokeState = filterReceiverState(bb, getReceiver().getState());
updateReceiver(bb, calleeFlows, invokeState);
}
@Override
public Collection<MethodFlowsGraph> getCalleesFlows(BigBang bb) {
if (callee == null) {
return Collections.emptyList();
} else {
MethodFlowsGraph methodFlows = callee.getFlows(bb.contextPolicy().emptyContext());
return Collections.singletonList(methodFlows);
}
}
}
}