package jqian.sootex.location;
import java.util.*;
import jqian.Global;
import soot.jimple.*;
import soot.jimple.internal.*;
import soot.*;
import soot.util.*;
/**
* Encode an access paths in the program.
* An access path always starts with a Location
* <immutable>
*/
public class AccessPath implements Numberable{
private static Object UNCLEAR_INDEX = new Object();
private static Map<Location, AccessPath> _root2AccessPath;
private static int _count = 0;
protected static void reset(){
_count = 0;
//XXX: Use WeakHashMap instead of HashMap from 2008-09-18
_root2AccessPath = new WeakHashMap<Location,AccessPath>();
}
static{
reset();
Global.v().regesiterResetableGlobals(AccessPath.class);
}
public static void release(SootMethod m){
if(!m.hasActiveBody())
return;
Body b = m.getActiveBody();
Collection<Local> locals = b.getLocals();
for(Local local: locals){
Location root = Location.valueToLocation(local);
_root2AccessPath.remove(root);
}
}
/**
* @note In this method, the null constant, string constant are represent with
* HeapObject. The implicit return object also can be represented with a HeapObject
*/
public static AccessPath valueToAccessPath(SootMethod method,Unit stmt,Value value){//throws Exception{
AccessPath ap=null;
//the atoms
if(value instanceof Local
//||value instanceof NullConstant
//||value instanceof StringConstant
){
Location loc = Location.valueToLocation(value);
ap = AccessPath.getByRoot(loc);
}
else if(value instanceof ParameterRef){
//Location loc = StackLocation.getLocation(value);
//ap=AccessPath.getByRoot(loc);
return null;
}
//handling the identity references
else if(value instanceof JCaughtExceptionRef
|| value instanceof ThisRef
|| value instanceof NewExpr){
//Location loc = CommonHeapObject.getHeapObject(method,stmt,value);
//ap=AccessPath.getByRoot(loc);
return null;
}
//handling the new expressions
else if( value instanceof NewArrayExpr
|| value instanceof NewMultiArrayExpr){
//HeapObject hobj=ArraySpace.getHeapObject(method,stmt,value);
//ap=AccessPath.getByRoot(hobj);
return null;
}
//handling array element references
else if(value instanceof ArrayRef){
//may be some problem
Value base=((ArrayRef)value).getBase();
Local local = (Local)base;
ap=AccessPath.getByRoot(Location.valueToLocation(local));
//ap=ap.appendArrayRef(((ArrayRef)value).getIndex());
ap=ap.appendArrayRef();
}
//handling instance field references
else if(value instanceof InstanceFieldRef){
Value base=((InstanceFieldRef)value).getBase();
Local local = (Local)base;
ap=AccessPath.getByRoot(Location.valueToLocation(local));
ap=ap.appendFieldRef(((FieldRef)value).getField());
}
//handling class field references
else if(value instanceof StaticFieldRef){
SootField field=((StaticFieldRef)value).getField();
GlobalLocation loc = Location.getGlobalLocation(field);
ap=AccessPath.getByRoot(loc);
}
//handling cast expression
else if(value instanceof CastExpr){
Value base=((CastExpr)value).getOp();
if(base instanceof Local){
Location loc = Location.valueToLocation(base);
ap = AccessPath.getByRoot(loc);
}else{
return null;
}
}
//handling the access of array length
//NOTE: the array length access is translated to a special instruction in .class files
else if(value instanceof LengthExpr){
//as it return an integer, the expression is left unhandled
return null;
}
return ap;
}
/**
* Get the AccessPath instance by root pointer. If it is not found, just build
* it and store it to the map.
* @param root
* @return
*/
public static AccessPath getByRoot(Location root){
AccessPath ap= _root2AccessPath.get(root);
if(null==ap){
ap=new AccessPath(root);
}
return ap;
}
private final Location _root;
private final AccessPath _father;
private final int _id;
private final Object[] _accessList; // accessor can be SootField/Value/UNCLEAR_INDEX
// private List<AccessPath> _extensions = new ArrayList<AccessPath>(5);
// XXX: Using list to store extensions is too slow
// Using map will improve efficiency, but some program may fail to run due
// to OutOfMemoryException
private Map<Object, AccessPath> _extensions = new HashMap<Object, AccessPath>(5);
//The access path can only be build from an implcit factory
private AccessPath(Location loc){
this._root = loc;
this._accessList = new Object[0];
this._father = null;
this._id=_count;
//register to the factory
_root2AccessPath.put(loc,this);
_count++;
}
private AccessPath(AccessPath father, Object extraAccessor){
this._root = father._root;
int fatherAccessors = father._accessList.length;
_accessList = new Object[fatherAccessors+1];
System.arraycopy(father._accessList, 0, _accessList, 0, fatherAccessors);
_accessList[fatherAccessors] = extraAccessor;
this._father = father;
this._id=_count;
_count++;
}
public int getNumber(){
return _id;
}
public void setNumber(int id){}
public final AccessPath getRootModified(final Location newRoot){
AccessPath ap=getByRoot(newRoot);
for(Object accessor: _accessList){
ap=ap.appendAccessor(accessor);
}
return ap;
}
final AccessPath appendAccessor(final Object accessor){
//if found, do not reconstruct it
/*for(AccessPath ap: _extensions){
int index = ap._accessList.length-1;
Object ac = ap._accessList[index];
if(ac==accessor){
return ap;
}
}
AccessPath ext = new AccessPath(this,accessor);
_extensions.add(ext);
return ext; */
AccessPath ext = _extensions.get(accessor);
if(ext==null){
ext = new AccessPath(this,accessor);
_extensions.put(accessor,ext);
}
return ext;
}
public AccessPath appendFieldRef(SootField field){
return appendAccessor(field);
}
/** Append an array reference without distinguish the index. */
public AccessPath appendArrayRef(){
return appendAccessor(UNCLEAR_INDEX);
}
/**Truncate the access path by the given length*/
public AccessPath getTruncated(int length){
if(length<0){
throw new RuntimeException("Truncate access path error");
}
AccessPath cur=this;
while(cur!=null && cur.length()>length){
cur=cur._father;
}
return cur;
}
/**Return a sequence of ancestor access paths*/
public List<AccessPath> getAncestors(){
List<AccessPath> list=new LinkedList<AccessPath>();
AccessPath cur=_father;
while(cur!=null){
list.add(0,cur); //add to list head
cur=cur._father;
}
return list;
}
/** return the length of this access path */
public int length(){
return _accessList.length;
}
public String toString(){
String str=_root.toString();
for(Object ac: _accessList){
if(ac instanceof SootField){
str+='.'+((SootField)ac).getName();
}else{
str+="[]";
}
}
return str;
}
/** return the root of this access path */
public Location getRoot(){
return _root;
}
/**Check whether the access path contains an array element access*/
public boolean withArrayElmtAccess(){
for(Object ac: _accessList){
if(!(ac instanceof SootField)){
return true;
}
}
return false;
}
/** Get the declaration type of an access path.
* NOTE: To save memory space, we do not have a type field for the AccessPath,
* and for long access paths, accessing its type may have some cost.
* For an access path with length in 3, this will not be a problem.
*/
public Type getDeclareType(){
if(_accessList.length==0){
return _root.getType();
}
Object lastAccessor = getLastAccessor();
if(lastAccessor instanceof SootField){
return ((SootField)lastAccessor).getType();
}
else{// if is a array accessor, return the type of array element
Type type = _father.getDeclareType();
assert(type instanceof ArrayType);
return ((ArrayType)type).getArrayElementType();
}
}
public AccessPath getFather(){
return _father;
}
/**Check whether the current access path is a prefix of another access path. */
public boolean isPrefixOf(AccessPath that){
if(this._root!=that._root){
return false;
}
int length = _accessList.length;
if(length > that._accessList.length){
return false;
}
for(int i=0;i<length;i++){
Object thisAc = _accessList[i];
Object thatAc = that._accessList[i];
if(thisAc!=thatAc){
return false;
}
}
return true;
}
public boolean isTruePrefixOf(AccessPath that){
if(!isPrefixOf(that)){
return false;
}else if(this.length()>=that.length()){
return false;
}
return true;
}
public static Collection<AccessPath> getAllRootAccessPaths(){
Set<AccessPath> rootAps = new HashSet<AccessPath>();
Set<Location> rootPtrs = _root2AccessPath.keySet();
for(Iterator<Location> it = rootPtrs.iterator();it.hasNext();){
rootAps.add(_root2AccessPath.get(it.next()));
}
return rootAps;
}
/**Return all currently constructed access paths*/
public static Collection<AccessPath> getAllAccessPaths(){
Collection<AccessPath> rootAps = getAllRootAccessPaths();
List<AccessPath> list=new LinkedList<AccessPath>();
for(Iterator<AccessPath> it=rootAps.iterator();it.hasNext();){
AccessPath ap = it.next();
list.add(ap);
list.addAll(ap.getAllExtensions());
}
return list;
}
public Collection<AccessPath> getImmediateExtensions(){
//return _extensions;
return _extensions.values();
}
/**Recusively get all extensions of the current access path*/
public List<AccessPath> getAllExtensions(){
List<AccessPath> list=new LinkedList<AccessPath>();
for(AccessPath ext: _extensions.values()){
list.add(ext);
list.addAll(ext.getAllExtensions());
}
return list;
}
/** Get the sequence of accessors. */
public Object[] getAccessors(){
return _accessList;
}
public final Object getLastAccessor(){
int index = _accessList.length-1;
return _accessList[index];
}
public AccessPath appendAccessors(List<Object> accessors){
AccessPath cur = this;
for(Iterator<Object> it=accessors.iterator();it.hasNext();){
Object access = it.next();
cur = cur.appendAccessor(access);
}
return cur;
}
/** Get the last accessed field in the accessor list.
* This method only work correctly when withArrayElementAccess()==false*/
public SootField getLastAccessedField(){
Object accessor = getLastAccessor();
return (SootField)accessor;
}
/** Check whether two access path is the same in a limited length. */
public boolean isPartialyEquals(AccessPath that,int length){
if(this._root!=that._root)
return false;
if(_accessList.length<length || that._accessList.length<length)
throw new RuntimeException("Too big length to compare.");
for(int i=0;i<length;i++){
Object thisAccessor = _accessList[i];
Object thatAccessor = that._accessList[i];
if(!thisAccessor.equals(thatAccessor))
return false;
}
return true;
}
public boolean match(AccessPath ap){
if(getLastAccessedField()!=AnyField.v())
return this==ap;
else{
int len=this.length();
if(len!=ap.length()) return false;
if(this._father==ap._father)
return true;
return false;
}
}
public static boolean isFieldSelector(Object accessor){
return accessor!=null && (accessor instanceof SootField);
}
public static SootField getAccessedField(Object accessor){
return (SootField)accessor;
}
}