package dk.brics.xact.analysis.soot;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import soot.ArrayType;
import soot.NullType;
import soot.RefType;
import soot.SootClass;
import soot.SootMethod;
import soot.Type;
import soot.ValueBox;
import soot.jimple.FieldRef;
import soot.jimple.InvokeExpr;
import dk.brics.automaton.Automaton;
import dk.brics.automaton.Datatypes;
import dk.brics.misc.Origin;
import dk.brics.string.StringAnalysis;
import dk.brics.string.external.FieldResolution;
import dk.brics.string.external.MethodResolution;
import dk.brics.string.external.Resolver;
import dk.brics.xact.XMLValidationException;
import dk.brics.xact.analysis.Debug;
import dk.brics.xact.analysis.config.Configuration;
import dk.brics.xact.analysis.flowgraph.FlowGraph;
import dk.brics.xact.analysis.flowgraph.Statement;
import dk.brics.xact.analysis.flowgraph.statements.ArrayWriteStringStm;
import dk.brics.xact.analysis.flowgraph.statements.BasicStatementVisitor;
import dk.brics.xact.analysis.flowgraph.statements.InsertStm;
import dk.brics.xact.analysis.flowgraph.statements.NodeStm;
import dk.brics.xact.analysis.flowgraph.statements.PlugStm;
import dk.brics.xact.analysis.flowgraph.statements.SetStm;
/**
* Runs the string analysis and replaces the string sources of certain statements.
*/
public class PutStringSources {
private StringAnalysis sa;
private TranslatorContext context;
private FlowGraph graph;
private Configuration config;
public PutStringSources(FlowGraph g, TranslatorContext context, Configuration config) {
this.graph = g;
this.context = context;
this.config = config;
}
/**
* Runs string analysis with hotspots that are relevant for XACT operations.
* This method is <i>not</i> thread-safe (because of Soot).
*/
public void analyzeStrings() {
dk.brics.string.Debug.init();
Debug.println(1, true, "Running string analysis...");
StringAnalysis.clearResolvers();
StringAnalysis.addResolver(new Resolver() {
public MethodResolution resolveMethod(InvokeExpr e, SootMethod m) {
String sig = m.getSignature();
if (sig.equals("<dk.brics.xact.XML: java.lang.String getString(java.lang.String)>") ||
sig.equals("<dk.brics.xact.XML: java.lang.String getString()>")) {
Automaton a = Datatypes.get("string"); // TODO: should use the XML graph (run twice!?)
return new MethodResolution(e.getArgCount(), a);
}
// TODO methods taking an Iterable or array should not corrupt the argument
if (sig.equals("<dk.brics.xact.XML: dk.brics.xact.XML concat(java.lang.Object[])>")) {
MethodResolution r = new MethodResolution(1, Automaton.makeAnyString());
r.setArgumentCorrupted(0, false);
return r;
}
return null;
}
public FieldResolution resolveField(FieldRef f) {
return null;
}
public Automaton resolveToString(SootClass clazz) {
return config.resolveToString(clazz);
}
});
Set<ValueBox> hots = new HashSet<ValueBox>();
for (Statement stm : graph.getNodes()) {
if (context.getStringHotspot1(stm) != null)
hots.add(context.getStringHotspot1(stm));
if (context.getStringHotspot2(stm) != null)
hots.add(context.getStringHotspot2(stm));
}
sa = new StringAnalysis(
hots,
null,
null,
null,
null,
false); // <-- this argument means "do not unload soot"
Debug.println(1, true, "Extracting strings...");
for (Statement xstm : new ArrayList<Statement>(graph.getNodes())) {
xstm.visitBy(new BasicStatementVisitor() {
@Override
public void visitArrayWriteStringStm(ArrayWriteStringStm s) {
s.setStringsrc(getAutomatonFromObjectExp(context.getStringHotspot1(s)));
}
// @Override // set in ArrayTransformer
// public void visitConcatStm(ConcatStm s) {
// s.setStringSource(getAutomatonFromObjectExp(context.getHotspot1(s)));
// }
@Override
public void visitInsertStm(InsertStm s) {
if (context.getStringHotspot1(s) != null)
s.setStringSource(getAutomatonFromObjectExp(context.getStringHotspot1(s)));
}
@Override
public void visitNodeStm(NodeStm s) {
if (context.getStringHotspot1(s) != null)
s.setName(getAutomatonFromStringExp(context.getStringHotspot1(s), s.getOrigin()));
if (context.getStringHotspot2(s) != null)
s.setValue(getAutomatonFromStringExp(context.getStringHotspot2(s), s.getOrigin()));
if (s.getName() != null && context.getNamespace(s) != null)
s.setName(Automaton.makeString('{' + context.getNamespace(s) + '}').concatenate(s.getName()));
}
@Override
public void visitPlugStm(PlugStm s) {
if (context.getStringHotspot1(s) != null)
s.setStringSource(getAutomatonFromObjectExp(context.getStringHotspot1(s)));
}
@Override
public void visitSetStm(SetStm s) {
if (context.getStringHotspot1(s) != null)
s.setStringSource(getAutomatonFromObjectExp(context.getStringHotspot1(s)));
}
});
}
Debug.println(1, true, "String analysis completed.");
}
public Automaton getAutomatonFromStringExp(ValueBox b, Origin origin) {
if (b.getValue().getType() instanceof NullType)
return Automaton.makeString("null");
if (!TranslatorContext.isStringType(b))
throw new XMLValidationException("String expression expected " + b, origin);
return sa.getAutomaton(b);
}
public Automaton getAutomatonFromObjectExp(ValueBox b) { // TODO: cache results of getAutomatonFromObjectExp?
Type t = b.getValue().getType();
if (t instanceof NullType)
return Automaton.makeString("null");
while (t instanceof ArrayType)
t = ((ArrayType) t).getElementType();
if (t instanceof RefType) {
if (t.equals(RefType.v("java.lang.String")) || t.equals(RefType.v("java.lang.Object"))) { // for (arrays of) String, ask the string analyzer
return sa.getAutomaton(b);
} else { // for other reference types, take the union of the type automata for the possible types
List<Automaton> auts = new ArrayList<Automaton>();
for (SootClass sc : context.getSubclassesOfIncluding((RefType)t))
if (!context.implementsToXMLable(sc))
auts.add(sa.getTypeAutomaton(sc.getType()));
return Automaton.union(auts); // TODO: minimize?
}
} else {
throw new RuntimeException("RefType expected. Instead got " + t); // unreachable
}
}
}