/* Soot - a J*va Optimization Framework
* Copyright (C) 1997-2000 Etienne Gagnon. All rights reserved.
*
* 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.
*/
/*
* Modified by the Sable Research Group and others 1997-1999.
* See the 'credits' file distributed with Soot for the complete list of
* contributors. (Soot is distributed at http://www.sable.mcgill.ca/soot)
*/
package soot.jimple.toolkits.typing;
import soot.*;
import soot.options.Options;
import soot.util.*;
import java.util.*;
/**
* Each instance of this class represents one type in the class hierarchy (or basic types).
**/
class TypeNode
{
private static final boolean DEBUG = false;
private final int id;
private final Type type;
private final ClassHierarchy hierarchy;
private TypeNode parentClass;
private TypeNode element;
private TypeNode array;
private List parents = Collections.EMPTY_LIST;
private final BitVector ancestors = new BitVector(0);
private final BitVector descendants = new BitVector(0);
public TypeNode(int id, Type type, ClassHierarchy hierarchy)
{
if(type == null || hierarchy == null)
{
throw new InternalTypingException();
}
if(!((type instanceof PrimType) || (type instanceof RefType) ||
(type instanceof ArrayType) || (type instanceof NullType)))
{
G.v().out.println("Unhandled type: " + type);
throw new InternalTypingException();
}
this.id = id;
this.type = type;
this.hierarchy = hierarchy;
if(DEBUG)
{
G.v().out.println("creating node " + this);
}
}
public TypeNode(int id, RefType type, ClassHierarchy hierarchy)
{
this(id, (Type) type, hierarchy);
{
SootClass sClass = type.getSootClass();
if( sClass == null ) throw new RuntimeException( "Oops, forgot to load "+type );
if(sClass.isPhantomClass()) throw new RuntimeException("Jimplification requires "+sClass+", but it is a phantom ref.");
List plist = new LinkedList();
if(sClass.hasSuperclass() &&
!sClass.getName().equals("java.lang.Object"))
{
TypeNode parent = hierarchy.typeNode(RefType.v(sClass.getSuperclass().getName()));
plist.add(parent);
parentClass = parent;
}
for(Iterator i = sClass.getInterfaces().iterator(); i.hasNext();)
{
TypeNode parent = hierarchy.typeNode(RefType.v(((SootClass) i.next()).getName()));
plist.add(parent);
}
parents = Collections.unmodifiableList(plist);
}
descendants.set(hierarchy.NULL.id);
hierarchy.NULL.ancestors.set(id);
for( Iterator parentIt = parents.iterator(); parentIt.hasNext(); ) {
final TypeNode parent = (TypeNode) parentIt.next();
ancestors.set(parent.id);
ancestors.or(parent.ancestors);
parent.fixDescendants(id);
}
}
public TypeNode(int id, ArrayType type, ClassHierarchy hierarchy)
{
this(id, (Type) type, hierarchy);
if(type.numDimensions < 1)
{
throw new InternalTypingException();
}
if(type.numDimensions == 1)
{
element = hierarchy.typeNode(type.baseType);
}
else
{
element = hierarchy.typeNode(ArrayType.v(type.baseType, type.numDimensions - 1));
}
if(element != hierarchy.INT)
{
if(element.array != null)
{
throw new InternalTypingException();
}
element.array = this;
}
{
List plist = new LinkedList();
if(type.baseType instanceof RefType)
{
RefType baseType = (RefType) type.baseType;
SootClass sClass = baseType.getSootClass();
if(sClass.hasSuperclass() && !sClass.getName().equals("java.lang.Object"))
{
TypeNode parent = hierarchy.typeNode(ArrayType.v(RefType.v(sClass.getSuperclass().getName()), type.numDimensions));
plist.add(parent);
parentClass = parent;
}
else if(type.numDimensions == 1)
{
plist.add(hierarchy.OBJECT);
// hack for J2ME library, reported by Stephen Cheng
if (!Options.v().j2me()) {
plist.add(hierarchy.CLONEABLE);
plist.add(hierarchy.SERIALIZABLE);
}
parentClass = hierarchy.OBJECT;
}
else
{
plist.add(hierarchy.typeNode(ArrayType.v(hierarchy.OBJECT.type(), type.numDimensions - 1)));
// hack for J2ME library, reported by Stephen Cheng
if (!Options.v().j2me()) {
plist.add(hierarchy.typeNode(ArrayType.v(hierarchy.CLONEABLE.type(), type.numDimensions - 1)));
plist.add(hierarchy.typeNode(ArrayType.v(hierarchy.SERIALIZABLE.type(), type.numDimensions - 1)));
}
parentClass = hierarchy.typeNode(ArrayType.v(hierarchy.OBJECT.type(), type.numDimensions - 1));
}
for(Iterator i = sClass.getInterfaces().iterator(); i.hasNext(); )
{
TypeNode parent = hierarchy.typeNode(ArrayType.v(RefType.v(((SootClass) i.next()).getName()), type.numDimensions));
plist.add(parent);
}
}
else if(type.numDimensions == 1)
{
plist.add(hierarchy.OBJECT);
// hack for J2ME library, reported by Stephen Cheng
if (!Options.v().j2me()) {
plist.add(hierarchy.CLONEABLE);
plist.add(hierarchy.SERIALIZABLE);
}
parentClass = hierarchy.OBJECT;
}
else
{
plist.add(hierarchy.typeNode(ArrayType.v(hierarchy.OBJECT.type(), type.numDimensions - 1)));
// hack for J2ME library, reported by Stephen Cheng
if (!Options.v().j2me()) {
plist.add(hierarchy.typeNode(ArrayType.v(hierarchy.CLONEABLE.type(), type.numDimensions - 1)));
plist.add(hierarchy.typeNode(ArrayType.v(hierarchy.SERIALIZABLE.type(), type.numDimensions - 1)));
}
parentClass = hierarchy.typeNode(ArrayType.v(hierarchy.OBJECT.type(), type.numDimensions - 1));
}
parents = Collections.unmodifiableList(plist);
}
descendants.set(hierarchy.NULL.id);
hierarchy.NULL.ancestors.set(id);
for( Iterator parentIt = parents.iterator(); parentIt.hasNext(); ) {
final TypeNode parent = (TypeNode) parentIt.next();
ancestors.set(parent.id);
ancestors.or(parent.ancestors);
parent.fixDescendants(id);
}
}
/** Adds the given node to the list of descendants of this node and its ancestors. **/
private void fixDescendants(int id)
{
if(descendants.get(id))
{
return;
}
for( Iterator parentIt = parents.iterator(); parentIt.hasNext(); ) {
final TypeNode parent = (TypeNode) parentIt.next();
parent.fixDescendants(id);
}
descendants.set(id);
}
/** Returns the unique id of this type node. **/
public int id()
{
return id;
}
/** Returns the type represented by this type node. **/
public Type type()
{
return type;
}
public boolean hasAncestor(TypeNode typeNode)
{
return ancestors.get(typeNode.id);
}
public boolean hasAncestorOrSelf(TypeNode typeNode)
{
if(typeNode == this)
return true;
return ancestors.get(typeNode.id);
}
public boolean hasDescendant(TypeNode typeNode)
{
return descendants.get(typeNode.id);
}
public boolean hasDescendantOrSelf(TypeNode typeNode)
{
if(typeNode == this)
return true;
return descendants.get(typeNode.id);
}
public List parents()
{
return parents;
}
public TypeNode parentClass()
{
return parentClass;
}
public String toString()
{
return type.toString()+ "(" + id + ")";
}
public TypeNode lca(TypeNode type) throws TypeException
{
if(type == null)
{
throw new InternalTypingException();
}
if(type == this)
{
return this;
}
if(hasAncestor(type))
{
return type;
}
if(hasDescendant(type))
{
return this;
}
do
{
type = type.parentClass;
if(type == null)
{
try
{
TypeVariable.error("Type Error(12)");
}
catch(TypeException e)
{
if(DEBUG) e.printStackTrace();
throw e;
}
}
}
while(!hasAncestor(type));
return type;
}
public TypeNode lcaIfUnique(TypeNode type) throws TypeException
{
TypeNode initial = type;
if(type == null)
{
throw new InternalTypingException();
}
if(type == this)
{
return this;
}
if(hasAncestor(type))
{
return type;
}
if(hasDescendant(type))
{
return this;
}
do
{
if(type.parents.size() == 1)
{
type = (TypeNode) type.parents.get(0);
}
else
{
if(DEBUG)
{
G.v().out.println("lca " + initial + " (" + type + ") & " + this + " =");
for(Iterator i = type.parents.iterator(); i.hasNext(); )
{
G.v().out.println(" " + i.next());
}
}
return null;
}
}
while(!hasAncestor(type));
return type;
}
public boolean hasElement()
{
return element != null;
}
public TypeNode element()
{
if(element == null)
{
throw new InternalTypingException();
}
return element;
}
public TypeNode array()
{
if(array != null)
{
return array;
}
if(type instanceof ArrayType)
{
ArrayType atype = (ArrayType) type;
array = hierarchy.typeNode(ArrayType.v(atype.baseType, atype.numDimensions + 1));
return array;
}
if(type instanceof PrimType || type instanceof RefType)
{
array = hierarchy.typeNode(ArrayType.v(type, 1));
return array;
}
throw new InternalTypingException();
}
public boolean isNull()
{
if(type instanceof NullType)
{
return true;
}
return false;
}
public boolean isClass()
{
if(type instanceof ArrayType ||
type instanceof NullType ||
(type instanceof RefType &&
!((RefType) type).getSootClass().isInterface()))
{
return true;
}
return false;
}
public boolean isClassOrInterface()
{
if(type instanceof ArrayType ||
type instanceof NullType ||
type instanceof RefType)
{
return true;
}
return false;
}
public boolean isArray()
{
if(type instanceof ArrayType ||
type instanceof NullType)
{
return true;
}
return false;
}
}