/**
* Transforms the given flow graph.
*/
public void run(final FlowGraph graph) {
final ControlFlowBuilder cfg = new ControlFlowBuilder(graph, null);
final UnionFindForest<Variable,Component> var_alias = new UnionFindForest<Variable, Component>() {
@Override
protected Component mergeNodeData(Component a, Component b) {
a.array |= b.array;
a.escaped |= b.escaped;
a.stringsrc = a.stringsrc.union(b.stringsrc);
return a;
}
@Override
protected Component defaultNodeData() {
return new Component();
}
};
for (Statement s : graph.getNodes()) {
s.visitBy(new BasicStatementVisitor() {
@Override
public void visitEmptyStm(EmptyStm s) {
if (s.isArray()) {
var_alias.getData(s.getDest()).array = true;
}
}
@Override
public void visitEscapeStm(EscapeStm s) {
var_alias.getData(s.getVar()).escaped = true;
}
@Override
public void visitUnknownStm(UnknownStm s) {
var_alias.getData(s.getDest()).escaped = true;
}
@Override
public void visitArrayWriteStringStm(ArrayWriteStringStm s) {
Component c = var_alias.getData(s.getDest());
c.stringsrc = c.stringsrc.union(s.getStringsrc());
}
@Override
public void visitArrayReadStm(ArrayReadStm s) {
var_alias.getData(s.getSource()).array = true;
}
@Override
public void visitArrayWriteStm(ArrayWriteStm s) {
var_alias.getData(s.getDest()).array = true;
}
@Override
public void visitConcatStm(ConcatStm s) {
var_alias.getData(s.getXMLSource()).array = true;
}
@Override
public void visitPlugStm(PlugStm s) {
if (s.getKind() == PlugStm.Kind.PLUGMULTI || s.getKind() == PlugStm.Kind.PLUGWRAP)
var_alias.getData(s.getXMLSource()).array = true;
}
@Override
public void visitVarStm(VarStm s) {
var_alias.merge(s.getSource(), s.getDest());
}
});
}
for (Statement s : new LinkedHashSet<Statement>(graph.getNodes())) {
if (s instanceof Assignment) {
if (var_alias.getData(((Assignment) s).getDest()).array) {
NopStm nop = new NopStm(s.getOrigin());
graph.addNode(nop);
for (Edge<Statement,VariableFilter> e : graph.getInEdges(s)) {
graph.addEdge(e.getFrom(), nop, e.getData());
}
for (Edge<Statement,VariableFilter> e : graph.getOutEdges(s)) {
graph.addEdge(nop, e.getTo(), e.getData());
}
}
}
s.visitBy(new BasicStatementVisitor() {
private void replace(Statement old, StatementPair st) {
replaceNode(graph, old, st);
}
@Override
public void visitArrayWriteStringStm(ArrayWriteStringStm s) {
replace(s, cfg.finish()); // replace the statement with a nop
}
@Override
public void visitArrayReadStm(ArrayReadStm s) {
// merge arrays if one array is stored in the other
if (var_alias.getData(s.getDest()).array) {
var_alias.merge(s.getSource(), s.getDest());
}
cfg.addStatement(new VarStm(s.getDest(), s.getSource(), s.getOrigin()));
replace(s, cfg.finish());
}
@Override
public void visitArrayWriteStm(ArrayWriteStm s) {
// merge arrays if one array is stored in the other
if (var_alias.getData(s.getSource()).array) {
var_alias.merge(s.getSource(), s.getDest());
}
cfg.addStatement(new VarStm(s.getDest(), s.getSource(), s.getOrigin()));
replace(s, cfg.finish());
}
@Override
public void visitCallStm(CallStm s) {
for (int i=0; i<s.getArguments().length; i++) {
// merge argument and parameter if either one is used as an array
Variable arg = s.getArgument(i);
Variable param = s.getMethod().getParameter(i);
if (var_alias.getData(arg).array || var_alias.getData(param).array) {
var_alias.merge(arg, param);
s.setArgumentMutable(i, true);
}
}
// merge return var and result if ether one is used as an array
if (var_alias.getData(s.getResult()).array || var_alias.getData(s.getMethod().getReturnVar()).array) {
var_alias.merge(s.getResult(), s.getMethod().getReturnVar());
}
}
@Override
public void visitEscapeStm(EscapeStm s) {
Component c = var_alias.getData(s.getVar());
if (c.array) {
cfg.addStatement(new UnknownStm(s.getVar(), s.getOrigin()));
replace(s, cfg.finish());
} else {
cfg.setOrigin(s.getOrigin());
replace(s, cfg.finish());
}
}
});
}
// replace each variable by its component representative IF the representative is an array