/* 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.*;
/** Represents a type variable. **/
class TypeVariable implements Comparable
{
private static final boolean DEBUG = false;
private final int id;
private final TypeResolver resolver;
private TypeVariable rep = this;
private int rank = 0;
private TypeNode approx;
private TypeNode type;
private TypeVariable array;
private TypeVariable element;
private int depth;
private List<TypeVariable> parents = Collections.unmodifiableList(new LinkedList());
private List<TypeVariable> children = Collections.unmodifiableList(new LinkedList());
private BitVector ancestors;
private BitVector indirectAncestors;
public TypeVariable(int id, TypeResolver resolver)
{
this.id = id;
this.resolver = resolver;
}
public TypeVariable(int id, TypeResolver resolver, TypeNode type)
{
this.id = id;
this.resolver = resolver;
this.type = type;
approx = type;
for( Iterator parentIt = type.parents().iterator(); parentIt.hasNext(); ) {
final TypeNode parent = (TypeNode) parentIt.next();
addParent(resolver.typeVariable(parent));
}
if(type.hasElement())
{
element = resolver.typeVariable(type.element());
element.array = this;
}
}
public int hashCode()
{
if(rep != this)
{
return ecr().hashCode();
}
return id;
}
public boolean equals(Object obj)
{
if(rep != this)
{
return ecr().equals(obj);
}
if(obj == null)
{
return false;
}
if(!obj.getClass().equals(getClass()))
{
return false;
}
TypeVariable ecr = ((TypeVariable) obj).ecr();
if(ecr != this)
{
return false;
}
return true;
}
public int compareTo(Object o)
{
if(rep != this)
{
return ecr().compareTo(o);
}
return id - ((TypeVariable) o).ecr().id;
}
private TypeVariable ecr()
{
if(rep != this)
{
rep = rep.ecr();
}
return rep;
}
public TypeVariable union(TypeVariable var) throws TypeException
{
if(rep != this)
{
return ecr().union(var);
}
TypeVariable y = var.ecr();
if(this == y)
{
return this;
}
if(rank > y.rank)
{
y.rep = this;
merge(y);
y.clear();
return this;
}
rep = y;
if(rank == y.rank)
{
y.rank++;
}
y.merge(this);
clear();
return y;
}
private void clear()
{
approx = null;
type = null;
element = null;
array = null;
parents = null;
children = null;
ancestors = null;
indirectAncestors = null;
}
private void merge(TypeVariable var) throws TypeException
{
if(depth != 0 || var.depth != 0)
{
throw new InternalTypingException();
}
// Merge types
if(type == null)
{
type = var.type;
}
else if(var.type != null)
{
error("Type Error(1): Attempt to merge two types.");
}
// Merge parents
{
Set<TypeVariable> set = new TreeSet<TypeVariable>(parents);
set.addAll(var.parents);
set.remove(this);
parents = Collections.unmodifiableList(new LinkedList<TypeVariable>(set));
}
// Merge children
{
Set<TypeVariable> set = new TreeSet<TypeVariable>(children);
set.addAll(var.children);
set.remove(this);
children = Collections.unmodifiableList(new LinkedList<TypeVariable>(set));
}
}
void validate() throws TypeException
{
if(rep != this)
{
ecr().validate();
return;
}
// Validate relations.
if(type != null)
{
for (TypeVariable typeVariable : parents) {
TypeVariable parent = typeVariable.ecr();
if(parent.type != null)
{
if(!type.hasAncestor(parent.type))
{
if(DEBUG)
{
G.v().out.println(parent.type + " is not a parent of " + type);
}
error("Type Error(2): Parent type is not a valid ancestor.");
}
}
}
for (TypeVariable typeVariable : children) {
TypeVariable child = typeVariable.ecr();
if(child.type != null)
{
if(!type.hasDescendant(child.type))
{
if(DEBUG)
{
G.v().out.println(child.type + "(" + child + ") is not a child of " + type + "(" + this + ")");
}
error("Type Error(3): Child type is not a valid descendant.");
}
}
}
}
}
public void removeIndirectRelations()
{
if(rep != this)
{
ecr().removeIndirectRelations();
return;
}
if(indirectAncestors == null)
{
fixAncestors();
}
List<TypeVariable> parentsToRemove = new LinkedList<TypeVariable>();
for (TypeVariable parent : parents) {
if(indirectAncestors.get(parent.id()))
{
parentsToRemove.add(parent);
}
}
for (TypeVariable parent : parentsToRemove) {
removeParent(parent);
}
}
private void fixAncestors()
{
BitVector ancestors = new BitVector(0);
BitVector indirectAncestors = new BitVector(0);
for (TypeVariable typeVariable : parents) {
TypeVariable parent = typeVariable.ecr();
if(parent.ancestors == null)
{
parent.fixAncestors();
}
ancestors.set(parent.id);
ancestors.or(parent.ancestors);
indirectAncestors.or(parent.ancestors);
}
this.ancestors = ancestors;
this.indirectAncestors = indirectAncestors;
}
public int id()
{
if(rep != this)
{
return ecr().id();
}
return id;
}
public void addParent(TypeVariable variable)
{
if(rep != this)
{
ecr().addParent(variable);
return;
}
TypeVariable var = variable.ecr();
if(var == this)
{
return;
}
{
Set<TypeVariable> set = new TreeSet<TypeVariable>(parents);
set.add(var);
parents = Collections.unmodifiableList(new LinkedList<TypeVariable>(set));
}
{
Set<TypeVariable> set = new TreeSet<TypeVariable>(var.children);
set.add(this);
var.children = Collections.unmodifiableList(new LinkedList<TypeVariable>(set));
}
}
public void removeParent(TypeVariable variable)
{
if(rep != this)
{
ecr().removeParent(variable);
return;
}
TypeVariable var = variable.ecr();
{
Set<TypeVariable> set = new TreeSet<TypeVariable>(parents);
set.remove(var);
parents = Collections.unmodifiableList(new LinkedList<TypeVariable>(set));
}
{
Set<TypeVariable> set = new TreeSet<TypeVariable>(var.children);
set.remove(this);
var.children = Collections.unmodifiableList(new LinkedList<TypeVariable>(set));
}
}
public void addChild(TypeVariable variable)
{
if(rep != this)
{
ecr().addChild(variable);
return;
}
TypeVariable var = variable.ecr();
if(var == this)
{
return;
}
{
Set<TypeVariable> set = new TreeSet<TypeVariable>(children);
set.add(var);
children = Collections.unmodifiableList(new LinkedList<TypeVariable>(set));
}
{
Set<TypeVariable> set = new TreeSet<TypeVariable>(var.parents);
set.add(this);
var.parents = Collections.unmodifiableList(new LinkedList<TypeVariable>(set));
}
}
public void removeChild(TypeVariable variable)
{
if(rep != this)
{
ecr().removeChild(variable);
return;
}
TypeVariable var = variable.ecr();
{
Set<TypeVariable> set = new TreeSet<TypeVariable>(children);
set.remove(var);
children = Collections.unmodifiableList(new LinkedList<TypeVariable>(set));
}
{
Set<TypeVariable> set = new TreeSet<TypeVariable>(var.parents);
set.remove(this);
var.parents = Collections.unmodifiableList(new LinkedList<TypeVariable>(set));
}
}
public int depth()
{
if(rep != this)
{
return ecr().depth();
}
return depth;
}
public void makeElement()
{
if(rep != this)
{
ecr().makeElement();
return;
}
if(element == null)
{
element = resolver.typeVariable();
element.array = this;
}
}
public TypeVariable element()
{
if(rep != this)
{
return ecr().element();
}
return (element == null) ? null : element.ecr();
}
public TypeVariable array()
{
if(rep != this)
{
return ecr().array();
}
return (array == null) ? null : array.ecr();
}
public List<TypeVariable> parents()
{
if(rep != this)
{
return ecr().parents();
}
return parents;
}
public List<TypeVariable> children()
{
if(rep != this)
{
return ecr().children();
}
return children;
}
public TypeNode approx()
{
if(rep != this)
{
return ecr().approx();
}
return approx;
}
public TypeNode type()
{
if(rep != this)
{
return ecr().type();
}
return type;
}
static void error(String message) throws TypeException
{
try
{
throw new TypeException(message);
}
catch(TypeException e)
{
if(DEBUG)
{
e.printStackTrace();
}
throw e;
}
}
/** Computes approximative types. The work list must be
* initialized with all constant type variables. */
public static void computeApprox(TreeSet<TypeVariable> workList) throws TypeException
{
while(workList.size() > 0)
{
TypeVariable var = workList.first();
workList.remove(var);
var.fixApprox(workList);
}
}
private void fixApprox(TreeSet<TypeVariable> workList) throws TypeException
{
if(rep != this)
{
ecr().fixApprox(workList);
return;
}
if(type == null && approx != resolver.hierarchy().NULL)
{
TypeVariable element = element();
if(element != null)
{
if(!approx.hasElement())
{
G.v().out.println("*** " + this + " ***");
error("Type Error(4)");
}
TypeNode temp = approx.element();
if(element.approx == null)
{
element.approx = temp;
workList.add(element);
}
else
{
TypeNode type = element.approx.lca(temp);
if(type != element.approx)
{
element.approx = type;
workList.add(element);
}
else if(element.approx != resolver.hierarchy().INT)
{
type = approx.lca(element.approx.array());
if(type != approx)
{
approx = type;
workList.add(this);
}
}
}
}
TypeVariable array = array();
if(array != null &&
approx != resolver.hierarchy().NULL &&
approx != resolver.hierarchy().INT)
{
TypeNode temp = approx.array();
if(array.approx == null)
{
array.approx = temp;
workList.add(array);
}
else
{
TypeNode type = array.approx.lca(temp);
if(type != array.approx)
{
array.approx = type;
workList.add(array);
}
else
{
type = approx.lca(array.approx.element());
if(type != approx)
{
approx = type;
workList.add(this);
}
}
}
}
}
for (TypeVariable typeVariable : parents) {
TypeVariable parent = typeVariable.ecr();
if(parent.approx == null)
{
parent.approx = approx;
workList.add(parent);
}
else
{
TypeNode type = parent.approx.lca(approx);
if(type != parent.approx)
{
parent.approx = type;
workList.add(parent);
}
}
}
if(type != null)
{
approx = type;
}
}
public void fixDepth() throws TypeException
{
if(rep != this)
{
ecr().fixDepth();
return;
}
if(type != null)
{
if(type.type() instanceof ArrayType)
{
ArrayType at = (ArrayType) type.type();
depth = at.numDimensions;
}
else
{
depth = 0;
}
}
else
{
if(approx.type() instanceof ArrayType)
{
ArrayType at = (ArrayType) approx.type();
depth = at.numDimensions;
}
else
{
depth = 0;
}
}
// make sure array types have element type
if(depth == 0 && element() != null)
{
error("Type Error(11)");
}
else if(depth > 0 && element() == null)
{
makeElement();
TypeVariable element = element();
element.depth = depth - 1;
while(element.depth != 0)
{
element.makeElement();
element.element().depth = element.depth - 1;
element = element.element();
}
}
}
public void propagate()
{
if(rep != this)
{
ecr().propagate();
}
if(depth == 0)
{
return;
}
for (TypeVariable typeVariable : parents) {
TypeVariable var = typeVariable.ecr();
if(var.depth() == depth)
{
element().addParent(var.element());
}
else if(var.depth() == 0)
{
if(var.type() == null) {
// hack for J2ME library, reported by Stephen Cheng
if (!Options.v().j2me()) {
var.addChild(resolver.typeVariable(resolver.hierarchy().CLONEABLE));
var.addChild(resolver.typeVariable(resolver.hierarchy().SERIALIZABLE));
}
}
}
else
{
if(var.type() == null) {
// hack for J2ME library, reported by Stephen Cheng
if (!Options.v().j2me()) {
var.addChild(resolver.typeVariable(ArrayType.v(RefType.v("java.lang.Cloneable"), var.depth())));
var.addChild(resolver.typeVariable(ArrayType.v(RefType.v("java.io.Serializable"), var.depth())));
}
}
}
}
for (TypeVariable var : parents) {
removeParent(var);
}
}
public String toString()
{
if(rep != this)
{
return ecr().toString();
}
StringBuffer s = new StringBuffer();
s.append(",[parents:");
{
boolean comma = false;
for (TypeVariable typeVariable : parents) {
if(comma)
{
s.append(",");
}
else
{
comma = true;
}
s.append(typeVariable.id());
}
}
s.append("],[children:");
{
boolean comma = false;
for (TypeVariable typeVariable : children) {
if(comma)
{
s.append(",");
}
else
{
comma = true;
}
s.append(typeVariable.id());
}
}
s.append("]");
return "[id:" + id + ",depth:" + depth + ((type != null) ? (",type:" + type) : "") + ",approx:" + approx + s +
(element == null ? "" : ",arrayof:" + element.id()) + "]";
}
public void fixParents()
{
if(rep != this)
{
ecr().fixParents();
return;
}
{
Set<TypeVariable> set = new TreeSet<TypeVariable>(parents);
parents = Collections.unmodifiableList(new LinkedList<TypeVariable>(set));
}
}
public void fixChildren()
{
if(rep != this)
{
ecr().fixChildren();
return;
}
{
Set<TypeVariable> set = new TreeSet<TypeVariable>(children);
children = Collections.unmodifiableList(new LinkedList<TypeVariable>(set));
}
}
}