soot.jimple.spark.internal.TypeManager Maven / Gradle / Ivy
package soot.jimple.spark.internal;
/*-
* #%L
* Soot - a J*va Optimization Framework
* %%
* Copyright (C) 2002 Ondrej Lhotak
* %%
* 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.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import soot.AnySubType;
import soot.ArrayType;
import soot.FastHierarchy;
import soot.NullType;
import soot.RefLikeType;
import soot.RefType;
import soot.Scene;
import soot.SootClass;
import soot.Type;
import soot.TypeSwitch;
import soot.jimple.spark.pag.AllocNode;
import soot.jimple.spark.pag.Node;
import soot.jimple.spark.pag.PAG;
import soot.util.ArrayNumberer;
import soot.util.BitVector;
import soot.util.LargeNumberedMap;
import soot.util.queue.QueueReader;
/**
* A map of bit-vectors representing subtype relationships.
*
* @author Ondrej Lhotak
*
* @author Hamid A. Toussi ([email protected]): Making TypeManager faster by making type masks during a depth-first-traversal
* on the class hierarchy. First, type-masks of the leaves of Class Hierarchy are created and then the type mask of
* each type T is obtained by ORing type maks of Types sub-types and setting the bit-numbers associated with
* Allocation Nodes of type T. The type-mask of each interface is achieved by ORing the type-masks of its top-level
* concrete implementers. In fact, Reference types are visited in reversed-topological-order.
*/
public final class TypeManager {
private Map> class2allocs = new HashMap>(1024);
private List anySubtypeAllocs = new LinkedList();
protected final RefType rtObject;
protected final RefType rtSerializable;
protected final RefType rtCloneable;
public TypeManager(PAG pag) {
this.pag = pag;
this.rtObject = RefType.v("java.lang.Object");
this.rtSerializable = RefType.v("java.io.Serializable");
this.rtCloneable = RefType.v("java.lang.Cloneable");
}
public static boolean isUnresolved(Type type) {
if (type instanceof ArrayType) {
ArrayType at = (ArrayType) type;
type = at.getArrayElementType();
}
if (!(type instanceof RefType)) {
return false;
}
RefType rt = (RefType) type;
if (!rt.hasSootClass()) {
return true;
}
SootClass cl = rt.getSootClass();
return cl.resolvingLevel() < SootClass.HIERARCHY;
}
final public BitVector get(Type type) {
if (type == null) {
return null;
}
while (allocNodeListener.hasNext()) {
AllocNode n = allocNodeListener.next();
for (final Type t : Scene.v().getTypeNumberer()) {
if (!(t instanceof RefLikeType)) {
continue;
}
if (t instanceof AnySubType) {
continue;
}
if (isUnresolved(t)) {
continue;
}
if (castNeverFails(n.getType(), t)) {
BitVector mask = typeMask.get(t);
if (mask == null) {
typeMask.put(t, mask = new BitVector());
for (final AllocNode an : pag.getAllocNodeNumberer()) {
if (castNeverFails(an.getType(), t)) {
mask.set(an.getNumber());
}
}
continue;
}
mask.set(n.getNumber());
}
}
}
BitVector ret = (BitVector) typeMask.get(type);
if (ret == null && fh != null) {
// If we have a phantom class and have no type mask, we assume that
// it is not cast-compatible to anything
SootClass curClass = ((RefType) type).getSootClass();
if (curClass.isPhantom()) {
return new BitVector();
} else {
// Scan through the hierarchy. We might have a phantom class higher up
while (curClass.hasSuperclass()) {
curClass = curClass.getSuperclass();
if (type instanceof RefType && curClass.isPhantom()) {
return new BitVector();
}
}
throw new RuntimeException("Type mask not found for type " + type);
}
}
return ret;
}
final public void clearTypeMask() {
typeMask = null;
}
final public void makeTypeMask() {
RefType.v("java.lang.Class");
typeMask = new LargeNumberedMap(Scene.v().getTypeNumberer());
if (fh == null) {
return;
}
// **
initClass2allocs();
makeClassTypeMask(Scene.v().getSootClass("java.lang.Object"));
BitVector visitedTypes = new BitVector();
{
Iterator it = typeMask.keyIterator();
while (it.hasNext()) {
Type t = it.next();
visitedTypes.set(t.getNumber());
}
}
// **
ArrayNumberer allocNodes = pag.getAllocNodeNumberer();
for (Type t : Scene.v().getTypeNumberer()) {
if (!(t instanceof RefLikeType)) {
continue;
}
if (t instanceof AnySubType) {
continue;
}
if (isUnresolved(t)) {
continue;
}
// **
if (t instanceof RefType && t != rtObject && t != rtSerializable && t != rtCloneable) {
RefType rt = (RefType) t;
SootClass sc = rt.getSootClass();
if (sc.isInterface()) {
makeMaskOfInterface(sc);
}
if (!visitedTypes.get(t.getNumber()) && !rt.getSootClass().isPhantom()) {
makeClassTypeMask(rt.getSootClass());
}
continue;
}
// **
BitVector mask = new BitVector(allocNodes.size());
for (Node n : allocNodes) {
if (castNeverFails(n.getType(), t)) {
mask.set(n.getNumber());
}
}
typeMask.put(t, mask);
}
allocNodeListener = pag.allocNodeListener();
}
private LargeNumberedMap typeMask = null;
final public boolean castNeverFails(Type src, Type dst) {
if (fh == null) {
return true;
} else if (dst == null) {
return true;
} else if (dst == src) {
return true;
} else if (src == null) {
return false;
} else if (src instanceof NullType) {
return true;
} else if (src instanceof AnySubType) {
return true;
} else if (dst instanceof NullType) {
return false;
} else if (dst instanceof AnySubType) {
throw new RuntimeException("oops src=" + src + " dst=" + dst);
} else {
return getFastHierarchy().canStoreType(src, dst);
}
}
public void setFastHierarchy(Supplier fh) {
this.fh = fh;
}
public FastHierarchy getFastHierarchy() {
return fh == null ? null : fh.get();
}
protected Supplier fh = null;
protected PAG pag;
protected QueueReader allocNodeListener = null;
// ** new methods
private void initClass2allocs() {
for (AllocNode an : pag.getAllocNodeNumberer()) {
addAllocNode(an);
}
}
final private void addAllocNode(final AllocNode alloc) {
alloc.getType().apply(new TypeSwitch() {
final public void caseRefType(RefType t) {
SootClass cl = t.getSootClass();
List list;
if ((list = class2allocs.get(cl)) == null) {
list = new LinkedList();
class2allocs.put(cl, list);
}
list.add(alloc);
}
final public void caseAnySubType(AnySubType t) {
anySubtypeAllocs.add(alloc);
}
});
}
final private BitVector makeClassTypeMask(SootClass clazz) {
{
BitVector cachedMask = typeMask.get(clazz.getType());
if (cachedMask != null) {
return cachedMask;
}
}
int nBits = pag.getAllocNodeNumberer().size();
final BitVector mask = new BitVector(nBits);
List allocs = null;
if (clazz.isConcrete()) {
allocs = class2allocs.get(clazz);
}
if (allocs != null) {
for (AllocNode an : allocs) {
mask.set(an.getNumber());
}
}
Collection subclasses = fh.get().getSubclassesOf(clazz);
if (subclasses == Collections.EMPTY_LIST) {
for (AllocNode an : anySubtypeAllocs) {
mask.set(an.getNumber());
}
typeMask.put(clazz.getType(), mask);
return mask;
}
for (SootClass subcl : subclasses) {
mask.or(makeClassTypeMask(subcl));
}
typeMask.put(clazz.getType(), mask);
return mask;
}
final private BitVector makeMaskOfInterface(SootClass interf) {
if (!(interf.isInterface())) {
throw new RuntimeException();
}
BitVector ret = new BitVector(pag.getAllocNodeNumberer().size());
typeMask.put(interf.getType(), ret);
Collection implementers = getFastHierarchy().getAllImplementersOfInterface(interf);
for (SootClass impl : implementers) {
BitVector other = typeMask.get(impl.getType());
if (other == null) {
other = makeClassTypeMask(impl);
}
ret.or(other);
}
// I think, the following can be eliminated. It is added to make
// type-masks exactly the same as the original type-masks
if (implementers.size() == 0) {
for (AllocNode an : anySubtypeAllocs) {
ret.set(an.getNumber());
}
}
return ret;
}
}