/* Soot - a J*va Optimization Framework * Copyright (C) 2002 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.spark.internal; import java.util.*; import soot.jimple.spark.pag.*; import soot.*; import soot.util.*; import java.util.Iterator; import soot.util.queue.*; import soot.Type; /** A map of bit-vectors representing subtype relationships. * @author Ondrej Lhotak * * @author Hamid A. Toussi (hamid2c@gmail.com): * 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 T’s 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<SootClass, List<AllocNode>> class2allocs = new HashMap<SootClass, List<AllocNode>>(1024); private List<AllocNode> anySubtypeAllocs = new LinkedList<AllocNode>(); public TypeManager( PAG pag ) { this.pag = pag; } 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 = (AllocNode) allocNodeListener.next(); for( Iterator tIt = Scene.v().getTypeNumberer().iterator(); tIt.hasNext(); ) { final Type t = (Type) tIt.next(); if( !(t instanceof RefLikeType) ) continue; if( t instanceof AnySubType ) continue; if( isUnresolved(t) ) continue; if( castNeverFails( n.getType(), t ) ) { BitVector mask = (BitVector) typeMask.get( t ); if( mask == null ) { typeMask.put( t, mask = new BitVector() ); for( Iterator anIt = pag.getAllocNodeNumberer().iterator(); anIt.hasNext(); ) { final AllocNode an = (AllocNode) anIt.next(); 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 ) throw new RuntimeException( "oops"+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; int numTypes = Scene.v().getTypeNumberer().size(); if( pag.getOpts().verbose() ) G.v().out.println( "Total types: "+numTypes ); // ** initClass2allocs(); makeClassTypeMask(Scene.v().getSootClass("java.lang.Object")); // ** ArrayNumberer allocNodes = pag.getAllocNodeNumberer(); for( Iterator tIt = Scene.v().getTypeNumberer().iterator(); tIt.hasNext(); ) { final Type t = (Type) tIt.next(); if( !(t instanceof RefLikeType) ) continue; if( t instanceof AnySubType ) continue; if( isUnresolved(t) ) continue; // ** if (t instanceof RefType && !t.equals(RefType.v("java.lang.Object")) && !t.equals(RefType.v("java.io.Serializable")) && !t.equals(RefType.v("java.lang.Cloneable"))) { SootClass sc = ((RefType)t).getSootClass(); if (sc.isInterface()) { makeMaskOfInterface(sc); } continue; } // ** BitVector mask = new BitVector( allocNodes.size() ); for( Iterator nIt = allocNodes.iterator(); nIt.hasNext(); ) { final Node n = (Node) nIt.next(); 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; if( dst == null ) return true; if( dst == src ) return true; if( src == null ) return false; if( dst.equals( src ) ) return true; if( src instanceof NullType ) return true; if( src instanceof AnySubType ) return true; if( dst instanceof NullType ) return false; if( dst instanceof AnySubType ) throw new RuntimeException( "oops src="+src+" dst="+dst ); return fh.canStoreType( src, dst ); } public void setFastHierarchy( FastHierarchy fh ) { this.fh = fh; } public FastHierarchy getFastHierarchy() { return fh; } protected FastHierarchy fh = null; protected PAG pag; protected QueueReader allocNodeListener = null; // ** new methods private void initClass2allocs() { Iterator allocIt = pag.getAllocNodeNumberer().iterator(); while (allocIt.hasNext()) { AllocNode an = (AllocNode) allocIt.next(); 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<AllocNode> list ; if ((list = class2allocs.get(cl)) == null) { list = new LinkedList<AllocNode>(); class2allocs.put(cl, list); } list.add(alloc); } final public void caseAnySubType(AnySubType t) { anySubtypeAllocs.add(alloc); } }); } final private BitVector makeClassTypeMask(SootClass clazz) { int nBits = pag.getAllocNodeNumberer().size(); final BitVector mask = new BitVector(nBits); List<AllocNode> allocs = null; if (clazz.isConcrete()) { allocs = class2allocs.get(clazz); } if (allocs != null){ for (AllocNode an : allocs) { mask.set(an.getNumber()); } } Collection<SootClass> subclasses = fh.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<SootClass> implementers = fh.getAllImplementersOfInterface(interf); for (SootClass impl : implementers) { BitVector other = (BitVector)typeMask.get(impl.getType()); if (other == null) throw new RuntimeException(impl.toString()); 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; } }