package dk.brics.xact.analysis.transformations;
import java.util.LinkedHashSet;
import dk.brics.automaton.Automaton;
import dk.brics.xact.analysis.Debug;
import dk.brics.xact.analysis.flowgraph.Edge;
import dk.brics.xact.analysis.flowgraph.FlowGraph;
import dk.brics.xact.analysis.flowgraph.Statement;
import dk.brics.xact.analysis.flowgraph.Variable;
import dk.brics.xact.analysis.flowgraph.VariableFilter;
import dk.brics.xact.analysis.flowgraph.statements.AnalyzeStm;
import dk.brics.xact.analysis.flowgraph.statements.ArrayReadStm;
import dk.brics.xact.analysis.flowgraph.statements.ArrayWriteStm;
import dk.brics.xact.analysis.flowgraph.statements.ArrayWriteStringStm;
import dk.brics.xact.analysis.flowgraph.statements.Assignment;
import dk.brics.xact.analysis.flowgraph.statements.BasicStatementVisitor;
import dk.brics.xact.analysis.flowgraph.statements.CallStm;
import dk.brics.xact.analysis.flowgraph.statements.CastStm;
import dk.brics.xact.analysis.flowgraph.statements.CheckStm;
import dk.brics.xact.analysis.flowgraph.statements.ConcatStm;
import dk.brics.xact.analysis.flowgraph.statements.ConstStm;
import dk.brics.xact.analysis.flowgraph.statements.CopyStm;
import dk.brics.xact.analysis.flowgraph.statements.EmptyStm;
import dk.brics.xact.analysis.flowgraph.statements.EscapeStm;
import dk.brics.xact.analysis.flowgraph.statements.GapifyStm;
import dk.brics.xact.analysis.flowgraph.statements.GetStm;
import dk.brics.xact.analysis.flowgraph.statements.InsertStm;
import dk.brics.xact.analysis.flowgraph.statements.NodeStm;
import dk.brics.xact.analysis.flowgraph.statements.NopStm;
import dk.brics.xact.analysis.flowgraph.statements.PlugStm;
import dk.brics.xact.analysis.flowgraph.statements.RemoveStm;
import dk.brics.xact.analysis.flowgraph.statements.SetStm;
import dk.brics.xact.analysis.flowgraph.statements.StatementVisitor;
import dk.brics.xact.analysis.flowgraph.statements.UnknownStm;
import dk.brics.xact.analysis.flowgraph.statements.ValidateStm;
import dk.brics.xact.analysis.flowgraph.statements.VarStm;
import dk.brics.xact.analysis.soot.ControlFlowBuilder;
import dk.brics.xact.analysis.soot.StatementPair;
import dk.brics.xact.analysis.util.UnionFindForest;
/**
* Flow graph transformation for linking array variables using weak updating.
* All assignments of a non-array value to an array is made into a weak update.
* All assignments between array variables cause variable aliasing.
* Aliased variables are joined.
* Arrays are classified as XML arrays and non-XML arrays. Concat statements
* whose argument is an XML array have their string source replaced by the
* empty language automaton.
*/
public class ArrayTransformer {
/**
* Constructs a new <code>ArrayTransformer</code>.
*/
public ArrayTransformer() {}
private static class Component {
public boolean array;
public boolean escaped;
public Automaton stringsrc;
public Component() {
this.array = this.escaped = false;
this.stringsrc = Automaton.makeEmpty();
}
}
/**
* 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
for (Statement s : graph.getNodes()) {
// replace variables in nodes
s.visitBy(new StatementVisitor() {
private Variable getrep(Variable var) {
return var_alias.getRepresentativeKey(var);
}
private Variable replace(Variable var) {
Variable rep = getrep(var);
if (!var_alias.getData(rep).array) {
return var;
} else {
Debug.println(6, true, "Aliasing: " + var);
return rep;
}
}
private Automaton getStringSource(Variable var) {
return var_alias.getData(var).stringsrc;
}
public void visitAnalyzeStm(AnalyzeStm s) {
s.setBase(replace(s.getBase()));
s.setDest(replace(s.getDest()));
}
public void visitConcatStm(ConcatStm s) {
s.setDest(replace(s.getDest()));
s.setXMLSource(replace(s.getXMLSource()));
s.setStringSource(getStringSource(s.getXMLSource()));
}
public void visitConstStm(ConstStm s) {
s.setDest(replace(s.getDest()));
}
public void visitCopyStm(CopyStm s) {
s.setBase(replace(s.getBase()));
s.setDest(replace(s.getDest()));
if (s.getFirstChild() != null)
s.setFirstChild(replace(s.getFirstChild()));
if (s.getFirstAttr() != null)
s.setFirstAttr(replace(s.getFirstAttr()));
if (s.getNextNode() != null)
s.setNextNode(replace(s.getNextNode()));
}
public void visitCastStm(CastStm s) {
s.setBase(replace(s.getBase()));
s.setDest(replace(s.getDest()));
}
public void visitCheckStm(CheckStm s) {
if (s.getBase() != null)
s.setBase(replace(s.getBase()));
}
public void visitEmptyStm(EmptyStm s) {
s.setDest(replace(s.getDest()));
}
public void visitGapifyStm(GapifyStm s) {
s.setBase(replace(s.getBase()));
s.setDest(replace(s.getDest()));
}
public void visitGetStm(GetStm s) {
s.setBase(replace(s.getBase()));
s.setDest(replace(s.getDest()));
}
public void visitInsertStm(InsertStm s) {
s.setBase(replace(s.getBase()));
s.setDest(replace(s.getDest()));
if (s.getXMLSource() != null)
s.setXMLSource(replace(s.getXMLSource()));
}
public void visitNodeStm(NodeStm s) {
s.setDest(replace(s.getDest()));
if (s.getFirstChild() != null)
s.setFirstChild(replace(s.getFirstChild()));
if (s.getFirstAttr() != null)
s.setFirstAttr(replace(s.getFirstAttr()));
if (s.getNextNode() != null)
s.setNextNode(replace(s.getNextNode()));
}
public void visitNopStm(NopStm s) {
// do nothing
}
public void visitPlugStm(PlugStm s) {
s.setBase(replace(s.getBase()));
s.setDest(replace(s.getDest()));
if (s.getXMLSource() != null)
s.setXMLSource(replace(s.getXMLSource()));
if (s.getKind() == PlugStm.Kind.PLUGMULTI || s.getKind() == PlugStm.Kind.PLUGWRAP)
s.setStringSource(getStringSource(s.getXMLSource()));
}
public void visitRemoveStm(RemoveStm s) {
s.setBase(replace(s.getBase()));
s.setDest(replace(s.getDest()));
}
public void visitSetStm(SetStm s) {
s.setBase(replace(s.getBase()));
s.setDest(replace(s.getDest()));
if (s.getXMLSource() != null)
s.setXMLSource(replace(s.getXMLSource()));
}
public void visitUnknownStm(UnknownStm s) {
s.setDest(replace(s.getDest()));
}
public void visitValidateStm(ValidateStm s) {
if (s.getBase() != null)
s.setBase(replace(s.getBase()));
s.setDest(replace(s.getDest()));
}
public void visitVarStm(VarStm s) {
s.setSource(replace(s.getSource()));
s.setDest(replace(s.getDest()));
}
public void visitCallStm(CallStm s) {
s.setResult(replace(s.getResult()));
for (int i=0; i<s.getArguments().length; i++) {
s.setArgument(i, replace(s.getArgument(i)));
s.getMethod().setParameter(i, replace(s.getMethod().getParameter(i)));
}
}
// not present anymore
public void visitEscapeStm(EscapeStm s) {
}
public void visitArrayReadStm(ArrayReadStm s) {
}
public void visitArrayWriteStm(ArrayWriteStm s) {
}
public void visitArrayWriteStringStm(ArrayWriteStringStm s) {
}
});
// replace variables in edges
// XXX this could take quadratic time. it could be improved easily
for (Edge<Statement,VariableFilter> edge : graph.getOutEdges(s)) {
VariableFilter filter = edge.getData();
for (Variable var : var_alias.getKeys())
if (filter.containsVariable(var))
filter.addVariable(var_alias.getRepresentativeKey(var)); // XXX: no need to remove var?
}
}
}
private void replaceNode(final FlowGraph graph, Statement old,
StatementPair st) {
for (Edge<Statement,VariableFilter> edge : graph.getInEdges(old))
graph.addEdge(edge.getFrom(), st.first, edge.getData());
for (Edge<Statement,VariableFilter> edge : graph.getOutEdges(old))
graph.addEdge(st.last, edge.getTo(), edge.getData());
graph.removeNode(old);
}
}