package com.jopdesign.dfa.framework;
import com.jopdesign.common.AppInfo;
import com.jopdesign.common.MethodInfo;
import com.jopdesign.common.code.CallString;
import com.jopdesign.common.code.CallString.CallStringSerialization;
import com.jopdesign.common.misc.MethodNotFoundException;
import com.jopdesign.common.type.MemberID;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InstructionList;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintStream;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
/** Helper class to dump analysis results.
* <p>Result Map: MethodInfo -> Instruction Offset -> Callstring -> R</p>
*
* @author Benedikt Huber <benedikt.huber@gmail.com>
*
* @param <R> Type of the result (indexed by instruction handle and callstring)
* @param <S> Type of the serialized result
*/
public class AnalysisResultSerialization<R> {
/** Interface for custom result printers (e.g., for loop bounds) */
public interface ResultFormatter<T> {
public String format(String method, CallStringSerialization callString, Integer position,
T value);
}
/** Interface for custom result type conversion(e.g., for FlowEdge) */
public interface Serializer<T,ST> {
public ST serializedRepresentation(T obj);
public T fromSerializedRepresentation(ST obj, AppInfo appInfo)
throws ClassNotFoundException, MethodNotFoundException, IOException;
}
private Map<String, Map<CallStringSerialization, Map<Integer, R>>> serializedResults;
public AnalysisResultSerialization() {
this.serializedResults = new LinkedHashMap<String, Map<CallStringSerialization,Map<Integer,R>>>();
}
private AnalysisResultSerialization(Map<String, Map<CallStringSerialization, Map<Integer, R>>> serialized) {
this.serializedResults = serialized;
}
public static<T,R> AnalysisResultSerialization<R> fromContextMapResult(
Map<InstructionHandle, ContextMap<CallString, T>> result) {
return fromContextMapResult(result, null);
}
/**
* <p>Result Map: MethodInfo -> Instruction Offset -> Callstring -> R</p>
* <p>TODO: More efficient representations are possible</p>
* @param result the result of the DFA analysis
* @param serializer converter for the result domain (if not serializable), or null
* if the results of type T should be serialized directly
*/
public static<T,R> AnalysisResultSerialization<R> fromContextMapResult(
Map<InstructionHandle, ContextMap<CallString, T>> result,
Serializer<T, R> serializer) {
AnalysisResultSerialization<R> analysisResult = new AnalysisResultSerialization<R>();
/* sort instruction handle by: method, offset */
for (InstructionHandle instr : result.keySet()) {
ContextMap<CallString, T> r = result.get(instr);
Context c = r.getContext();
InstructionList il = c.getMethodInfo().getCode().getInstructionList(true, false);
for (CallString cs : r.keySet()) {
Integer position = instr.getPosition();
// skip stuff that is not used anymore
if (position < 0) continue;
if (il.findHandle(position) != instr) continue;
if(serializer != null) {
T rValue = r.get(cs);
R sValue = serializer.serializedRepresentation(rValue);
analysisResult.addResult(c.getMethodInfo(), position, cs, sValue);
} else {
analysisResult.addResult(c.getMethodInfo(), position, cs, (R) r.get(cs));
}
}
}
return analysisResult;
}
public<T> Map<InstructionHandle, ContextMap<CallString, T>>
toContextMapResult(AppInfo appInfo, Serializer<T,R> serializer)
throws MethodNotFoundException, IOException, ClassNotFoundException {
/* `context' is a really bad hack in the DFA. In the deserialization,
* we guarantee that context.getMethodInfo() and context.callstring are correct,
* but the rest of context is undefined.
*/
Context currentContext;
Map<InstructionHandle, ContextMap<CallString, T>> resultMap =
new LinkedHashMap<InstructionHandle, ContextMap<CallString,T>>();
for(Entry<String, Map<CallStringSerialization, Map<Integer, R>>> miEntry : serializedResults.entrySet()) {
MethodInfo mi = appInfo.getMethodInfo(MemberID.parse(miEntry.getKey()));
for(Entry<CallStringSerialization, Map<Integer, R>> csEntry : miEntry.getValue().entrySet()) {
CallString cs = csEntry.getKey().getCallString(appInfo);
currentContext = new Context();
currentContext.setMethodInfo(mi);
currentContext.callString = cs;
for(Entry<Integer, R> posEntry : csEntry.getValue().entrySet()) {
int pos = posEntry.getKey();
R value = posEntry.getValue();
InstructionHandle instr = mi.getCode().getInstructionList(false,false).findHandle(pos);
ContextMap<CallString, T> ctxMap = resultMap.get(instr);
if(ctxMap == null) {
ctxMap = new ContextMap<CallString, T>(currentContext, new LinkedHashMap<CallString, T>());
resultMap.put(instr, ctxMap);
}
if(serializer == null) {
ctxMap.put(cs, (T) value);
} else {
T origValue = serializer.fromSerializedRepresentation(value, appInfo);
ctxMap.put(cs, origValue);
}
}
}
}
return resultMap;
}
public void addResult(MethodInfo method,
Integer pos,
CallString cs,
R result) {
Map<CallStringSerialization, Map<Integer, R>> csMap =
getOrCreateMapEntry (serializedResults, method.getFQMethodName(), LinkedHashMap.class);
Map<Integer, R> posMap =
getOrCreateMapEntry(csMap, new CallStringSerialization(cs), TreeMap.class);
if(posMap.containsKey(pos)) {
throw new AssertionError("Duplicate Key in DFA result set");
}
posMap.put(pos, result);
}
/* Type hackery: did not manage to implement this in a fully checked way */
@SuppressWarnings("unchecked")
private static <K,V>
V getOrCreateMapEntry(Map<K,V> map, K key, Class instantiable) {
V r = map.get(key);
if(r == null) {
try {
r = (V) instantiable.newInstance();
map.put(key, r);
} catch (ClassCastException e) {
throw new RuntimeException("AnalysisResultSerialization: Internal Error",e);
} catch (Exception e) {
throw new RuntimeException("AnalysisResultSerialization: Internal Error",e);
}
}
return r;
}
/** Dump the results in human readable into a string */
public String dump() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
dump(ps);
return baos.toString();
}
/** Dump the results in human readable form to the given stream */
public void dump(PrintStream os) {
dump(os,null);
}
/** Dump the results in human readable form to the given stream */
public void dump(PrintStream os,ResultFormatter<R> formatter) {
for(Entry<String, Map<CallStringSerialization, Map<Integer, R>>> miEntry : serializedResults.entrySet()) {
os.println(miEntry.getKey());
for(Entry<CallStringSerialization, Map<Integer, R>> csEntry : miEntry.getValue().entrySet()) {
os.println(" "+csEntry.getKey().toString());
for(Entry<Integer, R> posEntry : csEntry.getValue().entrySet()) {
String rStr;
if(formatter != null) {
rStr = formatter.format(miEntry.getKey(), csEntry.getKey(),
posEntry.getKey(), posEntry.getValue());
} else {
rStr = ""+posEntry.getValue();
}
os.println(String.format(" %-6d: %s",posEntry.getKey(), rStr));
}
}
}
}
public void serialize(File f) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(f));
serialize(oos);
oos.close();
}
public void serialize(ObjectOutputStream oos) throws IOException {
oos.writeObject(this.serializedResults);
}
public static<R> AnalysisResultSerialization<R>
fromSerialization(File cacheFile)
throws IOException, ClassNotFoundException, MethodNotFoundException {
FileInputStream fis = new FileInputStream(cacheFile);
ObjectInputStream ois = new ObjectInputStream(fis);
AnalysisResultSerialization<R> result = fromSerialization(ois);
ois.close();
fis.close();
return result;
}
@SuppressWarnings("unchecked")
public static<R> AnalysisResultSerialization<R>
fromSerialization(ObjectInputStream ois)
throws IOException, ClassNotFoundException, MethodNotFoundException {
Map<String, Map<CallStringSerialization, Map<Integer, R>>> serializedResults =
(Map<String, Map<CallStringSerialization, Map<Integer, R>>>) ois.readObject();
return new AnalysisResultSerialization<R>(serializedResults);
}
public static<R, T> Map<InstructionHandle, ContextMap<CallString, T>>
deserializeContextMap(AppInfo appInfo, ObjectInputStream ois,
Serializer<T, R> serializer)
throws IOException, ClassNotFoundException, MethodNotFoundException {
AnalysisResultSerialization<R> r = fromSerialization(ois);
return r.toContextMapResult(appInfo, serializer);
}
}