CommonCallStatement.java
package dev.civl.mc.model.common.statement;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import dev.civl.mc.model.IF.CIVLFunction;
import dev.civl.mc.model.IF.CIVLSource;
import dev.civl.mc.model.IF.Scope;
import dev.civl.mc.model.IF.expression.ConditionalExpression;
import dev.civl.mc.model.IF.expression.Expression;
import dev.civl.mc.model.IF.expression.Expression.ExpressionKind;
import dev.civl.mc.model.IF.expression.FunctionIdentifierExpression;
import dev.civl.mc.model.IF.expression.LHSExpression;
import dev.civl.mc.model.IF.expression.VariableExpression;
import dev.civl.mc.model.IF.location.Location;
import dev.civl.mc.model.IF.statement.CallOrSpawnStatement;
import dev.civl.mc.model.IF.variable.Variable;
import dev.civl.mc.model.common.StaticAnalysisConfiguration;
import dev.civl.sarl.IF.SymbolicUniverse;
/**
* A function call or spawn. Either of the form f(x) or else v=f(x), i.e., the
* left-hand side expression is optional.
*
* @author Timothy K. Zirkel (zirkel)
*
*/
public class CommonCallStatement extends CommonStatement
implements
CallOrSpawnStatement {
private static final BitSet EMPTY_BITSET = new BitSet();
private boolean isCall;
private boolean isRun;
/**
* Set to true iff the {@link #lhs()} is initialized by the return value of
* this call statement
*/
private boolean isInitializer;
private LHSExpression lhs = null;
private Expression functionExpression;
private List<Expression> arguments;
/**
* A function call. Either of the form f(x) or else v=f(x).
*
* @param source
* The source location for this call statement.
* @param isCall
* is this a call statement (not spawn)?
* @param function
* The function.
* @param arguments
* The arguments to the function.
*/
public CommonCallStatement(CIVLSource civlSource, Scope hscope,
Scope lscope, Location source, Expression guard, boolean isCall,
LHSExpression lhs, Expression functionExpression,
List<Expression> arguments, boolean isInitializer) {
super(civlSource, hscope, lscope, source, guard);
this.isCall = isCall;
this.lhs = lhs;
this.functionExpression = functionExpression;
this.arguments = arguments;
this.isInitializer = isInitializer;
}
/**
* @return The left hand side expression if applicable. Else null.
*/
@Override
public LHSExpression lhs() {
return lhs;
}
/**
* @return The function being called.
*/
@Override
public CIVLFunction function() {
if (this.functionExpression
.expressionKind() == ExpressionKind.FUNCTION_IDENTIFIER)
return ((FunctionIdentifierExpression) functionExpression)
.function();
return null;
}
@Override
public Expression functionExpression() {
return this.functionExpression;
}
/**
* @return The arguments to the function.
*/
@Override
public List<Expression> arguments() {
return arguments;
}
/**
* @param lhs
* The left hand side expression if applicable. Else null.
*/
@Override
public void setLhs(LHSExpression lhs) {
this.lhs = lhs;
}
/**
* @param arguments
* The arguments to the function.
*/
@Override
public void setArguments(List<Expression> arguments) {
this.arguments = arguments;
}
@Override
public String toString() {
String result = this.functionExpression.toString();
boolean first = true;
result += "(";
for (Expression arg : arguments) {
if (first)
first = false;
else
result += ", ";
result += arg.toString();
}
result += ")";
if (!isCall)
result = "$spawn " + result;
if (lhs != null) {
result = lhs + " = " + result;
}
return result;
}
@Override
public boolean isCall() {
return isCall;
}
@Override
public boolean isSpawn() {
return !isCall;
}
@Override
public void calculateDerefs() {
this.hasDerefs = false;
if (this.lhs != null) {
lhs.calculateDerefs();
this.hasDerefs = this.hasDerefs || lhs.hasDerefs();
}
// if (this.arguments != null) {
// for (Expression arg : this.arguments) {
// arg.calculateDerefs();
// this.hasDerefs = this.hasDerefs || arg.hasDerefs();
// // early return
// if (this.hasDerefs)
// return;
// }
// }
}
@Override
public void purelyLocalAnalysisOfVariables(Scope funcScope) {
super.purelyLocalAnalysisOfVariables(funcScope);
if (this.lhs != null)
this.lhs.purelyLocalAnalysisOfVariables(funcScope);
for (Expression arg : this.arguments) {
arg.purelyLocalAnalysisOfVariables(funcScope);
}
}
@Override
public void purelyLocalAnalysis() {
// if (this.isSystemCall()) {
// this.purelyLocal = false;
// return;
// }
this.guard().purelyLocalAnalysis();
if (!this.guard().isPurelyLocal()) {
this.purelyLocal = false;
return;
}
if (this.lhs != null) {
this.lhs.purelyLocalAnalysis();
if (!this.lhs.isPurelyLocal()) {
this.purelyLocal = false;
return;
}
}
for (Expression arg : this.arguments) {
// if (arg.getExpressionType().isHandleType()) {
// this.purelyLocal = false;
// return;
// }
arg.purelyLocalAnalysis();
if (!arg.isPurelyLocal()) {
this.purelyLocal = false;
return;
}
}
CIVLFunction function = this.function();
if (function != null) {
if (function.isSystemFunction() || function.isAtomicFunction()) {
if (!function.isPurelyLocal())
this.purelyLocal = false;
}
}
}
@Override
public void replaceWith(ConditionalExpression oldExpression,
VariableExpression newExpression) {
int number = arguments.size();
super.replaceWith(oldExpression, newExpression);
for (int i = 0; i < number; i++) {
Expression arg = arguments.get(i);
if (arg == oldExpression) {
arguments.set(i, newExpression);
return;
}
arg.replaceWith(oldExpression, newExpression);
}
}
@Override
public CallOrSpawnStatement replaceWith(ConditionalExpression oldExpression,
Expression newExpression) {
Expression newGuard = guardReplaceWith(oldExpression, newExpression);
CommonCallStatement newStatement = null;
if (newGuard != null) {
newStatement = new CommonCallStatement(this.getSource(),
this.statementScope, this.lowestScope, this.source(),
newGuard, this.isCall, lhs, this.functionExpression,
this.arguments, this.isInitializer);
} else {
boolean hasNewArg = false;
ArrayList<Expression> newArgs = new ArrayList<Expression>();
int number = this.arguments.size();
for (int i = 0; i < number; i++) {
if (hasNewArg)
newArgs.add(arguments.get(i));
else {
Expression newArg = arguments.get(i);
newArg = newArg.replaceWith(oldExpression, newExpression);
if (newArg != null) {
newArgs.add(newArg);
hasNewArg = true;
} else
newArgs.add(arguments.get(i));
}
}
if (hasNewArg) {
newStatement = new CommonCallStatement(this.getSource(),
this.statementScope, this.lowestScope, this.source(),
this.guard(), this.isCall, lhs, this.functionExpression,
newArgs, this.isInitializer);
}
}
return newStatement;
}
@Override
public Set<Variable> variableAddressedOf(Scope scope) {
Set<Variable> result = new HashSet<>();
if (lhs != null) {
Variable lhsVariable = lhs.variableWritten(scope);
if (lhsVariable != null)
result.add(lhsVariable);
}
if (arguments != null) {
Set<Variable> argumentResult;
int n = this.arguments.size();
CIVLFunction function = this.function();
BitSet ignored = EMPTY_BITSET;
if (function != null)
ignored = StaticAnalysisConfiguration
.getIgnoredArgumenets(function);
for (int i = 0; i < n; i++) {
if (ignored.get(i))
continue;
Expression argument = arguments.get(i);
argumentResult = argument.variableAddressedOf(scope);
if (argumentResult != null)
result.addAll(argumentResult);
}
}
return result;
}
@Override
public Set<Variable> variableAddressedOf() {
Set<Variable> result = new HashSet<>();
if (arguments != null) {
Set<Variable> argumentResult;
int n = this.arguments.size();
CIVLFunction function = this.function();
BitSet ignored = EMPTY_BITSET;
if (function != null)
ignored = StaticAnalysisConfiguration
.getIgnoredArgumenets(function);
for (int i = 0; i < n; i++) {
if (ignored.get(i))
continue;
Expression argument = arguments.get(i);
argumentResult = argument.variableAddressedOf();
if (argumentResult != null)
result.addAll(argumentResult);
}
}
return result;
}
@Override
public StatementKind statementKind() {
return StatementKind.CALL_OR_SPAWN;
}
@Override
public boolean isSystemCall() {
CIVLFunction function = this.function();
if (this.isCall && function != null && function.isSystemFunction()) {
return true;
}
return false;
}
@Override
public String locationStepString() {
String result = (source() == null ? "??" : source().id()) + "->";
CIVLFunction function = this.function();
if (this.isCall && function != null && function.isNormalFunction()) {
result += Integer.toString(function.startLocation().id());
return result;
} else
return super.locationStepString();
}
@Override
public void setFunction(FunctionIdentifierExpression function) {
this.functionExpression = function;
}
@Override
protected void calculateConstantValueWork(SymbolicUniverse universe) {
for (Expression arg : arguments)
arg.calculateConstantValue(universe);
}
@Override
protected boolean containsHereWork() {
for (Expression arg : arguments) {
if (arg.containsHere())
return true;
}
return false;
}
@Override
public boolean isRun() {
assert !isCall;
return isRun;
}
@Override
public void setAsRun(boolean isRun) {
assert !isCall;
this.isRun = isRun;
}
@Override
public boolean equals(Object obj) {
if (super.equals(obj)) {
if (obj instanceof CommonCallStatement) {
CommonCallStatement other = (CommonCallStatement) obj;
if (other.functionExpression.equals(functionExpression))
if (other.arguments.equals(arguments))
if (other.lhs == lhs)
return other.isCall == isCall;
}
}
return false;
}
@Override
public boolean isInitializer() {
return this.isInitializer;
}
@Override
public Set<Variable> freeVariables() {
Set<Variable> result = super.freeVariables();
result.addAll(functionExpression.freeVariables());
if (lhs != null)
result.addAll(lhs.freeVariables());
for (Expression arg : arguments) {
result.addAll(arg.freeVariables());
}
return result;
}
}