sync.pds.solver.SyncPDSSolver Maven / Gradle / Ivy
/**
* ***************************************************************************** Copyright (c) 2018
* Fraunhofer IEM, Paderborn, Germany. This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
*
Contributors: Johannes Spaeth - initial API and implementation
* *****************************************************************************
*/
package sync.pds.solver;
import com.google.common.base.Objects;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import java.util.AbstractMap;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.slf4j.LoggerFactory;
import sync.pds.solver.nodes.ExclusionNode;
import sync.pds.solver.nodes.GeneratedState;
import sync.pds.solver.nodes.INode;
import sync.pds.solver.nodes.Node;
import sync.pds.solver.nodes.NodeWithLocation;
import sync.pds.solver.nodes.PopNode;
import sync.pds.solver.nodes.PushNode;
import sync.pds.solver.nodes.SingleNode;
import wpds.impl.NestedAutomatonListener;
import wpds.impl.NestedWeightedPAutomatons;
import wpds.impl.NormalRule;
import wpds.impl.PopRule;
import wpds.impl.PushRule;
import wpds.impl.Rule;
import wpds.impl.Transition;
import wpds.impl.Weight;
import wpds.impl.WeightedPAutomaton;
import wpds.impl.WeightedPushdownSystem;
import wpds.interfaces.Location;
import wpds.interfaces.State;
import wpds.interfaces.WPAStateListener;
import wpds.interfaces.WPAUpdateListener;
public abstract class SyncPDSSolver<
Stmt extends Location, Fact, Field extends Location, W extends Weight> {
public enum PDSSystem {
FIELDS,
CALLS
}
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(SyncPDSSolver.class);
private static final boolean FieldSensitive = true;
private static final boolean ContextSensitive = true;
protected final WeightedPushdownSystem, W> callingPDS =
new WeightedPushdownSystem, W>() {
public String toString() {
return "Call " + SyncPDSSolver.this.toString();
}
;
};
protected final WeightedPushdownSystem>, W> fieldPDS =
new WeightedPushdownSystem>, W>() {
public String toString() {
return "Field " + SyncPDSSolver.this.toString();
}
;
};
private final Set> reachedStates = Sets.newHashSet();
private final Set> callingContextReachable = Sets.newHashSet();
private final Set> fieldContextReachable = Sets.newHashSet();
private final Set> updateListeners = Sets.newHashSet();
private final Multimap, SyncStatePDSUpdateListener>
reachedStateUpdateListeners = HashMultimap.create();
protected final WeightedPAutomaton>, W> fieldAutomaton;
protected final WeightedPAutomaton, W> callAutomaton;
protected boolean preventFieldTransitionAdd(
Transition>> trans, W weight) {
return false;
}
protected boolean preventCallTransitionAdd(Transition> trans, W weight) {
return false;
}
public SyncPDSSolver(
final boolean useCallSummaries,
NestedWeightedPAutomatons, W> callSummaries,
final boolean useFieldSummaries,
NestedWeightedPAutomatons>, W> fieldSummaries,
int maxCallDepth,
int maxFieldDepth,
int maxUnbalancedCallDepth) {
fieldAutomaton =
new WeightedPAutomaton>, W>() {
@Override
public INode> createState(INode> d, Field loc) {
if (loc.equals(emptyField())) return d;
return generateFieldState(d, loc);
}
@Override
public Field epsilon() {
return epsilonField();
}
@Override
public boolean nested() {
return useFieldSummaries;
}
;
@Override
public W getOne() {
return getFieldWeights().getOne();
}
@Override
public int getMaxDepth() {
return maxFieldDepth;
}
public boolean addWeightForTransition(
Transition>> trans, W weight) {
if (preventFieldTransitionAdd(trans, weight)) return false;
logger.trace("Adding field transition {} with weight {}", trans, weight);
return super.addWeightForTransition(trans, weight);
}
;
@Override
public boolean isGeneratedState(INode> d) {
return d instanceof GeneratedState;
}
};
callAutomaton =
new WeightedPAutomaton, W>() {
@Override
public INode createState(INode d, Stmt loc) {
return generateCallState(d, loc);
}
@Override
public Stmt epsilon() {
return epsilonStmt();
}
@Override
public boolean nested() {
return useCallSummaries;
}
;
@Override
public W getOne() {
return getCallWeights().getOne();
}
public boolean addWeightForTransition(Transition> trans, W weight) {
if (preventCallTransitionAdd(trans, weight)) return false;
logger.trace("Adding call transition {} with weight {}", trans, weight);
return super.addWeightForTransition(trans, weight);
}
;
@Override
public boolean isGeneratedState(INode d) {
return d instanceof GeneratedState;
}
@Override
public int getMaxDepth() {
return maxCallDepth;
}
@Override
public int getMaxUnbalancedDepth() {
return maxUnbalancedCallDepth;
}
};
callAutomaton.registerListener(new CallAutomatonListener());
fieldAutomaton.registerListener(new FieldUpdateListener());
if (callAutomaton.nested())
callAutomaton.registerNestedAutomatonListener(new CallSummaryListener());
// if(fieldAutomaton.nested())
// fieldAutomaton.registerNestedAutomatonListener(new FieldSummaryListener());
callingPDS.poststar(callAutomaton, callSummaries);
fieldPDS.poststar(fieldAutomaton, fieldSummaries);
}
private class FieldSummaryListener
implements NestedAutomatonListener>, W> {
@Override
public void nestedAutomaton(
final WeightedPAutomaton>, W> parent,
final WeightedPAutomaton>, W> child) {
for (INode> s : child.getInitialStates()) {
child.registerListener(new FieldAddEpsilonToInitialStateListener(s, parent));
}
}
}
private class FieldAddEpsilonToInitialStateListener
extends WPAStateListener>, W> {
private WeightedPAutomaton>, W> parent;
public FieldAddEpsilonToInitialStateListener(
INode> state,
WeightedPAutomaton>, W> parent) {
super(state);
this.parent = parent;
}
@Override
public void onOutTransitionAdded(
Transition>> t,
W w,
WeightedPAutomaton>, W> weightedPAutomaton) {}
@Override
public void onInTransitionAdded(
final Transition>> nestedT,
W w,
WeightedPAutomaton>, W> weightedPAutomaton) {
if (nestedT.getLabel().equals(fieldAutomaton.epsilon())) {
parent.registerListener(
new FieldOnOutTransitionAddToStateListener(this.getState(), nestedT));
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + getOuterType().hashCode();
result = prime * result + ((parent == null) ? 0 : parent.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!super.equals(obj)) return false;
if (getClass() != obj.getClass()) return false;
FieldAddEpsilonToInitialStateListener other = (FieldAddEpsilonToInitialStateListener) obj;
if (!getOuterType().equals(other.getOuterType())) return false;
if (parent == null) {
if (other.parent != null) return false;
} else if (!parent.equals(other.parent)) return false;
return true;
}
private SyncPDSSolver getOuterType() {
return SyncPDSSolver.this;
}
}
private class FieldOnOutTransitionAddToStateListener
extends WPAStateListener>, W> {
private Transition>> nestedT;
public FieldOnOutTransitionAddToStateListener(
INode> state, Transition>> nestedT) {
super(state);
this.nestedT = nestedT;
}
@Override
public void onOutTransitionAdded(
Transition>> t,
W w,
WeightedPAutomaton>, W> weightedPAutomaton) {
setFieldContextReachable(nestedT.getStart().fact());
}
@Override
public void onInTransitionAdded(
Transition>> t,
W w,
WeightedPAutomaton>, W> weightedPAutomaton) {}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + getOuterType().hashCode();
result = prime * result + ((nestedT == null) ? 0 : nestedT.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!super.equals(obj)) return false;
if (getClass() != obj.getClass()) return false;
FieldOnOutTransitionAddToStateListener other = (FieldOnOutTransitionAddToStateListener) obj;
if (!getOuterType().equals(other.getOuterType())) return false;
if (nestedT == null) {
if (other.nestedT != null) return false;
} else if (!nestedT.equals(other.nestedT)) return false;
return true;
}
private SyncPDSSolver getOuterType() {
return SyncPDSSolver.this;
}
}
private class CallSummaryListener implements NestedAutomatonListener, W> {
@Override
public void nestedAutomaton(
final WeightedPAutomaton, W> parent,
final WeightedPAutomaton, W> child) {
for (INode s : child.getInitialStates()) {
child.registerListener(new AddEpsilonToInitialStateListener(s, parent));
}
}
}
private class AddEpsilonToInitialStateListener extends WPAStateListener, W> {
private WeightedPAutomaton, W> parent;
public AddEpsilonToInitialStateListener(
INode state, WeightedPAutomaton, W> parent) {
super(state);
this.parent = parent;
}
@Override
public void onOutTransitionAdded(
Transition> t,
W w,
WeightedPAutomaton, W> weightedPAutomaton) {}
@Override
public void onInTransitionAdded(
final Transition> nestedT,
W w,
WeightedPAutomaton, W> weightedPAutomaton) {
if (nestedT.getLabel().equals(callAutomaton.epsilon())) {
parent.registerListener(new OnOutTransitionAddToStateListener(this.getState(), nestedT));
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + getOuterType().hashCode();
result = prime * result + ((parent == null) ? 0 : parent.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!super.equals(obj)) return false;
if (getClass() != obj.getClass()) return false;
AddEpsilonToInitialStateListener other = (AddEpsilonToInitialStateListener) obj;
if (!getOuterType().equals(other.getOuterType())) return false;
if (parent == null) {
if (other.parent != null) return false;
} else if (!parent.equals(other.parent)) return false;
return true;
}
private SyncPDSSolver getOuterType() {
return SyncPDSSolver.this;
}
}
private class OnOutTransitionAddToStateListener extends WPAStateListener, W> {
private Transition> nestedT;
public OnOutTransitionAddToStateListener(
INode state, Transition> nestedT) {
super(state);
this.nestedT = nestedT;
}
@Override
public void onOutTransitionAdded(
Transition> t,
W w,
WeightedPAutomaton, W> weightedPAutomaton) {
Node returningNode =
new Node(t.getLabel(), nestedT.getStart().fact());
setCallingContextReachable(returningNode);
}
@Override
public void onInTransitionAdded(
Transition> t,
W w,
WeightedPAutomaton, W> weightedPAutomaton) {}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + getOuterType().hashCode();
result = prime * result + ((nestedT == null) ? 0 : nestedT.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!super.equals(obj)) return false;
if (getClass() != obj.getClass()) return false;
OnOutTransitionAddToStateListener other = (OnOutTransitionAddToStateListener) obj;
if (!getOuterType().equals(other.getOuterType())) return false;
if (nestedT == null) {
if (other.nestedT != null) return false;
} else if (!nestedT.equals(other.nestedT)) return false;
return true;
}
private SyncPDSSolver getOuterType() {
return SyncPDSSolver.this;
}
}
private class CallAutomatonListener implements WPAUpdateListener, W> {
@Override
public void onWeightAdded(
Transition> t, W w, WeightedPAutomaton, W> aut) {
if (!(t.getStart() instanceof GeneratedState)
&& !t.getLabel().equals(callAutomaton.epsilon())) {
Node node = new Node(t.getLabel(), t.getStart().fact());
setCallingContextReachable(node);
}
}
}
public void solve(
Node curr,
Field field,
INode> fieldTarget,
Stmt stmt,
INode callTarget,
W weight) {
fieldAutomaton.addInitialState(fieldTarget);
callAutomaton.addInitialState(callTarget);
INode> start = asFieldFact(curr);
if (!field.equals(emptyField())) {
INode> generateFieldState = generateFieldState(start, field);
Transition>> fieldTrans =
new Transition<>(start, field, generateFieldState);
fieldAutomaton.addTransition(fieldTrans);
Transition>> fieldTransToInitial =
new Transition<>(generateFieldState, emptyField(), fieldTarget);
fieldAutomaton.addTransition(fieldTransToInitial);
} else {
Transition>> fieldTrans =
new Transition<>(start, emptyField(), fieldTarget);
fieldAutomaton.addTransition(fieldTrans);
}
Transition> callTrans =
new Transition<>(wrap(curr.fact()), curr.stmt(), callTarget);
callAutomaton.addWeightForTransition(callTrans, weight);
processNode(curr);
}
public void solve(
Node curr,
Field field,
INode> fieldTarget,
Stmt stmt,
INode callTarget) {
solve(curr, field, fieldTarget, stmt, callTarget, getCallWeights().getOne());
}
public void processNode(Node curr) {
if (!addReachableState(curr)) return;
computeSuccessor(curr);
}
public void propagate(Node curr, State s) {
if (s instanceof Node) {
Node succ = (Node) s;
if (succ instanceof PushNode) {
PushNode pushNode = (PushNode) succ;
PDSSystem system = pushNode.system();
Location location = pushNode.location();
processPush(curr, location, pushNode, system);
} else {
processNormal(curr, succ);
}
} else if (s instanceof PopNode) {
PopNode popNode = (PopNode) s;
processPop(curr, popNode);
}
}
private boolean addReachableState(Node curr) {
if (reachedStates.contains(curr)) return false;
reachedStates.add(curr);
for (SyncPDSUpdateListener l : Lists.newLinkedList(updateListeners)) {
l.onReachableNodeAdded(curr);
}
for (SyncStatePDSUpdateListener l :
Lists.newLinkedList(reachedStateUpdateListeners.get(curr))) {
l.reachable();
}
return true;
}
public void processNormal(Node curr, Node succ) {
addNormalFieldFlow(curr, succ);
addNormalCallFlow(curr, succ);
}
public void addNormalCallFlow(Node curr, Node succ) {
addCallRule(
new NormalRule<>(
wrap(curr.fact()),
curr.stmt(),
wrap(succ.fact()),
succ.stmt(),
getCallWeights().normal(curr, succ)));
}
public void addNormalFieldFlow(final Node curr, final Node succ) {
if (succ instanceof ExclusionNode) {
ExclusionNode exNode = (ExclusionNode) succ;
addFieldRule(
new NormalRule<>(
asFieldFact(curr),
fieldWildCard(),
asFieldFact(succ),
exclusionFieldWildCard(exNode.exclusion()),
getFieldWeights().normal(curr, succ)));
return;
}
addFieldRule(
new NormalRule<>(
asFieldFact(curr),
fieldWildCard(),
asFieldFact(succ),
fieldWildCard(),
getFieldWeights().normal(curr, succ)));
}
public INode> asFieldFact(Node node) {
return new SingleNode<>(new Node<>(node.stmt(), node.fact()));
}
public void processPop(Node curr, PopNode popNode) {
PDSSystem system = popNode.system();
Object location = popNode.location();
if (system.equals(PDSSystem.FIELDS)) {
NodeWithLocation node = (NodeWithLocation) location;
if (FieldSensitive) {
addFieldRule(
new PopRule<>(
asFieldFact(curr),
node.location(),
asFieldFact(node.fact()),
getFieldWeights().pop(curr)));
} else {
addNormalFieldFlow(curr, node.fact());
}
addNormalCallFlow(curr, node.fact());
} else if (system.equals(PDSSystem.CALLS)) {
if (ContextSensitive) {
addCallRule(
new PopRule<>(
wrap(curr.fact()), curr.stmt(), wrap((Fact) location), getCallWeights().pop(curr)));
}
}
}
private void applyCallSummary(Stmt callSite, Fact factInCallee, Stmt spInCallee) {
callAutomaton.addSummaryListener(
t -> {
GeneratedState genSt = ((GeneratedState) t.getTarget());
Stmt sp = genSt.location();
Fact v = genSt.node().fact();
Stmt exitStmt = t.getLabel();
Fact returnedFact = t.getStart().fact();
if (spInCallee.equals(sp) && factInCallee.equals(v)) {
if (summaries.add(
new Summary(callSite, factInCallee, spInCallee, exitStmt, returnedFact))) {
for (OnAddedSummaryListener s : Lists.newArrayList(summaryListeners)) {
s.apply(callSite, factInCallee, spInCallee, exitStmt, returnedFact);
}
}
// TODO can be removed and
applyCallSummary(callSite, factInCallee, spInCallee, exitStmt, returnedFact);
}
});
}
Set summaries = Sets.newHashSet();
Set summaryListeners = Sets.newHashSet();
public void addApplySummaryListener(OnAddedSummaryListener l) {
if (summaryListeners.add(l)) {
for (Summary s : Lists.newArrayList(summaries)) {
l.apply(s.callSite, s.factInCallee, s.spInCallee, s.exitStmt, s.returnedFact);
}
}
}
public interface OnAddedSummaryListener {
void apply(Stmt callSite, Fact factInCallee, Stmt spInCallee, Stmt exitStmt, Fact returnedFact);
}
private class Summary {
private final Stmt callSite;
private final Fact factInCallee;
private final Stmt spInCallee;
private final Stmt exitStmt;
private final Fact returnedFact;
private Summary(
Stmt callSite, Fact factInCallee, Stmt spInCallee, Stmt exitStmt, Fact returnedFact) {
this.callSite = callSite;
this.factInCallee = factInCallee;
this.spInCallee = spInCallee;
this.exitStmt = exitStmt;
this.returnedFact = returnedFact;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Summary summary = (Summary) o;
return Objects.equal(callSite, summary.callSite)
&& Objects.equal(factInCallee, summary.factInCallee)
&& Objects.equal(spInCallee, summary.spInCallee)
&& Objects.equal(exitStmt, summary.exitStmt)
&& Objects.equal(returnedFact, summary.returnedFact);
}
@Override
public int hashCode() {
return Objects.hashCode(callSite, factInCallee, spInCallee, exitStmt, returnedFact);
}
}
public abstract void applyCallSummary(
Stmt callSite, Fact factInCallee, Stmt spInCallee, Stmt exitStmt, Fact returnedFact);
public void processPush(
Node curr, Location location, PushNode succ, PDSSystem system) {
if (system.equals(PDSSystem.FIELDS)) {
if (FieldSensitive) {
addFieldRule(
new PushRule<>(
asFieldFact(curr),
fieldWildCard(),
asFieldFact(succ),
(Field) location,
fieldWildCard(),
getFieldWeights().push(curr, succ, (Field) location)));
} else {
addNormalFieldFlow(curr, succ);
}
addNormalCallFlow(curr, succ);
} else if (system.equals(PDSSystem.CALLS)) {
addNormalFieldFlow(curr, succ);
if (ContextSensitive) {
addCallRule(
new PushRule<>(
wrap(curr.fact()),
curr.stmt(),
wrap(succ.fact()),
succ.stmt(),
(Stmt) location,
getCallWeights().push(curr, succ, (Stmt) location)));
} else {
addNormalCallFlow(curr, succ);
}
applyCallSummary((Stmt) location, succ.fact(), succ.stmt());
}
}
public void addCallRule(Rule, W> rule) {
callingPDS.addRule(rule);
}
public void addFieldRule(Rule>, W> rule) {
fieldPDS.addRule(rule);
}
public abstract WeightFunctions getFieldWeights();
public abstract WeightFunctions getCallWeights();
private class FieldUpdateListener
implements WPAUpdateListener>, W> {
@Override
public void onWeightAdded(
Transition>> t,
W w,
WeightedPAutomaton>, W> aut) {
INode> n = t.getStart();
if (!(n instanceof GeneratedState) && !t.getLabel().equals(fieldAutomaton.epsilon())) {
Node fact = n.fact();
Node node = new Node(fact.stmt(), fact.fact());
setFieldContextReachable(node);
}
}
}
private void setCallingContextReachable(Node node) {
if (!callingContextReachable.add(node)) return;
if (fieldContextReachable.contains(node)) {
processNode(node);
}
}
private void setFieldContextReachable(Node node) {
if (!fieldContextReachable.add(node)) {
return;
}
if (callingContextReachable.contains(node)) {
processNode(node);
}
}
public void registerListener(SyncPDSUpdateListener listener) {
if (!updateListeners.add(listener)) {
return;
}
for (Node reachableNode : Lists.newArrayList(reachedStates)) {
listener.onReachableNodeAdded(reachableNode);
}
}
public void registerListener(SyncStatePDSUpdateListener listener) {
if (!reachedStateUpdateListeners.put(listener.getNode(), listener)) {
return;
}
if (reachedStates.contains(listener.getNode())) {
listener.reachable();
}
}
protected INode wrap(Fact variable) {
return new SingleNode(variable);
}
protected Map, Stmt>, INode> generatedCallState = Maps.newHashMap();
public INode generateCallState(final INode d, final Stmt loc) {
Entry, Stmt> e = new AbstractMap.SimpleEntry<>(d, loc);
if (!generatedCallState.containsKey(e)) {
generatedCallState.put(e, new GeneratedState(d, loc));
}
return generatedCallState.get(e);
}
Map>, Field>, INode>> generatedFieldState =
Maps.newHashMap();
public INode> generateFieldState(
final INode> d, final Field loc) {
Entry>, Field> e = new AbstractMap.SimpleEntry<>(d, loc);
if (!generatedFieldState.containsKey(e)) {
generatedFieldState.put(e, new GeneratedState, Field>(d, loc));
}
return generatedFieldState.get(e);
}
public void addGeneratedFieldState(GeneratedState, Field> state) {
Entry>, Field> e =
new AbstractMap.SimpleEntry<>(state.node(), state.location());
generatedFieldState.put(e, state);
}
public abstract void computeSuccessor(Node node);
public abstract Field epsilonField();
public abstract Field emptyField();
public abstract Stmt epsilonStmt();
public abstract Field exclusionFieldWildCard(Field exclusion);
public abstract Field fieldWildCard();
public Set> getReachedStates() {
return Sets.newHashSet(reachedStates);
}
public void debugOutput() {
logger.debug(this.getClass().toString());
logger.debug("All reachable states");
prettyPrintSet(getReachedStates());
HashSet> notFieldReachable = Sets.newHashSet(callingContextReachable);
notFieldReachable.removeAll(getReachedStates());
HashSet> notCallingContextReachable = Sets.newHashSet(fieldContextReachable);
notCallingContextReachable.removeAll(getReachedStates());
if (!notFieldReachable.isEmpty()) {
logger.debug("Calling context reachable");
prettyPrintSet(notFieldReachable);
}
if (!notCallingContextReachable.isEmpty()) {
logger.debug("Field matching reachable");
prettyPrintSet(notCallingContextReachable);
}
logger.debug(fieldPDS.toString());
logger.debug(fieldAutomaton.toDotString());
logger.debug(callingPDS.toString());
logger.debug(callAutomaton.toDotString());
logger.debug("===== end === " + this.getClass());
}
private void prettyPrintSet(Collection extends Object> set) {
int j = 0;
String s = "";
for (Object reachableState : set) {
s += reachableState;
s += "\t";
if (j++ > 5) {
s += "\n";
j = 0;
}
}
logger.debug(s);
}
}