soot.jimple.toolkits.reflection.ReflectionTraceInfo Maven / Gradle / Ivy
package soot.jimple.toolkits.reflection;
/*-
* #%L
* Soot - a J*va Optimization Framework
* %%
* Copyright (C) 2010 Eric Bodden
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 2.1 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import soot.Body;
import soot.Scene;
import soot.SootClass;
import soot.SootMethod;
import soot.Unit;
import soot.tagkit.Host;
import soot.tagkit.LineNumberTag;
import soot.tagkit.SourceLnPosTag;
public class ReflectionTraceInfo {
private static final Logger logger = LoggerFactory.getLogger(ReflectionTraceInfo.class);
public enum Kind {
ClassForName, ClassNewInstance, ConstructorNewInstance, MethodInvoke, FieldSet, FieldGet
}
protected Map> classForNameReceivers;
protected Map> classNewInstanceReceivers;
protected Map> constructorNewInstanceReceivers;
protected Map> methodInvokeReceivers;
protected Map> fieldSetReceivers;
protected Map> fieldGetReceivers;
public ReflectionTraceInfo(String logFile) {
classForNameReceivers = new LinkedHashMap>();
classNewInstanceReceivers = new LinkedHashMap>();
constructorNewInstanceReceivers = new LinkedHashMap>();
methodInvokeReceivers = new LinkedHashMap>();
fieldSetReceivers = new LinkedHashMap>();
fieldGetReceivers = new LinkedHashMap>();
if (logFile == null) {
throw new InternalError("Trace based refection model enabled but no trace file given!?");
} else {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(logFile)));
String line;
int lines = 0;
Set ignoredKinds = new HashSet();
while ((line = reader.readLine()) != null) {
if (line.length() == 0) {
continue;
}
String[] portions = line.split(";", -1);
String kind = portions[0];
String target = portions[1];
String source = portions[2];
int lineNumber = portions[3].length() == 0 ? -1 : Integer.parseInt(portions[3]);
Set possibleSourceMethods = inferSource(source, lineNumber);
for (SootMethod sourceMethod : possibleSourceMethods) {
if (kind.equals("Class.forName")) {
Set receiverNames;
if ((receiverNames = classForNameReceivers.get(sourceMethod)) == null) {
classForNameReceivers.put(sourceMethod, receiverNames = new LinkedHashSet());
}
receiverNames.add(target);
} else if (kind.equals("Class.newInstance")) {
Set receiverNames;
if ((receiverNames = classNewInstanceReceivers.get(sourceMethod)) == null) {
classNewInstanceReceivers.put(sourceMethod, receiverNames = new LinkedHashSet());
}
receiverNames.add(target);
} else if (kind.equals("Method.invoke")) {
if (!Scene.v().containsMethod(target)) {
throw new RuntimeException("Unknown method for signature: " + target);
}
Set receiverNames;
if ((receiverNames = methodInvokeReceivers.get(sourceMethod)) == null) {
methodInvokeReceivers.put(sourceMethod, receiverNames = new LinkedHashSet());
}
receiverNames.add(target);
} else if (kind.equals("Constructor.newInstance")) {
if (!Scene.v().containsMethod(target)) {
throw new RuntimeException("Unknown method for signature: " + target);
}
Set receiverNames;
if ((receiverNames = constructorNewInstanceReceivers.get(sourceMethod)) == null) {
constructorNewInstanceReceivers.put(sourceMethod, receiverNames = new LinkedHashSet());
}
receiverNames.add(target);
} else if (kind.equals("Field.set*")) {
if (!Scene.v().containsField(target)) {
throw new RuntimeException("Unknown method for signature: " + target);
}
Set receiverNames;
if ((receiverNames = fieldSetReceivers.get(sourceMethod)) == null) {
fieldSetReceivers.put(sourceMethod, receiverNames = new LinkedHashSet());
}
receiverNames.add(target);
} else if (kind.equals("Field.get*")) {
if (!Scene.v().containsField(target)) {
throw new RuntimeException("Unknown method for signature: " + target);
}
Set receiverNames;
if ((receiverNames = fieldGetReceivers.get(sourceMethod)) == null) {
fieldGetReceivers.put(sourceMethod, receiverNames = new LinkedHashSet());
}
receiverNames.add(target);
} else {
ignoredKinds.add(kind);
}
}
lines++;
}
if (!ignoredKinds.isEmpty()) {
logger
.debug("Encountered reflective calls entries of the following kinds that\n" + "cannot currently be handled:");
for (String kind : ignoredKinds) {
logger.debug("" + kind);
}
}
} catch (FileNotFoundException e) {
throw new RuntimeException("Trace file not found.", e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
private Set inferSource(String source, int lineNumber) {
String className = source.substring(0, source.lastIndexOf("."));
String methodName = source.substring(source.lastIndexOf(".") + 1);
if (!Scene.v().containsClass(className)) {
Scene.v().addBasicClass(className, SootClass.BODIES);
Scene.v().loadBasicClasses();
if (!Scene.v().containsClass(className)) {
throw new RuntimeException("Trace file refers to unknown class: " + className);
}
}
SootClass sootClass = Scene.v().getSootClass(className);
Set methodsWithRightName = new LinkedHashSet();
for (SootMethod m : sootClass.getMethods()) {
if (m.isConcrete() && m.getName().equals(methodName)) {
methodsWithRightName.add(m);
}
}
if (methodsWithRightName.isEmpty()) {
throw new RuntimeException("Trace file refers to unknown method with name " + methodName + " in Class " + className);
} else if (methodsWithRightName.size() == 1) {
return Collections.singleton(methodsWithRightName.iterator().next());
} else {
// more than one method with that name
for (SootMethod sootMethod : methodsWithRightName) {
if (coversLineNumber(lineNumber, sootMethod)) {
return Collections.singleton(sootMethod);
}
if (sootMethod.isConcrete()) {
if (!sootMethod.hasActiveBody()) {
sootMethod.retrieveActiveBody();
}
Body body = sootMethod.getActiveBody();
if (coversLineNumber(lineNumber, body)) {
return Collections.singleton(sootMethod);
}
for (Unit u : body.getUnits()) {
if (coversLineNumber(lineNumber, u)) {
return Collections.singleton(sootMethod);
}
}
}
}
// if we get here then we found no method with the right line number information;
// be conservative and return all method that we found
return methodsWithRightName;
}
}
private boolean coversLineNumber(int lineNumber, Host host) {
{
SourceLnPosTag tag = (SourceLnPosTag) host.getTag("SourceLnPosTag");
if (tag != null) {
if (tag.startLn() <= lineNumber && tag.endLn() >= lineNumber) {
return true;
}
}
}
{
LineNumberTag tag = (LineNumberTag) host.getTag("LineNumberTag");
if (tag != null) {
if (tag.getLineNumber() == lineNumber) {
return true;
}
}
}
return false;
}
public Set classForNameClassNames(SootMethod container) {
if (!classForNameReceivers.containsKey(container)) {
return Collections.emptySet();
}
return classForNameReceivers.get(container);
}
public Set classForNameClasses(SootMethod container) {
Set result = new LinkedHashSet();
for (String className : classForNameClassNames(container)) {
result.add(Scene.v().getSootClass(className));
}
return result;
}
public Set classNewInstanceClassNames(SootMethod container) {
if (!classNewInstanceReceivers.containsKey(container)) {
return Collections.emptySet();
}
return classNewInstanceReceivers.get(container);
}
public Set classNewInstanceClasses(SootMethod container) {
Set result = new LinkedHashSet();
for (String className : classNewInstanceClassNames(container)) {
result.add(Scene.v().getSootClass(className));
}
return result;
}
public Set constructorNewInstanceSignatures(SootMethod container) {
if (!constructorNewInstanceReceivers.containsKey(container)) {
return Collections.emptySet();
}
return constructorNewInstanceReceivers.get(container);
}
public Set constructorNewInstanceConstructors(SootMethod container) {
Set result = new LinkedHashSet();
for (String signature : constructorNewInstanceSignatures(container)) {
result.add(Scene.v().getMethod(signature));
}
return result;
}
public Set methodInvokeSignatures(SootMethod container) {
if (!methodInvokeReceivers.containsKey(container)) {
return Collections.emptySet();
}
return methodInvokeReceivers.get(container);
}
public Set methodInvokeMethods(SootMethod container) {
Set result = new LinkedHashSet();
for (String signature : methodInvokeSignatures(container)) {
result.add(Scene.v().getMethod(signature));
}
return result;
}
public Set methodsContainingReflectiveCalls() {
Set res = new LinkedHashSet();
res.addAll(classForNameReceivers.keySet());
res.addAll(classNewInstanceReceivers.keySet());
res.addAll(constructorNewInstanceReceivers.keySet());
res.addAll(methodInvokeReceivers.keySet());
return res;
}
public Set fieldSetSignatures(SootMethod container) {
if (!fieldSetReceivers.containsKey(container)) {
return Collections.emptySet();
}
return fieldSetReceivers.get(container);
}
public Set fieldGetSignatures(SootMethod container) {
if (!fieldGetReceivers.containsKey(container)) {
return Collections.emptySet();
}
return fieldGetReceivers.get(container);
}
}