/* 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. * @deprecated use {@link soot.jimple.toolkits.typing.fast.TypeResolver} instead **/ class TypeVariableBV implements Comparable { private static final boolean DEBUG = false; private final int id; private final TypeResolverBV resolver; private TypeVariableBV rep = this; private int rank = 0; private TypeNode approx; private TypeNode type; private TypeVariableBV array; private TypeVariableBV element; private int depth; private BitVector parents = new BitVector(); private BitVector children = new BitVector(); private BitVector ancestors; private BitVector indirectAncestors; public TypeVariableBV(int id, TypeResolverBV resolver) { this.id = id; this.resolver = resolver; } public TypeVariableBV(int id, TypeResolverBV 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; } TypeVariableBV ecr = ((TypeVariableBV) obj).ecr(); if(ecr != this) { return false; } return true; } public int compareTo(Object o) { if(rep != this) { return ecr().compareTo(o); } return id - ((TypeVariableBV) o).ecr().id; } private TypeVariableBV ecr() { if(rep != this) { rep = rep.ecr(); } return rep; } public TypeVariableBV union(TypeVariableBV var) throws TypeException { if(rep != this) { return ecr().union(var); } TypeVariableBV y = var.ecr(); if(this == y) { parents.clear(var.ownId()); children.clear(var.ownId()); return this; } if(rank > y.rank) { resolver.invalidateId(y.id()); y.rep = this; merge(y); y.clear(); return this; } resolver.invalidateId(this.id()); 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(TypeVariableBV 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."); } parents.or(var.parents); parents.clear(var.ownId()); parents.clear(this.ownId()); children.or(var.children); children.clear(var.ownId()); children.clear(this.ownId()); } void validate() throws TypeException { if(rep != this) { ecr().validate(); return; } // Validate relations. if(type != null) { for(BitSetIterator i = parents.iterator(); i.hasNext();) { TypeVariableBV parent = resolver.typeVariableForId(i.next()).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(BitSetIterator i = children.iterator(); i.hasNext();) { TypeVariableBV child = resolver.typeVariableForId(i.next()).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(); } BitVector parentsToRemove = new BitVector(); for( BitSetIterator parentIt = parents.iterator(); parentIt.hasNext(); ) { final int parent = parentIt.next(); if(indirectAncestors.get(parent)) { parentsToRemove.set(parent); } } for(BitSetIterator i = parentsToRemove.iterator(); i.hasNext(); ) { removeParent(resolver.typeVariableForId(i.next())); } } private void fixAncestors() { BitVector ancestors = new BitVector(0); BitVector indirectAncestors = new BitVector(0); fixParents(); for(BitSetIterator i = parents.iterator(); i.hasNext();) { TypeVariableBV parent = resolver.typeVariableForId(i.next()).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; } private void fixParents() { if(rep != this) { ecr().fixParents(); } BitVector invalid = new BitVector(); invalid.or(parents); invalid.and(resolver.invalidIds()); for(BitSetIterator i = invalid.iterator(); i.hasNext();) { parents.set(resolver.typeVariableForId(i.next()).id()); } parents.clear(this.id); parents.clear(this.id()); parents.andNot(invalid); } public int id() { if(rep != this) { return ecr().id(); } return id; } public int ownId() { return id; } public void addParent(TypeVariableBV variable) { if(rep != this) { ecr().addParent(variable); return; } TypeVariableBV var = variable.ecr(); if(var == this) { return; } parents.set(var.id); var.children.set(this.id); } public void removeParent(TypeVariableBV variable) { if(rep != this) { ecr().removeParent(variable); return; } parents.clear(variable.id()); parents.clear(variable.ownId()); variable.children().clear(this.id); } public void addChild(TypeVariableBV variable) { if(rep != this) { ecr().addChild(variable); return; } TypeVariableBV var = variable.ecr(); if(var == this) { return; } children.set(var.id); parents.set(var.id); } public void removeChild(TypeVariableBV variable) { if(rep != this) { ecr().removeChild(variable); return; } TypeVariableBV var = variable.ecr(); children.clear(var.id); var.parents.clear(var.id); } 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 TypeVariableBV element() { if(rep != this) { return ecr().element(); } return (element == null) ? null : element.ecr(); } public TypeVariableBV array() { if(rep != this) { return ecr().array(); } return (array == null) ? null : array.ecr(); } public BitVector parents() { if(rep != this) { return ecr().parents(); } return parents; } public BitVector 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<TypeVariableBV> workList) throws TypeException { while(workList.size() > 0) { TypeVariableBV var = workList.first(); workList.remove(var); var.fixApprox(workList); } } private void fixApprox(TreeSet<TypeVariableBV> workList) throws TypeException { if(rep != this) { ecr().fixApprox(workList); return; } if(type == null && approx != resolver.hierarchy().NULL) { TypeVariableBV 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); } } } } TypeVariableBV 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(BitSetIterator i = parents.iterator(); i.hasNext();) { TypeVariableBV parent = resolver.typeVariableForId(i.next()).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(); TypeVariableBV 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(BitSetIterator i = parents.iterator(); i.hasNext(); ) { TypeVariableBV var = resolver.typeVariableForId(i.next()).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( BitSetIterator varIt = parents.iterator(); varIt.hasNext(); ) { final TypeVariableBV var = resolver.typeVariableForId(varIt.next()); removeParent(var); } } public String toString() { if(rep != this) { return ecr().toString(); } StringBuffer s = new StringBuffer(); s.append(",[parents:"); { boolean comma = false; for(BitSetIterator i = parents.iterator(); i.hasNext(); ) { if(comma) { s.append(","); } else { comma = true; } s.append(i.next()); } } s.append("],[children:"); { boolean comma = false; for(BitSetIterator i = children.iterator(); i.hasNext(); ) { if(comma) { s.append(","); } else { comma = true; } s.append(i.next()); } } s.append("]"); return "[id:" + id + ",depth:" + depth + ((type != null) ? (",type:" + type) : "") + ",approx:" + approx + s + (element == null ? "" : ",arrayof:" + element.id()) + "]"; } }