soot.jimple.toolkits.callgraph.OnFlyCallGraphBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of robovm-soot Show documentation
Show all versions of robovm-soot Show documentation
RoboVM fork of Soot - A Java optimization framework
/* Soot - a J*va Optimization Framework
* Copyright (C) 2003 Ondrej Lhotak
*
* This library 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 library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
package soot.jimple.toolkits.callgraph;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import soot.ArrayType;
import soot.Body;
import soot.Context;
import soot.EntryPoints;
import soot.FastHierarchy;
import soot.G;
import soot.Kind;
import soot.Local;
import soot.MethodContext;
import soot.MethodOrMethodContext;
import soot.PackManager;
import soot.PhaseOptions;
import soot.RefType;
import soot.Scene;
import soot.SceneTransformer;
import soot.SootClass;
import soot.SootMethod;
import soot.SootMethodRef;
import soot.Transform;
import soot.Type;
import soot.Unit;
import soot.Value;
import soot.javaToJimple.LocalGenerator;
import soot.jimple.AssignStmt;
import soot.jimple.FieldRef;
import soot.jimple.InstanceInvokeExpr;
import soot.jimple.InvokeExpr;
import soot.jimple.InvokeStmt;
import soot.jimple.Jimple;
import soot.jimple.NewArrayExpr;
import soot.jimple.NewExpr;
import soot.jimple.NewMultiArrayExpr;
import soot.jimple.SpecialInvokeExpr;
import soot.jimple.StaticFieldRef;
import soot.jimple.StaticInvokeExpr;
import soot.jimple.Stmt;
import soot.jimple.StringConstant;
import soot.jimple.VirtualInvokeExpr;
import soot.jimple.toolkits.reflection.ReflectionTraceInfo;
import soot.options.CGOptions;
import soot.tagkit.Host;
import soot.tagkit.SourceLnPosTag;
import soot.util.LargeNumberedMap;
import soot.util.NumberedString;
import soot.util.SmallNumberedMap;
import soot.util.queue.ChunkedQueue;
import soot.util.queue.QueueReader;
/** Models the call graph.
* @author Ondrej Lhotak
*/
public final class OnFlyCallGraphBuilder
{
public class DefaultReflectionModel implements ReflectionModel {
protected CGOptions options = new CGOptions( PhaseOptions.v().getPhaseOptions("cg") );
protected HashSet warnedAlready = new HashSet();
public void classForName(SootMethod source, Stmt s) {
List stringConstants = (List) methodToStringConstants.get(source);
if( stringConstants == null )
methodToStringConstants.put(source, stringConstants = new ArrayList());
InvokeExpr ie = s.getInvokeExpr();
Value className = ie.getArg(0);
if( className instanceof StringConstant ) {
String cls = ((StringConstant) className ).value;
constantForName( cls, source, s );
} else {
Local constant = (Local) className;
if( options.safe_forname() ) {
for (SootMethod tgt : EntryPoints.v().clinits()) {
addEdge( source, s, tgt, Kind.CLINIT );
}
} else {
for (SootClass cls : Scene.v().dynamicClasses()) {
for (SootMethod clinit : EntryPoints.v().clinitsOf(cls)) {
addEdge( source, s, clinit, Kind.CLINIT);
}
}
VirtualCallSite site = new VirtualCallSite( s, source, null, null, Kind.CLINIT );
List sites = (List) stringConstToSites.get(constant);
if (sites == null) {
stringConstToSites.put(constant, sites = new ArrayList());
stringConstants.add(constant);
}
sites.add(site);
}
}
}
public void classNewInstance(SootMethod source, Stmt s) {
if( options.safe_newinstance() ) {
for (SootMethod tgt : EntryPoints.v().inits()) {
addEdge( source, s, tgt, Kind.NEWINSTANCE );
}
} else {
for (SootClass cls : Scene.v().dynamicClasses()) {
if( cls.declaresMethod(sigInit) ) {
addEdge( source, s, cls.getMethod(sigInit), Kind.NEWINSTANCE );
}
}
if( options.verbose() ) {
G.v().out.println( "Warning: Method "+source+
" is reachable, and calls Class.newInstance;"+
" graph will be incomplete!"+
" Use safe-newinstance option for a conservative result." );
}
}
}
public void contructorNewInstance(SootMethod source, Stmt s) {
if( options.safe_newinstance() ) {
for (SootMethod tgt : EntryPoints.v().allInits()) {
addEdge( source, s, tgt, Kind.NEWINSTANCE );
}
} else {
for (SootClass cls : Scene.v().dynamicClasses()) {
for(SootMethod m: cls.getMethods()) {
if(m.getName().equals("")) {
addEdge( source, s, m, Kind.NEWINSTANCE );
}
}
}
if( options.verbose() ) {
G.v().out.println( "Warning: Method "+source+
" is reachable, and calls Constructor.newInstance;"+
" graph will be incomplete!"+
" Use safe-newinstance option for a conservative result." );
}
}
}
public void methodInvoke(SootMethod container, Stmt invokeStmt) {
if( !warnedAlready(container) ) {
if( options.verbose() ) {
G.v().out.println( "Warning: call to "+
"java.lang.reflect.Method: invoke() from "+container+
"; graph will be incomplete!" );
}
markWarned(container);
}
}
private void markWarned(SootMethod m) {
warnedAlready.add(m);
}
private boolean warnedAlready(SootMethod m) {
return warnedAlready.contains(m);
}
}
public class TraceBasedReflectionModel implements ReflectionModel {
class Guard {
public Guard(SootMethod container, Stmt stmt, String message) {
this.container = container;
this.stmt = stmt;
this.message = message;
}
final SootMethod container;
final Stmt stmt;
final String message;
}
protected Set guards;
protected ReflectionTraceInfo reflectionInfo;
private boolean registeredTransformation = false;
private TraceBasedReflectionModel() {
guards = new HashSet();
String logFile = options.reflection_log();
if(logFile==null) {
throw new InternalError("Trace based refection model enabled but no trace file given!?");
} else {
reflectionInfo = new ReflectionTraceInfo(logFile);
}
}
/**
* Adds an edge to all class initializers of all possible receivers
* of Class.forName() calls within source.
*/
public void classForName(SootMethod container, Stmt forNameInvokeStmt) {
Set classNames = reflectionInfo.classForNameClassNames(container);
if(classNames==null || classNames.isEmpty()) {
registerGuard(container, forNameInvokeStmt, "Class.forName() call site; Soot did not expect this site to be reached");
} else {
for (String clsName : classNames) {
constantForName( clsName, container, forNameInvokeStmt );
}
}
}
/**
* Adds an edge to the constructor of the target class from this call to
* {@link Class#newInstance()}.
*/
public void classNewInstance(SootMethod container, Stmt newInstanceInvokeStmt) {
Set classNames = reflectionInfo.classNewInstanceClassNames(container);
if(classNames==null || classNames.isEmpty()) {
registerGuard(container, newInstanceInvokeStmt, "Class.newInstance() call site; Soot did not expect this site to be reached");
} else {
for (String clsName : classNames) {
SootClass cls = Scene.v().getSootClass(clsName);
if( cls.declaresMethod(sigInit) ) {
SootMethod constructor = cls.getMethod(sigInit);
addEdge( container, newInstanceInvokeStmt, constructor, Kind.REFL_CLASS_NEWINSTANCE );
}
}
}
}
/**
* Adds a special edge of kind {@link Kind#REFL_CONSTR_NEWINSTANCE} to all possible target constructors
* of this call to {@link Constructor#newInstance(Object...)}.
* Those kinds of edges are treated specially in terms of how parameters are assigned,
* as parameters to the reflective call are passed into the argument array of
* {@link Constructor#newInstance(Object...)}.
* @see PAG#addCallTarget(Edge)
*/
public void contructorNewInstance(SootMethod container, Stmt newInstanceInvokeStmt) {
Set constructorSignatures = reflectionInfo.constructorNewInstanceSignatures(container);
if(constructorSignatures==null || constructorSignatures.isEmpty()) {
registerGuard(container, newInstanceInvokeStmt, "Constructor.newInstance(..) call site; Soot did not expect this site to be reached");
} else {
for (String constructorSignature : constructorSignatures) {
SootMethod constructor = Scene.v().getMethod(constructorSignature);
addEdge( container, newInstanceInvokeStmt, constructor, Kind.REFL_CONSTR_NEWINSTANCE );
}
}
}
/**
* Adds a special edge of kind {@link Kind#REFL_INVOKE} to all possible target methods
* of this call to {@link Method#invoke(Object, Object...)}.
* Those kinds of edges are treated specially in terms of how parameters are assigned,
* as parameters to the reflective call are passed into the argument array of
* {@link Method#invoke(Object, Object...)}.
* @see PAG#addCallTarget(Edge)
*/
public void methodInvoke(SootMethod container, Stmt invokeStmt) {
Set methodSignatures = reflectionInfo.methodInvokeSignatures(container);
if (methodSignatures == null || methodSignatures.isEmpty()) {
registerGuard(container, invokeStmt, "Method.invoke(..) call site; Soot did not expect this site to be reached");
} else {
for (String methodSignature : methodSignatures) {
SootMethod method = Scene.v().getMethod(methodSignature);
addEdge( container, invokeStmt, method, Kind.REFL_INVOKE );
}
}
}
private void registerGuard(SootMethod container, Stmt stmt, String string) {
guards.add(new Guard(container,stmt,string));
if(options.verbose()) {
G.v().out.println("Incomplete trace file: Class.forName() is called in method '" +
container+"' but trace contains no information about the receiver class of this call.");
if(options.guards().equals("ignore")) {
G.v().out.println("Guarding strategy is set to 'ignore'. Will ignore this problem.");
} else if(options.guards().equals("print")) {
G.v().out.println("Guarding strategy is set to 'print'. " +
"Program will print a stack trace if this location is reached during execution.");
} else if(options.guards().equals("throw")) {
G.v().out.println("Guarding strategy is set to 'throw'. Program will throw an " +
"Error if this location is reached during execution.");
} else {
throw new RuntimeException("Invalid value for phase option (guarding): "+options.guards());
}
}
if(!registeredTransformation) {
registeredTransformation=true;
PackManager.v().getPack("wjap").add(new Transform("wjap.guards",new SceneTransformer() {
@Override
protected void internalTransform(String phaseName, Map options) {
for (Guard g : guards) {
insertGuard(g);
}
}
}));
PhaseOptions.v().setPhaseOption("wjap.guards", "enabled");
}
}
private void insertGuard(Guard guard) {
if(options.guards().equals("ignore")) return;
SootMethod container = guard.container;
Stmt insertionPoint = guard.stmt;
if(!container.hasActiveBody()) {
G.v().out.println("WARNING: Tried to insert guard into "+container+" but couldn't because method has no body.");
} else {
Body body = container.getActiveBody();
//exc = new Error
RefType runtimeExceptionType = RefType.v("java.lang.Error");
NewExpr newExpr = Jimple.v().newNewExpr(runtimeExceptionType);
LocalGenerator lg = new LocalGenerator(body);
Local exceptionLocal = lg.generateLocal(runtimeExceptionType);
AssignStmt assignStmt = Jimple.v().newAssignStmt(exceptionLocal, newExpr);
body.getUnits().insertBefore(assignStmt, insertionPoint);
//exc.(message)
SootMethodRef cref = runtimeExceptionType.getSootClass().getMethod("", Collections.singletonList(RefType.v("java.lang.String"))).makeRef();
SpecialInvokeExpr constructorInvokeExpr = Jimple.v().newSpecialInvokeExpr(exceptionLocal, cref, StringConstant.v(guard.message));
InvokeStmt initStmt = Jimple.v().newInvokeStmt(constructorInvokeExpr);
body.getUnits().insertAfter(initStmt, assignStmt);
if(options.guards().equals("print")) {
//exc.printStackTrace();
VirtualInvokeExpr printStackTraceExpr = Jimple.v().newVirtualInvokeExpr(exceptionLocal, Scene.v().getSootClass("java.lang.Throwable").getMethod("printStackTrace", Collections.emptyList()).makeRef());
InvokeStmt printStackTraceStmt = Jimple.v().newInvokeStmt(printStackTraceExpr);
body.getUnits().insertAfter(printStackTraceStmt, initStmt);
} else if(options.guards().equals("throw")) {
body.getUnits().insertAfter(Jimple.v().newThrowStmt(exceptionLocal), initStmt);
} else {
throw new RuntimeException("Invalid value for phase option (guarding): "+options.guards());
}
}
}
}
/** context-insensitive stuff */
private final CallGraph cicg = new CallGraph();
private final HashSet analyzedMethods = new HashSet();
private final LargeNumberedMap receiverToSites = new LargeNumberedMap( Scene.v().getLocalNumberer() ); // Local -> List(VirtualCallSite)
private final LargeNumberedMap methodToReceivers = new LargeNumberedMap( Scene.v().getMethodNumberer() ); // SootMethod -> List(Local)
public LargeNumberedMap methodToReceivers() { return methodToReceivers; }
private final SmallNumberedMap stringConstToSites = new SmallNumberedMap( Scene.v().getLocalNumberer() ); // Local -> List(VirtualCallSite)
private final LargeNumberedMap methodToStringConstants = new LargeNumberedMap( Scene.v().getMethodNumberer() ); // SootMethod -> List(Local)
public LargeNumberedMap methodToStringConstants() { return methodToStringConstants; }
private CGOptions options;
private boolean appOnly;
/** context-sensitive stuff */
private ReachableMethods rm;
private QueueReader worklist;
private ContextManager cm;
private final ChunkedQueue targetsQueue = new ChunkedQueue();
private final QueueReader targets = targetsQueue.reader();
public OnFlyCallGraphBuilder( ContextManager cm, ReachableMethods rm ) {
this.cm = cm;
this.rm = rm;
worklist = rm.listener();
options = new CGOptions( PhaseOptions.v().getPhaseOptions("cg") );
if( !options.verbose() ) {
G.v().out.println( "[Call Graph] For information on where the call graph may be incomplete, use the verbose option to the cg phase." );
}
// if(options.reflection_log()==null || options.reflection_log().length()==0) {
reflectionModel = new DefaultReflectionModel();
// } else {
// reflectionModel = new TraceBasedReflectionModel();
// }
}
public OnFlyCallGraphBuilder( ContextManager cm, ReachableMethods rm, boolean appOnly ) {
this( cm, rm );
this.appOnly = appOnly;
}
public void processReachables() {
while(true) {
if( !worklist.hasNext() ) {
rm.update();
if( !worklist.hasNext() ) break;
}
MethodOrMethodContext momc = (MethodOrMethodContext) worklist.next();
SootMethod m = momc.method();
if( appOnly && !m.getDeclaringClass().isApplicationClass() ) continue;
if( analyzedMethods.add( m ) ) processNewMethod( m );
processNewMethodContext( momc );
}
}
public boolean wantTypes( Local receiver ) {
return receiverToSites.get(receiver) != null;
}
public void addType( Local receiver, Context srcContext, Type type, Context typeContext ) {
FastHierarchy fh = Scene.v().getOrMakeFastHierarchy();
for( Iterator siteIt = ((Collection) receiverToSites.get( receiver )).iterator(); siteIt.hasNext(); ) {
final VirtualCallSite site = (VirtualCallSite) siteIt.next();
InstanceInvokeExpr iie = site.iie();
if( site.kind() == Kind.THREAD
&& !fh.canStoreType( type, clRunnable ) )
continue;
if( site.iie() instanceof SpecialInvokeExpr && site.kind != Kind.THREAD ) {
SootMethod target = VirtualCalls.v().resolveSpecial(
(SpecialInvokeExpr) site.iie(),
site.subSig(),
site.container() );
//if the call target resides in a phantom class then "target" will be null;
//simply do not add the target in that case
if(target!=null) {
targetsQueue.add( target );
}
} else {
VirtualCalls.v().resolve( type,
receiver.getType(),
site.subSig(),
site.container(),
targetsQueue );
}
while(targets.hasNext()) {
SootMethod target = (SootMethod) targets.next();
cm.addVirtualEdge(
MethodContext.v( site.container(), srcContext ),
site.stmt(),
target,
site.kind(),
typeContext );
}
}
}
public boolean wantStringConstants( Local stringConst ) {
return stringConstToSites.get(stringConst) != null;
}
public void addStringConstant( Local l, Context srcContext, String constant ) {
for( Iterator siteIt = ((Collection) stringConstToSites.get( l )).iterator(); siteIt.hasNext(); ) {
final VirtualCallSite site = (VirtualCallSite) siteIt.next();
if( constant == null ) {
if( options.verbose() ) {
G.v().out.println( "Warning: Method "+site.container()+
" is reachable, and calls Class.forName on a"+
" non-constant String; graph will be incomplete!"+
" Use safe-forname option for a conservative result." );
}
} else {
if( constant.length() > 0 && constant.charAt(0) == '[' ) {
if( constant.length() > 1 && constant.charAt(1) == 'L'
&& constant.charAt(constant.length()-1) == ';' ) {
constant = constant.substring(2,constant.length()-1);
} else continue;
}
if( !Scene.v().containsClass( constant ) ) {
if( options.verbose() ) {
G.v().out.println( "Warning: Class "+constant+" is"+
" a dynamic class, and you did not specify"+
" it as such; graph will be incomplete!" );
}
} else {
SootClass sootcls = Scene.v().getSootClass( constant );
if( !sootcls.isApplicationClass() ) {
sootcls.setLibraryClass();
}
for (SootMethod clinit : EntryPoints.v().clinitsOf(sootcls)) {
cm.addStaticEdge(
MethodContext.v( site.container(), srcContext ),
site.stmt(),
clinit,
Kind.CLINIT );
}
}
}
}
}
/* End of public methods. */
private void addVirtualCallSite( Stmt s, SootMethod m, Local receiver,
InstanceInvokeExpr iie, NumberedString subSig, Kind kind ) {
List sites = (List) receiverToSites.get(receiver);
if (sites == null) {
receiverToSites.put(receiver, sites = new ArrayList());
List receivers = (List) methodToReceivers.get(m);
if( receivers == null )
methodToReceivers.put(m, receivers = new ArrayList());
receivers.add(receiver);
}
sites.add(new VirtualCallSite(s, m, iie, subSig, kind));
}
private void processNewMethod( SootMethod m ) {
if( m.isNative() || m.isPhantom() ) {
return;
}
Body b = m.retrieveActiveBody();
getImplicitTargets( m );
findReceivers(m, b);
}
private void findReceivers(SootMethod m, Body b) {
for( Iterator sIt = b.getUnits().iterator(); sIt.hasNext(); ) {
final Stmt s = (Stmt) sIt.next();
if (s.containsInvokeExpr()) {
InvokeExpr ie = s.getInvokeExpr();
if (ie instanceof InstanceInvokeExpr) {
InstanceInvokeExpr iie = (InstanceInvokeExpr) ie;
Local receiver = (Local) iie.getBase();
NumberedString subSig =
iie.getMethodRef().getSubSignature();
addVirtualCallSite( s, m, receiver, iie, subSig,
Edge.ieToKind(iie) );
if( subSig == sigStart ) {
addVirtualCallSite( s, m, receiver, iie, sigRun,
Kind.THREAD );
}
} else {
SootMethod tgt = ie.getMethod();
addEdge(m, s, tgt);
if( tgt.getSignature().equals( "" )
|| tgt.getSignature().equals( "" )
|| tgt.getSignature().equals( "" )
|| tgt.getSignature().equals( "" ) ) {
Local receiver = (Local) ie.getArg(0);
addVirtualCallSite( s, m, receiver, null, sigObjRun,
Kind.PRIVILEGED );
}
}
}
}
}
ReflectionModel reflectionModel;
private void getImplicitTargets( SootMethod source ) {
final SootClass scl = source.getDeclaringClass();
if( source.isNative() || source.isPhantom() ) return;
if( source.getSubSignature().indexOf( "" ) >= 0 ) {
handleInit(source, scl);
}
Body b = source.retrieveActiveBody();
for( Iterator sIt = b.getUnits().iterator(); sIt.hasNext(); ) {
final Stmt s = (Stmt) sIt.next();
if( s.containsInvokeExpr() ) {
InvokeExpr ie = s.getInvokeExpr();
if( ie.getMethodRef().getSignature().equals( "" ) ) {
reflectionModel.methodInvoke(source,s);
}
if( ie.getMethodRef().getSignature().equals( "" ) ) {
reflectionModel.classNewInstance(source,s);
}
if( ie.getMethodRef().getSignature().equals( "" ) ) {
reflectionModel.contructorNewInstance(source, s);
}
if( ie.getMethodRef().getSubSignature() == sigForName ) {
reflectionModel.classForName(source,s);
}
if( ie instanceof StaticInvokeExpr ) {
SootClass cl = ie.getMethodRef().declaringClass();
for (SootMethod clinit : EntryPoints.v().clinitsOf(cl)) {
addEdge( source, s, clinit, Kind.CLINIT );
}
}
}
if( s.containsFieldRef() ) {
FieldRef fr = s.getFieldRef();
if( fr instanceof StaticFieldRef ) {
SootClass cl = fr.getFieldRef().declaringClass();
for (SootMethod clinit : EntryPoints.v().clinitsOf(cl)) {
addEdge( source, s, clinit, Kind.CLINIT );
}
}
}
if( s instanceof AssignStmt ) {
Value rhs = ((AssignStmt)s).getRightOp();
if( rhs instanceof NewExpr ) {
NewExpr r = (NewExpr) rhs;
SootClass cl = r.getBaseType().getSootClass();
for (SootMethod clinit : EntryPoints.v().clinitsOf(cl)) {
addEdge( source, s, clinit, Kind.CLINIT );
}
} else if( rhs instanceof NewArrayExpr || rhs instanceof NewMultiArrayExpr ) {
Type t = rhs.getType();
if( t instanceof ArrayType ) t = ((ArrayType)t).baseType;
if( t instanceof RefType ) {
SootClass cl = ((RefType) t).getSootClass();
for (SootMethod clinit : EntryPoints.v().clinitsOf(cl)) {
addEdge( source, s, clinit, Kind.CLINIT );
}
}
}
}
}
}
private void processNewMethodContext( MethodOrMethodContext momc ) {
SootMethod m = momc.method();
Object ctxt = momc.context();
Iterator it = cicg.edgesOutOf(m);
while( it.hasNext() ) {
Edge e = (Edge) it.next();
cm.addStaticEdge( momc, e.srcUnit(), e.tgt(), e.kind() );
}
}
private void handleInit(SootMethod source, final SootClass scl) {
addEdge( source, null, scl, sigFinalize, Kind.FINALIZE );
}
private void constantForName( String cls, SootMethod src, Stmt srcUnit ) {
if( cls.length() > 0 && cls.charAt(0) == '[' ) {
if( cls.length() > 1 && cls.charAt(1) == 'L' && cls.charAt(cls.length()-1) == ';' ) {
cls = cls.substring(2,cls.length()-1);
constantForName( cls, src, srcUnit );
}
} else {
if( !Scene.v().containsClass( cls ) ) {
if( options.verbose() ) {
G.v().out.println( "Warning: Class "+cls+" is"+
" a dynamic class, and you did not specify"+
" it as such; graph will be incomplete!" );
}
} else {
SootClass sootcls = Scene.v().getSootClass( cls );
if( !sootcls.isApplicationClass() ) {
sootcls.setLibraryClass();
}
for (SootMethod clinit : EntryPoints.v().clinitsOf(sootcls)) {
addEdge( src, srcUnit, clinit, Kind.CLINIT );
}
}
}
}
private void addEdge( SootMethod src, Stmt stmt, SootMethod tgt,
Kind kind ) {
cicg.addEdge( new Edge( src, stmt, tgt, kind ) );
}
private void addEdge( SootMethod src, Stmt stmt, SootClass cls, NumberedString methodSubSig, Kind kind ) {
if( cls.declaresMethod( methodSubSig ) ) {
addEdge( src, stmt, cls.getMethod( methodSubSig ), kind );
}
}
private void addEdge( SootMethod src, Stmt stmt, SootMethod tgt ) {
InvokeExpr ie = stmt.getInvokeExpr();
addEdge( src, stmt, tgt, Edge.ieToKind(ie) );
}
protected final NumberedString sigFinalize = Scene.v().getSubSigNumberer().
findOrAdd( "void finalize()" );
protected final NumberedString sigInit = Scene.v().getSubSigNumberer().
findOrAdd( "void ()" );
protected final NumberedString sigStart = Scene.v().getSubSigNumberer().
findOrAdd( "void start()" );
protected final NumberedString sigRun = Scene.v().getSubSigNumberer().
findOrAdd( "void run()" );
protected final NumberedString sigObjRun = Scene.v().getSubSigNumberer().
findOrAdd( "java.lang.Object run()" );
protected final NumberedString sigForName = Scene.v().getSubSigNumberer().
findOrAdd( "java.lang.Class forName(java.lang.String)" );
protected final RefType clRunnable = RefType.v("java.lang.Runnable");
}