/*
* This file is part of JOP, the Java Optimized Processor
* see <http://www.jopdesign.com/>
*
* Copyright (C) 2010, Benedikt Huber <benedikt.huber@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 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 Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jopdesign.dfa.analyses;
import com.jopdesign.common.misc.HashedString;
import com.jopdesign.dfa.framework.BoundedSetFactory;
import com.jopdesign.dfa.framework.BoundedSetFactory.BoundedSet;
import org.apache.log4j.Logger;
import java.io.Serializable;
import java.util.HashMap;
/**
*
* @author Benedikt Huber <benedikt.huber@gmail.com>
*
*/
public class SymbolicAddress implements Serializable {
private static final long serialVersionUID = 1L;
enum Tag { ROOT, FIELD_ACCESS, ARRAY_ACCESS }
private Tag tag;
private int depth;
private SymbolicAddressNode symname;
private abstract static class SymbolicAddressNode {
public abstract SymbolicAddress getParent();
public abstract boolean equals_same(SymbolicAddressNode oth);
}
private static class RootObject extends SymbolicAddressNode implements Serializable {
private static final long serialVersionUID = 1L;
private HashedString name;
public RootObject(HashedString name) {
this.name = name ;
}
public SymbolicAddress getParent() {
return null;
}
@Override
public int hashCode() {
return name.hashCode();
}
public boolean equals_same(SymbolicAddressNode oth) {
RootObject other = (RootObject) oth;
return(name.equals(other.name));
}
@Override
public String toString() {
return name.toString();
}
}
private static class FieldAccess extends SymbolicAddressNode implements Serializable {
private static final long serialVersionUID = 1L;
private SymbolicAddress parent;
private HashedString field;
private int hashCode;
private String stringReprCache = null;
public FieldAccess(SymbolicAddress parent, String field) {
this.parent = parent;
this.field = new HashedString(field);
hashCode = field.hashCode() * 31 + parent.hashCode();
}
public SymbolicAddress getParent() {
return parent;
}
@Override
public int hashCode() {
return hashCode;
}
public boolean equals_same(SymbolicAddressNode oth) {
FieldAccess other = (FieldAccess) oth;
if(! field.equals(other.field))
return false;
return(parent.equals(other.parent));
}
@Override
public String toString() {
if(stringReprCache != null) return stringReprCache;
StringBuffer sb = new StringBuffer();
sb.append(parent.toString());
sb.append(".");
sb.append(field.toString());
stringReprCache = sb.toString();
return stringReprCache;
}
}
private static class ArrayAccess extends SymbolicAddressNode implements Serializable {
private static final long serialVersionUID = 1L;
enum ArrayAccessKind { ANY, UNIQUE, ELEM }
private SymbolicAddress parent;
private ArrayAccessKind kind;
private long index;
private int hashCode;
private String stringReprCache;
public static ArrayAccess any(SymbolicAddress symbolicAddress) {
ArrayAccess san = new ArrayAccess();
san.parent = symbolicAddress;
san.kind = ArrayAccessKind.ANY;
san.hashCode = san.parent.hashCode() * 31 + 1;
san.index = 0;
return san;
}
public static ArrayAccess unique(SymbolicAddress symbolicAddress, long name) {
ArrayAccess san = new ArrayAccess();
san.parent = symbolicAddress;
san.kind = ArrayAccessKind.UNIQUE;
san.index = name;
san.hashCode = san.parent.hashCode() * 31 + 2 + ((int)name)<<1;
return san;
}
public static ArrayAccess element(SymbolicAddress symbolicAddress, int ix) {
ArrayAccess san = new ArrayAccess();
san.parent = symbolicAddress;
san.kind = ArrayAccessKind.ELEM;
san.index = ix;
san.hashCode = san.parent.hashCode() * 31 + 3 + (ix)<<1;
return san;
}
public SymbolicAddress getParent() {
return parent;
}
@Override
public int hashCode() {
return hashCode;
}
public boolean equals_same(SymbolicAddressNode oth) {
ArrayAccess other = (ArrayAccess) oth;
if(! kind.equals(other.kind))
return false;
if(index != other.index)
return false;
return(parent.equals(other.parent));
}
@Override
public String toString() {
if(stringReprCache != null) return stringReprCache;
StringBuffer sb = new StringBuffer();
sb.append(parent.toString());
sb.append("[");
switch(kind) {
case ANY: sb.append("*"); break;
case UNIQUE: sb.append("?"+index); break;
case ELEM: sb.append(index); break;
}
sb.append("]");
stringReprCache = sb.toString();
return stringReprCache;
}
}
// uh, bad coding style :( Sometimes I'm lazy too
private static long globalGen = 0;
private static HashMap<HashedString, RootObject> rootPool =
new HashMap<HashedString, RootObject>();
public static SymbolicAddress rootAddress(String root) {
HashedString hstr = new HashedString(root);
RootObject robj = rootPool.get(hstr);
if(robj == null) {
robj = new RootObject(hstr);
rootPool.put(hstr, robj);
}
return new SymbolicAddress(Tag.ROOT, robj);
}
public static SymbolicAddress staticField(String fieldName) {
return rootAddress(fieldName);
}
public static SymbolicAddress stringLiteral(String className, int cpIndex) {
return rootAddress("@lit:"+className+":"+cpIndex);
}
public static SymbolicAddress newName() {
long name = genName();
return rootAddress("?x"+name);
}
public SymbolicAddress access(String fieldName) {
return new SymbolicAddress(Tag.FIELD_ACCESS, new FieldAccess(this,fieldName));
}
public SymbolicAddress accessArrayAny() {
return new SymbolicAddress(Tag.ARRAY_ACCESS, ArrayAccess.any(this));
}
public SymbolicAddress accessArrayUnique() {
long name = genName();
return new SymbolicAddress(Tag.ARRAY_ACCESS, ArrayAccess.unique(this, name));
}
public SymbolicAddress accessArray(int j) {
return new SymbolicAddress(Tag.ARRAY_ACCESS, ArrayAccess.element(this, j));
}
public SymbolicAddress(Tag tag, SymbolicAddressNode san) {
this.tag = tag;
this.symname = san;
this.depth = ( san.getParent() == null ) ? 0 : (san.getParent().depth + 1);
}
public String toString() {
return symname.toString();
}
@Override
public int hashCode() {
return symname.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SymbolicAddress other = (SymbolicAddress) obj;
if (! tag.equals(other.tag))
return false;
if(depth != other.depth)
return false;
return symname.equals_same(other.symname);
}
public static BoundedSet<SymbolicAddress> fieldAccess(
BoundedSetFactory<SymbolicAddress> bsFactory,
BoundedSet<SymbolicAddress> objectMapping,
String fieldName) {
BoundedSet<SymbolicAddress> newMapping;
if(objectMapping == null) {
Logger.getLogger("Object Cache Analysis").error("Undefined mapping for "+fieldName);
return bsFactory.top();
}
if(objectMapping.isSaturated()) {
newMapping = bsFactory.top();
} else {
newMapping = bsFactory.empty();
for(SymbolicAddress addr: objectMapping.getSet()) {
newMapping.add(addr.access(fieldName));
}
}
return newMapping;
}
private static long genName() {
return globalGen++;
}
}