
soot.Hierarchy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of soot Show documentation
Show all versions of soot Show documentation
A Java Optimization Framework
package soot;
/*-
* #%L
* Soot - a J*va Optimization Framework
* %%
* Copyright (C) 1997 - 1999 Raja Vallee-Rai
* %%
* 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.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import soot.jimple.SpecialInvokeExpr;
import soot.util.ArraySet;
import soot.util.Chain;
/**
* Represents the class hierarchy. It is closely linked to a Scene, and must be recreated if the Scene changes.
*
* The general convention is that if a method name contains "Including", then it returns the non-strict result; otherwise, it
* does a strict query (e.g. strict superclass).
*/
public class Hierarchy {
// These two maps are not filled in the constructor.
protected Map> classToSubclasses;
protected Map> interfaceToSubinterfaces;
protected Map> interfaceToSuperinterfaces;
protected Map> classToDirSubclasses;
protected Map> interfaceToDirSubinterfaces;
protected Map> interfaceToDirSuperinterfaces;
// This holds the direct implementers.
protected Map> interfaceToDirImplementers;
int state;
Scene sc;
/** Constructs a hierarchy from the current scene. */
public Hierarchy() {
this.sc = Scene.v();
state = sc.getState();
// Well, this used to be describable by 'Duh'.
// Construct the subclasses hierarchy and the subinterfaces hierarchy.
{
Chain allClasses = sc.getClasses();
classToSubclasses = new HashMap>(allClasses.size() * 2 + 1, 0.7f);
interfaceToSubinterfaces = new HashMap>(allClasses.size() * 2 + 1, 0.7f);
interfaceToSuperinterfaces = new HashMap>(allClasses.size() * 2 + 1, 0.7f);
classToDirSubclasses = new HashMap>(allClasses.size() * 2 + 1, 0.7f);
interfaceToDirSubinterfaces = new HashMap>(allClasses.size() * 2 + 1, 0.7f);
interfaceToDirSuperinterfaces = new HashMap>(allClasses.size() * 2 + 1, 0.7f);
interfaceToDirImplementers = new HashMap>(allClasses.size() * 2 + 1, 0.7f);
initializeHierarchy(allClasses);
}
}
/**
* Initializes the hierarchy given a chain of all classes that shall be included in the hierarchy
*
* @param allClasses
* The chain of all classes to be included in the hierarchy
*/
protected void initializeHierarchy(Chain allClasses) {
for (SootClass c : allClasses) {
if (c.resolvingLevel() < SootClass.HIERARCHY) {
continue;
}
if (c.isInterface()) {
interfaceToDirSubinterfaces.put(c, new ArrayList());
interfaceToDirSuperinterfaces.put(c, new ArrayList());
interfaceToDirImplementers.put(c, new ArrayList());
} else {
classToDirSubclasses.put(c, new ArrayList());
}
}
for (SootClass c : allClasses) {
if (c.resolvingLevel() < SootClass.HIERARCHY) {
continue;
}
if (c.hasSuperclass()) {
if (c.isInterface()) {
List l2 = interfaceToDirSuperinterfaces.get(c);
for (SootClass i : c.getInterfaces()) {
if (c.resolvingLevel() < SootClass.HIERARCHY) {
continue;
}
List l = interfaceToDirSubinterfaces.get(i);
if (l != null) {
l.add(c);
}
if (l2 != null) {
l2.add(i);
}
}
} else {
List l = classToDirSubclasses.get(c.getSuperclass());
if (l != null) {
l.add(c);
}
for (SootClass i : c.getInterfaces()) {
if (c.resolvingLevel() < SootClass.HIERARCHY) {
continue;
}
l = interfaceToDirImplementers.get(i);
if (l != null) {
l.add(c);
}
}
}
}
}
// Fill the directImplementers lists with subclasses.
for (SootClass c : allClasses) {
if (c.resolvingLevel() < SootClass.HIERARCHY) {
continue;
}
if (c.isInterface()) {
List imp = interfaceToDirImplementers.get(c);
Set s = new ArraySet();
for (SootClass c0 : imp) {
if (c.resolvingLevel() < SootClass.HIERARCHY) {
continue;
}
s.addAll(getSubclassesOfIncluding(c0));
}
imp.clear();
imp.addAll(s);
}
}
}
protected void checkState() {
if (state != sc.getState()) {
throw new ConcurrentModificationException("Scene changed for Hierarchy!");
}
}
// This includes c in the list of subclasses.
/** Returns a list of subclasses of c, including itself. */
public List getSubclassesOfIncluding(SootClass c) {
c.checkLevel(SootClass.HIERARCHY);
if (c.isInterface()) {
throw new RuntimeException("class needed!");
}
List l = new ArrayList();
l.addAll(getSubclassesOf(c));
l.add(c);
return Collections.unmodifiableList(l);
}
/** Returns a list of subclasses of c, excluding itself. */
public List getSubclassesOf(SootClass c) {
c.checkLevel(SootClass.HIERARCHY);
if (c.isInterface()) {
throw new RuntimeException("class needed!");
}
checkState();
// If already cached, return the value.
if (classToSubclasses.get(c) != null) {
return classToSubclasses.get(c);
}
// Otherwise, build up the hashmap.
List l = new ArrayList();
for (SootClass cls : classToDirSubclasses.get(c)) {
if (cls.resolvingLevel() < SootClass.HIERARCHY) {
continue;
}
l.addAll(getSubclassesOfIncluding(cls));
}
l = Collections.unmodifiableList(l);
classToSubclasses.put(c, l);
return l;
}
/**
* Returns a list of superclasses of {@code sootClass}, including itself.
*
* @param sootClass
* the class of which superclasses will be taken. Must not be {@code null} or interface
* @return list of superclasses, including itself
* @throws IllegalArgumentException
* when passed class is an interface
* @throws NullPointerException
* when passed argument is {@code null}
*/
public List getSuperclassesOfIncluding(SootClass sootClass) {
final List superclasses = getSuperclassesOf(sootClass);
final List result = new ArrayList<>(superclasses.size() + 1);
result.add(sootClass);
result.addAll(superclasses);
return Collections.unmodifiableList(result);
}
/**
* Returns a list of direct superclasses of passed class in reverse order, starting with its parent.
*
* @param sootClass
* the class of which superclasses will be taken. Must not be {@code null} or interface
* @return list of superclasses
* @throws IllegalArgumentException
* when passed class is an interface
* @throws NullPointerException
* when passed argument is {@code null}
*/
public List getSuperclassesOf(SootClass sootClass) {
sootClass.checkLevel(SootClass.HIERARCHY);
if (sootClass.isInterface()) {
throw new IllegalArgumentException(sootClass.getName() + " is an interface, but class is expected");
}
checkState();
final List superclasses = new ArrayList<>();
SootClass current = sootClass;
while (current.hasSuperclass()) {
superclasses.add(current.getSuperclass());
current = current.getSuperclass();
}
return Collections.unmodifiableList(superclasses);
}
/**
* Returns a list of subinterfaces of sootClass, including itself.
*
* @param sootClass
* the interface of which subinterfaces will be taken. Must not be {@code null} or class
* @return list of subinterfaces, including passed one
* @throws IllegalArgumentException
* when passed class is a class
* @throws NullPointerException
* when passed argument is {@code null}
*/
public List getSubinterfacesOfIncluding(SootClass sootClass) {
final List result = new ArrayList<>(getSubinterfacesOf(sootClass));
result.add(sootClass);
return Collections.unmodifiableList(result);
}
/**
* Returns a list of subinterfaces of sootClass, excluding itself.
*
* @param sootClass
* the interface of which subinterfaces will be taken. Must not be {@code null} or class
* @return list of subinterfaces, including passed one
* @throws IllegalArgumentException
* when passed sootClass is a class
* @throws NullPointerException
* when passed argument is {@code null}
*/
public List getSubinterfacesOf(SootClass sootClass) {
sootClass.checkLevel(SootClass.HIERARCHY);
if (!sootClass.isInterface()) {
throw new IllegalArgumentException(sootClass.getName() + " is a class, but interface is expected");
}
checkState();
// If already cached, return the value.
List subInterfaces = interfaceToSubinterfaces.get(sootClass);
if (subInterfaces != null) {
return subInterfaces;
}
// Otherwise, build up the hashmap.
List directSubInterfaces = interfaceToDirSubinterfaces.get(sootClass);
if (directSubInterfaces == null || directSubInterfaces.isEmpty()) {
return Collections.emptyList();
}
final List result = new ArrayList<>();
for (SootClass si : directSubInterfaces) {
result.addAll(getSubinterfacesOfIncluding(si));
}
final List unmodifiableResult = Collections.unmodifiableList(result);
interfaceToSubinterfaces.put(sootClass, unmodifiableResult);
return unmodifiableResult;
}
/** Returns a list of superinterfaces of c, including itself. */
public List getSuperinterfacesOfIncluding(SootClass c) {
c.checkLevel(SootClass.HIERARCHY);
if (!c.isInterface()) {
throw new RuntimeException("interface needed!");
}
List l = new ArrayList();
l.addAll(getSuperinterfacesOf(c));
l.add(c);
return Collections.unmodifiableList(l);
}
/** Returns a list of superinterfaces of c, excluding itself. */
public List getSuperinterfacesOf(SootClass c) {
c.checkLevel(SootClass.HIERARCHY);
if (!c.isInterface()) {
throw new RuntimeException("interface needed!");
}
checkState();
// If already cached, return the value.
List cached = interfaceToSuperinterfaces.get(c);
if (cached != null) {
return cached;
}
// Otherwise, build up the hashmap.
List l = new ArrayList();
for (SootClass si : interfaceToDirSuperinterfaces.get(c)) {
l.addAll(getSuperinterfacesOfIncluding(si));
}
interfaceToSuperinterfaces.put(c, Collections.unmodifiableList(l));
return Collections.unmodifiableList(l);
}
/** Returns a list of direct superclasses of c, excluding c. */
public List getDirectSuperclassesOf(SootClass c) {
throw new RuntimeException("Not implemented yet!");
}
/** Returns a list of direct subclasses of c, excluding c. */
public List getDirectSubclassesOf(SootClass c) {
c.checkLevel(SootClass.HIERARCHY);
if (c.isInterface()) {
throw new RuntimeException("class needed!");
}
checkState();
return Collections.unmodifiableList(classToDirSubclasses.get(c));
}
// This includes c in the list of subclasses.
/** Returns a list of direct subclasses of c, including c. */
public List getDirectSubclassesOfIncluding(SootClass c) {
c.checkLevel(SootClass.HIERARCHY);
if (c.isInterface()) {
throw new RuntimeException("class needed!");
}
checkState();
List l = new ArrayList();
l.addAll(classToDirSubclasses.get(c));
l.add(c);
return Collections.unmodifiableList(l);
}
/** Returns a list of direct superinterfaces of c. */
public List getDirectSuperinterfacesOf(SootClass c) {
throw new RuntimeException("Not implemented yet!");
}
/** Returns a list of direct subinterfaces of c. */
public List getDirectSubinterfacesOf(SootClass c) {
c.checkLevel(SootClass.HIERARCHY);
if (!c.isInterface()) {
throw new RuntimeException("interface needed!");
}
checkState();
return interfaceToDirSubinterfaces.get(c);
}
/** Returns a list of direct subinterfaces of c, including itself. */
public List getDirectSubinterfacesOfIncluding(SootClass c) {
c.checkLevel(SootClass.HIERARCHY);
if (!c.isInterface()) {
throw new RuntimeException("interface needed!");
}
checkState();
List l = new ArrayList();
l.addAll(interfaceToDirSubinterfaces.get(c));
l.add(c);
return Collections.unmodifiableList(l);
}
/** Returns a list of direct implementers of c, excluding itself. */
public List getDirectImplementersOf(SootClass i) {
i.checkLevel(SootClass.HIERARCHY);
if (!i.isInterface()) {
throw new RuntimeException("interface needed; got " + i);
}
checkState();
return Collections.unmodifiableList(interfaceToDirImplementers.get(i));
}
/** Returns a list of implementers of c, excluding itself. */
public List getImplementersOf(SootClass i) {
i.checkLevel(SootClass.HIERARCHY);
if (!i.isInterface()) {
throw new RuntimeException("interface needed; got " + i);
}
checkState();
ArraySet set = new ArraySet();
for (SootClass c : getSubinterfacesOfIncluding(i)) {
set.addAll(getDirectImplementersOf(c));
}
ArrayList l = new ArrayList();
l.addAll(set);
return Collections.unmodifiableList(l);
}
/**
* Returns true if child is a subclass of possibleParent. If one of the known parent classes is phantom, we conservatively
* assume that the current class might be a child.
*/
public boolean isClassSubclassOf(SootClass child, SootClass possibleParent) {
child.checkLevel(SootClass.HIERARCHY);
possibleParent.checkLevel(SootClass.HIERARCHY);
List parentClasses = getSuperclassesOf(child);
if (parentClasses.contains(possibleParent)) {
return true;
}
for (SootClass sc : parentClasses) {
if (sc.isPhantom()) {
return true;
}
}
return false;
}
/**
* Returns true if child is, or is a subclass of, possibleParent. If one of the known parent classes is phantom, we
* conservatively assume that the current class might be a child.
*/
public boolean isClassSubclassOfIncluding(SootClass child, SootClass possibleParent) {
child.checkLevel(SootClass.HIERARCHY);
possibleParent.checkLevel(SootClass.HIERARCHY);
List parentClasses = getSuperclassesOfIncluding(child);
if (parentClasses.contains(possibleParent)) {
return true;
}
for (SootClass sc : parentClasses) {
if (sc.isPhantom()) {
return true;
}
}
return false;
}
/** Returns true if child is a direct subclass of possibleParent. */
public boolean isClassDirectSubclassOf(SootClass c, SootClass c2) {
throw new RuntimeException("Not implemented yet!");
}
/** Returns true if child is a superclass of possibleParent. */
public boolean isClassSuperclassOf(SootClass parent, SootClass possibleChild) {
parent.checkLevel(SootClass.HIERARCHY);
possibleChild.checkLevel(SootClass.HIERARCHY);
return getSubclassesOf(parent).contains(possibleChild);
}
/** Returns true if parent is, or is a superclass of, possibleChild. */
public boolean isClassSuperclassOfIncluding(SootClass parent, SootClass possibleChild) {
parent.checkLevel(SootClass.HIERARCHY);
possibleChild.checkLevel(SootClass.HIERARCHY);
return getSubclassesOfIncluding(parent).contains(possibleChild);
}
/** Returns true if child is a subinterface of possibleParent. */
public boolean isInterfaceSubinterfaceOf(SootClass child, SootClass possibleParent) {
child.checkLevel(SootClass.HIERARCHY);
possibleParent.checkLevel(SootClass.HIERARCHY);
return getSubinterfacesOf(possibleParent).contains(child);
}
/** Returns true if child is a direct subinterface of possibleParent. */
public boolean isInterfaceDirectSubinterfaceOf(SootClass child, SootClass possibleParent) {
child.checkLevel(SootClass.HIERARCHY);
possibleParent.checkLevel(SootClass.HIERARCHY);
return getDirectSubinterfacesOf(possibleParent).contains(child);
}
/** Returns true if parent is a superinterface of possibleChild. */
public boolean isInterfaceSuperinterfaceOf(SootClass parent, SootClass possibleChild) {
parent.checkLevel(SootClass.HIERARCHY);
possibleChild.checkLevel(SootClass.HIERARCHY);
return getSuperinterfacesOf(possibleChild).contains(parent);
}
/** Returns true if parent is a direct superinterface of possibleChild. */
public boolean isInterfaceDirectSuperinterfaceOf(SootClass parent, SootClass possibleChild) {
parent.checkLevel(SootClass.HIERARCHY);
possibleChild.checkLevel(SootClass.HIERARCHY);
return getDirectSuperinterfacesOf(possibleChild).contains(parent);
}
/**
* Returns the most specific type which is an ancestor of both c1 and c2.
*/
public SootClass getLeastCommonSuperclassOf(SootClass c1, SootClass c2) {
c1.checkLevel(SootClass.HIERARCHY);
c2.checkLevel(SootClass.HIERARCHY);
throw new RuntimeException("Not implemented yet!");
}
// Questions about method invocation.
/**
* Checks whether check is a visible class in view of the from class. It assumes that protected and private classes do not
* exit. If they exist and check is either protected or private, the check will return false.
*/
public boolean isVisible(SootClass from, SootClass check) {
if (check.isPublic()) {
return true;
}
if (check.isProtected() || check.isPrivate()) {
return false;
}
// package visibility
return from.getJavaPackageName().equals(check.getJavaPackageName());
}
/**
* Returns true if the classmember m is visible from code in the class from.
*/
public boolean isVisible(SootClass from, ClassMember m) {
from.checkLevel(SootClass.HIERARCHY);
m.getDeclaringClass().checkLevel(SootClass.HIERARCHY);
if (!isVisible(from, m.getDeclaringClass())) {
return false;
}
if (m.isPublic()) {
return true;
}
if (m.isPrivate()) {
return from.equals(m.getDeclaringClass());
}
if (m.isProtected()) {
return isClassSubclassOfIncluding(from, m.getDeclaringClass())
|| from.getJavaPackageName().equals(m.getDeclaringClass().getJavaPackageName());
}
// m is package
return from.getJavaPackageName().equals(m.getDeclaringClass().getJavaPackageName());
}
/**
* Given an object of actual type C (o = new C()), returns the method which will be called on an o.f() invocation.
*/
public SootMethod resolveConcreteDispatch(SootClass concreteType, SootMethod m) {
concreteType.checkLevel(SootClass.HIERARCHY);
m.getDeclaringClass().checkLevel(SootClass.HIERARCHY);
checkState();
if (concreteType.isInterface()) {
throw new RuntimeException("class needed!");
}
String methodSig = m.getSubSignature();
for (SootClass c : getSuperclassesOfIncluding(concreteType)) {
SootMethod sm = c.getMethodUnsafe(methodSig);
if (sm != null && isVisible(c, m)) {
return sm;
}
}
throw new RuntimeException("could not resolve concrete dispatch!\nType: " + concreteType + "\nMethod: " + m);
}
/**
* Given a set of definite receiver types, returns a list of possible targets.
*/
public List resolveConcreteDispatch(List classes, SootMethod m) {
m.getDeclaringClass().checkLevel(SootClass.HIERARCHY);
checkState();
Set s = new ArraySet();
for (Type cls : classes) {
if (cls instanceof RefType) {
s.add(resolveConcreteDispatch(((RefType) cls).getSootClass(), m));
} else if (cls instanceof ArrayType) {
s.add(resolveConcreteDispatch((RefType.v("java.lang.Object")).getSootClass(), m));
} else {
throw new RuntimeException("Unable to resolve concrete dispatch of type " + cls);
}
}
return Collections.unmodifiableList(new ArrayList(s));
}
// what can get called for c & all its subclasses
/**
* Given an abstract dispatch to an object of type c and a method m, gives a list of possible receiver methods.
*/
public List resolveAbstractDispatch(SootClass c, SootMethod m) {
c.checkLevel(SootClass.HIERARCHY);
m.getDeclaringClass().checkLevel(SootClass.HIERARCHY);
checkState();
Set s = new ArraySet();
Collection classesIt;
if (c.isInterface()) {
Set classes = new HashSet();
for (SootClass sootClass : getImplementersOf(c)) {
classes.addAll(getSubclassesOfIncluding(sootClass));
}
classesIt = classes;
} else {
classesIt = getSubclassesOfIncluding(c);
}
for (SootClass cl : classesIt) {
if (!Modifier.isAbstract(cl.getModifiers())) {
s.add(resolveConcreteDispatch(cl, m));
}
}
return Collections.unmodifiableList(new ArrayList(s));
}
// what can get called if you have a set of possible receiver types
/**
* Returns a list of possible targets for the given method and set of receiver types.
*/
public List resolveAbstractDispatch(List classes, SootMethod m) {
m.getDeclaringClass().checkLevel(SootClass.HIERARCHY);
Set s = new ArraySet();
for (SootClass sootClass : classes) {
s.addAll(resolveAbstractDispatch(sootClass, m));
}
return Collections.unmodifiableList(new ArrayList(s));
}
/** Returns the target for the given SpecialInvokeExpr. */
public SootMethod resolveSpecialDispatch(SpecialInvokeExpr ie, SootMethod container) {
container.getDeclaringClass().checkLevel(SootClass.HIERARCHY);
SootMethod target = ie.getMethod();
target.getDeclaringClass().checkLevel(SootClass.HIERARCHY);
/*
* This is a bizarre condition! Hopefully the implementation is correct. See VM Spec, 2nd Edition, Chapter 6, in the
* definition of invokespecial.
*/
if ("".equals(target.getName()) || target.isPrivate()) {
return target;
} else if (isClassSubclassOf(target.getDeclaringClass(), container.getDeclaringClass())) {
return resolveConcreteDispatch(container.getDeclaringClass(), target);
} else {
return target;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy