package dk.brics.xact.analysis.transformations;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
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.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;
/**
* Flow graph transformation for globally linking field variables.
* For each field variable, a nop node is created.
* Edges labeled with the variable are inserted from all definitions to the join node,
* and from the join node to all uses.
* The variable is removed from the labels of all edges out of use and def sites
* to restrict its flow to the new edges.
*/
public class FieldTransformer {
/**
* Constructs a new <code>FieldTransformer</code>.
*/
public FieldTransformer() {}
/**
* Transforms the given flow graph.
*/
public void run(final FlowGraph graph) {
final Map<Variable,Statement> global_use = new HashMap<Variable,Statement>();
// find all uses of field variables
for (Statement s : new LinkedHashSet<Statement>(graph.getNodes()))
s.visitBy(new StatementVisitor() {
private void use(Variable v, Statement s) {
if (v.isGlobal()) {
Statement join = global_use.get(v);
if (join == null) {
// make new join node
Debug.println(6, true, "Making join node for " + v + " (used in " + s + ")");
join = new NopStm(s.getOrigin());
graph.addNode(join);
global_use.put(v, join);
// Statement ea = new EmptyStm(v, s.getOrigin()); // initialisation value for the global var
// graph.addNode(ea);
// graph.addEntry(ea);
}
// add use edge
graph.addEdge(join, s, new VariableFilter(v));
// remove the variable from the outflow of the statement
for (Edge<Statement,VariableFilter> edge : graph.getOutEdges(s))
edge.getData().removeVariable(v);
}
}
public void visitAnalyzeStm(AnalyzeStm s) {
use(s.getBase(), s);
}
public void visitConcatStm(ConcatStm s) {
use(s.getXMLSource(), s);
}
public void visitConstStm(ConstStm s) {
// do nothing
}
public void visitCopyStm(CopyStm s) {
use(s.getBase(), s);
if (s.getFirstChild() != null)
use(s.getFirstChild(), s);
if (s.getFirstAttr() != null)
use(s.getFirstAttr(), s);
if (s.getNextNode() != null)
use(s.getNextNode(), s);
}
public void visitCastStm(CastStm s) {
use(s.getBase(), s);
}
public void visitCheckStm(CheckStm s) {
if (s.getBase() != null)
use(s.getBase(), s);
}
public void visitEmptyStm(EmptyStm s) {
// do nothing
}
public void visitGapifyStm(GapifyStm s) {
use(s.getBase(), s);
}
public void visitGetStm(GetStm s) {
use(s.getBase(), s);
}
public void visitInsertStm(InsertStm s) {
use(s.getBase(), s);
if (s.getXMLSource() != null)
use(s.getXMLSource(), s);
}
public void visitNodeStm(NodeStm s) {
if (s.getFirstChild() != null)
use(s.getFirstChild(), s);
if (s.getFirstAttr() != null)
use(s.getFirstAttr(), s);
if (s.getNextNode() != null)
use(s.getNextNode(), s);
}
public void visitNopStm(NopStm s) {
// do nothing
}
public void visitPlugStm(PlugStm s) {
use(s.getBase(), s);
if (s.getXMLSource() != null)
use(s.getXMLSource(), s);
}
public void visitRemoveStm(RemoveStm s) {
use(s.getBase(), s);
}
public void visitSetStm(SetStm s) {
use(s.getBase(), s);
if (s.getXMLSource() != null)
use(s.getXMLSource(), s);
}
public void visitUnknownStm(UnknownStm s) {
// do nothing
}
public void visitValidateStm(ValidateStm s) {
if (s.getBase() != null)
use(s.getBase(), s);
}
public void visitVarStm(VarStm s) {
use(s.getSource(), s);
}
// not in this phase
public void visitArrayReadStm(ArrayReadStm s) {
}
public void visitArrayWriteStm(ArrayWriteStm s) {
}
public void visitArrayWriteStringStm(ArrayWriteStringStm s) {
}
public void visitCallStm(CallStm s) {
}
public void visitEscapeStm(EscapeStm s) {
}
});
// insert edges from definitions to single global use
for (Statement s : new LinkedHashSet<Statement>(graph.getNodes()))
if (s instanceof Assignment) {
Variable v = ((Assignment)s).getDest();
if (v.isGlobal()) {
Debug.println(6, true, "Redirecting join flow edge for " + v + " (assigned in " + s + ")");
// remove the variable from the outflow of the statement
for (Edge<Statement,VariableFilter> edge : graph.getOutEdges(s))
edge.getData().removeVariable(v);
// insert new edge to the single global use (which only exists if it is used somewhere)
if (global_use.containsKey(v)) {
graph.addEdge(s, global_use.get(v), new VariableFilter(v));
}
}
}
}
}