Mirror of the BLOAT repository https://www.cs.purdue.edu/homes/hosking/bloat/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
bloat/src/EDU/purdue/cs/bloat/editor/ClassHierarchy.java

1270 lines
32 KiB

/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.editor;
import java.io.*;
import java.util.*;
import EDU.purdue.cs.bloat.reflect.*;
import EDU.purdue.cs.bloat.util.*;
/**
* ClassHierarchy maintains a graph of the subclass relationships of the classes
* loaded by the ClassInfoLoader.
*
* @see ClassInfoLoader
*/
public class ClassHierarchy {
public static final Type POS_SHORT = Type.getType("L+short!;");
public static final Type POS_BYTE = Type.getType("L+byte!;");
static final int MAX_INT = 8;
static final int MAX_SHORT = 7;
static final int MAX_CHAR = 6;
static final int MAX_BYTE = 5;
static final int MAX_BOOL = 4;
static final int MIN_CHAR = 3;
static final int MIN_BOOL = 3;
static final int ZERO = 3;
static final int MIN_BYTE = 2;
static final int MIN_SHORT = 1;
static final int MIN_INT = 0;
public static boolean DEBUG = false;
public static boolean RELAX = false;
Set classes; // The Types of the classes in hierarchy
Graph extendsGraph; // "Who extends who" graph
Graph implementsGraph; // "Who implements what" graph
boolean closure; // Do we visit all referenced classes?
// Maps methods to the methods they may resolve to
private Map resolvesToCache;
// These are only needed during construction.
EditorContext context;
LinkedList worklist;
Set inWorklist;
private void db(final String s) {
if (ClassHierarchy.DEBUG) {
System.out.println(s);
}
}
/**
* Constructor.
*
* @param context
* The context in which to access an <Tt>Editor</tt> and other
* such things.
* @param initial
* The names of the classes that initially constitue the
* hierarchy.
* @param closure
* Do we get the maximum amount of class information?
*/
public ClassHierarchy(final EditorContext context,
final Collection initial, final boolean closure) {
this.context = context;
this.closure = closure;
classes = new HashSet();
extendsGraph = new Graph();
implementsGraph = new Graph();
worklist = new LinkedList();
inWorklist = new HashSet();
this.resolvesToCache = new HashMap();
// Need new ArrayList to avoid ConcurrentModificationException
final Iterator iter = new ArrayList(initial).iterator();
while (iter.hasNext()) {
final String name = (String) iter.next();
addClass(name);
}
}
/**
* Adds a class of a given name to the ClassHierarchy.
*/
public void addClassNamed(final String name) {
addClass(name);
}
/**
* Returns the immediate subclasses of a given <tt>Type</tt> as a
* <tt>Collection</tt> of <tt>Type</tt>s.
*
* <p>
*
* The subclass relationship at the classfile level is a little screwy with
* respect to interfaces. An interface that extends another interface is
* compiled into an interface that extends java.lang.Object and implements
* the superinterface. As a result, the interface-subinterface is not
* captured in <tt>subclasses</tt> as one may expect. Instead, you have to
* look at <tt>implementors</tt> and filter out the classes.
*/
public Collection subclasses(final Type type) {
final TypeNode node = getExtendsNode(type);
if (node != null) {
final List list = new ArrayList(extendsGraph.preds(node));
final ListIterator iter = list.listIterator();
while (iter.hasNext()) {
final TypeNode v = (TypeNode) iter.next();
iter.set(v.type);
}
return list;
}
return new ArrayList();
}
/**
* Returns the superclass of a given <tt>Type</tt>. If the <tt>Type</tt>
* has no superclass (that is it is <tt>Type.OBJECT</tt>), then null is
* returned.
*/
public Type superclass(final Type type) {
final TypeNode node = getExtendsNode(type);
if (node != null) {
final Collection succs = extendsGraph.succs(node);
final Iterator iter = succs.iterator();
if (iter.hasNext()) {
final TypeNode v = (TypeNode) iter.next();
return v.type;
}
}
return null;
}
/**
* Returns the interfaces that a given <tt>Type</tt> implements as a
* <tt>Collection</tt> of <tt>Types</tt>
*/
public Collection interfaces(final Type type) {
final TypeNode node = getImplementsNode(type);
if (node != null) {
final List list = new ArrayList(implementsGraph.succs(node));
final ListIterator iter = list.listIterator();
while (iter.hasNext()) {
final TypeNode v = (TypeNode) iter.next();
iter.set(v.type);
}
return list;
}
return new ArrayList();
}
/**
* Returns the classes (<tt>Type</tt>s) that implement a given interface
* as a <tt>Collection</tt> of <Tt>Type</tt>s.
*
* <p>
*
* See note in <tt>subclasses</tt> for information about the interface
* hierarchy.
*/
public Collection implementors(final Type type) {
final TypeNode node = getImplementsNode(type);
if (node != null) {
final List list = new ArrayList(implementsGraph.preds(node));
final ListIterator iter = list.listIterator();
while (iter.hasNext()) {
final TypeNode v = (TypeNode) iter.next();
iter.set(v.type);
}
return list;
}
return new ArrayList();
}
/**
* Returns whether or not a is a subclass of b.
*/
public boolean subclassOf(final Type a, final Type b) {
// Is a <= b?
Assert.isTrue(a.isReference() && b.isReference(), "Cannot compare " + a
+ " and " + b);
// a <= a: true
if (a.equals(b)) {
return true;
}
// a <= java.lang.Object: true
if (b.equals(Type.OBJECT)) {
return true;
}
// null <= null: true
// a <= null: false
if (b.isNull()) {
return a.isNull();
}
if (a.isArray()) {
if (b.isArray()) {
// Both reference arrays.
// a <= b -> a[] <= b[]
if (a.elementType().isReference()
&& b.elementType().isReference()) {
return subclassOf(a.elementType(), b.elementType());
}
// a[] <= a[]: true
return a.elementType().equals(b.elementType());
}
// Only one is an array (and b is not Object--tested above).
return false;
}
// a <= b[]: false
if (b.isArray()) {
// Only one is an array.
return false;
}
// Neither is an array. Look at all of the superclasses of a. If
// one of those superclasses is b, then a is a subclass of b.
for (Type t = a; t != null; t = superclass(t)) {
if (t.equals(b)) {
return true;
}
}
return false;
}
/**
* Returns (the <tt>Type</tt>s of) all of the classes and interfaces in
* the hierarchy.
*/
public Collection classes() {
Assert.isTrue(classes != null);
return classes;
}
/**
* Returns <tt>true</tt> if class closure has been computed
*/
public boolean closure() {
return (this.closure);
}
/**
* Obtains a node from the extends graph. If it is not in the graph, we try
* to "bring it in".
*/
private TypeNode getExtendsNode(final Type type) {
final GraphNode node = extendsGraph.getNode(type);
if ((node == null) && type.isObject()) {
this.addClassNamed(type.className());
}
return ((TypeNode) extendsGraph.getNode(type));
}
/**
* Obtains a node from the class graph. If it is not in the graph, we try to
* "bring it in".
*/
private TypeNode getImplementsNode(final Type type) {
final GraphNode node = implementsGraph.getNode(type);
if ((node == null) && type.isObject()) {
this.addClassNamed(type.className());
}
return ((TypeNode) implementsGraph.getNode(type));
}
/**
* Adds a type (and all types it references) to the extends and implements
* graphs.
*/
private void addClass(final String name) {
Type type = Type.getType(Type.classDescriptor(name));
if (classes.contains(type)) {
return;
}
if (inWorklist.contains(type)) {
return;
}
db("ClassHierarchy: Adding " + name + " to hierarchy");
worklist.add(type);
inWorklist.add(type);
while (!worklist.isEmpty()) {
type = (Type) worklist.removeFirst();
inWorklist.remove(type);
if (classes.contains(type)) {
continue;
}
classes.add(type);
// Add a node in the extends graph for the type of interest
TypeNode extendsNode = getExtendsNode(type);
if (extendsNode == null) {
// Add a new node to the class graph
extendsNode = new TypeNode(type);
extendsGraph.addNode(type, extendsNode);
}
// TypeNode implementsNode = (TypeNode) getImplementsNode(type);
// if (implementsNode == null) {
// // Add a new node to the interface graph
// implementsNode = new TypeNode(type);
// implementsGraph.addNode(type, implementsNode);
// }
// Obtain a ClassEditor for the class
ClassEditor c;
try {
c = context.editClass(type.className());
} catch (final ClassNotFoundException ex) {
if (ClassHierarchy.RELAX) {
continue;
}
throw new RuntimeException("Class not found: "
+ ex.getMessage());
}
final Type[] interfaces = c.interfaces();
if (c.superclass() != null) {
// Add an edge from the superclass to the class in the extends
// graph.
if (!c.isInterface() || (interfaces.length == 0)) {
// Ignore interfaces that implement (really extend, see
// below) other interfaces. This way interfaces are put in
// the extends graph twice.
TypeNode superNode = getExtendsNode(c.superclass());
if (superNode == null) {
superNode = new TypeNode(c.superclass());
extendsGraph.addNode(c.superclass(), superNode);
}
// Make sure that we're not making java.lang.Object a
// superclass of itself. We assume that the java.lang.Object
// has no successors in the extendsGraph.
if (!extendsNode.type.equals(Type.OBJECT)) {
extendsGraph.addEdge(extendsNode, superNode);
}
}
} else {
// Only java.lang.Object has no superclass
if (!type.equals(Type.OBJECT) && !ClassHierarchy.RELAX) {
throw new RuntimeException("Null superclass for " + type);
}
}
// Consider the interfaces c implements
if (c.isInterface()) {
// Interfaces that extend other interfaces are compiled into
// classes that implement those other interfaces. So,
// interfaces that implement other interfaces really extend
// them. Yes, this makes the extends graph an inverted tree.
for (int i = 0; i < interfaces.length; i++) {
final Type iType = interfaces[i];
TypeNode iNode = getExtendsNode(iType);
if (iNode == null) {
iNode = new TypeNode(iType);
extendsGraph.addNode(iType, iNode);
}
extendsGraph.addEdge(extendsNode, iNode);
}
} else {
// Class c implements its interfaces
TypeNode implementsNode = null;
if (interfaces.length > 0) {
implementsNode = getImplementsNode(type);
if (implementsNode == null) {
implementsNode = new TypeNode(type);
implementsGraph.addNode(type, implementsNode);
}
}
for (int i = 0; i < interfaces.length; i++) {
final Type iType = interfaces[i];
TypeNode iNode = getImplementsNode(iType);
if (iNode == null) {
iNode = new TypeNode(iType);
implementsGraph.addNode(iType, iNode);
}
implementsGraph.addEdge(implementsNode, iNode);
}
}
if (c.superclass() != null) {
// Add the super type to the worklist
// db("typeref " + type + " -> " + c.superclass());
addType(c.superclass());
}
for (int i = 0; i < c.interfaces().length; i++) {
// Add all of the interface types to the worklist
// db("typeref " + type + " -> " + c.interfaces()[i]);
addType(c.interfaces()[i]);
}
if (!this.closure) {
context.release(c.classInfo());
continue;
}
for (int i = 0; i < c.methods().length; i++) {
// TODO: Add an enumeration to ClassEditor to get this list.
// Add all of the methods types. Actually, we only add the
// type involved with the methods.
final MethodInfo m = c.methods()[i];
final int typeIndex = m.typeIndex();
final String desc = (String) c.constants()
.constantAt(typeIndex);
final Type t = Type.getType(desc);
// db("typeref " + type + " -> " + t);
addType(t);
}
for (int i = 0; i < c.fields().length; i++) {
// Add the types of all of the fields
final FieldInfo f = c.fields()[i];
final int typeIndex = f.typeIndex();
final String desc = (String) c.constants()
.constantAt(typeIndex);
final Type t = Type.getType(desc);
// db("typeref " + type + " -> " + t);
addType(t);
}
for (int i = 0; i < c.constants().numConstants(); i++) {
// Look through the constant pool for interesting (non-LONG
// and non-DOUBLE) constants and add them to the worklist.
final int tag = c.constants().constantTag(i);
if ((tag == Constant.LONG) || (tag == Constant.DOUBLE)) {
i++;
} else if (tag == Constant.CLASS) {
final Type t = (Type) c.constants().constantAt(i);
// db("typeref " + type + " -> " + t);
addType(t);
} else if (tag == Constant.NAME_AND_TYPE) {
final NameAndType t = (NameAndType) c.constants()
.constantAt(i);
// db("typeref " + type + " -> " + t.type());
addType(t.type());
}
}
// We're done editing the class
context.release(c.classInfo());
}
/*
* // Now that we've entered the class (and at least all of its //
* subclasses) into the hierarchy, notify superclasses that // they've
* been subclassses. This will invalidate the TypeNodes // in the
* dependence graph. DependenceGraph dg =
* BloatContext.getContext().dependenceGraph();
*
* for(Type superclass = superclass(type); superclass != null;
* superclass = superclass(superclass)) { db("ClassHierarchy:
* Invalidating superclass " + superclass);
*
* EDU.purdue.cs.bloat.depend.TypeNode typeNode =
* dg.getTypeNode(superclass); typeNode.invalidate(); }
*/
}
// Adds a Type to the worklist. If the type is a method, then all
// of the parameters types and the return types are added.
private void addType(final Type type) {
if (type.isMethod()) {
// Add all of the types of the parameters and the return type
final Type[] paramTypes = type.paramTypes();
for (int i = 0; i < paramTypes.length; i++) {
// db("typeref " + type + " -> " + paramTypes[i]);
addType(paramTypes[i]);
}
final Type returnType = type.returnType();
// db("typeref " + type + " -> " + returnType);
addType(returnType);
return;
}
if (type.isArray()) {
// TODO: Add the supertypes of the array and make it implement
// SERIALIZABLE and CLONEABLE. Since we're only concerned with
// fields and since arrays have no fields, we can ignore this
// for now.
// db("typeref " + type + " -> " + type.elementType());
addType(type.elementType());
return;
}
if (!type.isObject()) {
return;
}
if (classes.contains(type)) {
return;
}
if (inWorklist.contains(type)) {
return;
}
worklist.add(type);
inWorklist.add(type);
}
/**
* Returns the intersection of two types. Basically, the interstion of two
* types is the type (if any) to which both types may be assigned. So, if a
* is a subtype of b, a is returned. Otherwise, <tt>Type.NULL</tt> is
* returned.
*/
public Type intersectType(final Type a, final Type b) {
Assert.isTrue(a.isReference() && b.isReference(), "Cannot intersect "
+ a + " and " + b);
if (a.equals(b)) {
return a;
}
if (a.isNull() || b.isNull()) {
return Type.NULL;
}
if (a.equals(Type.OBJECT)) {
return b;
}
if (b.equals(Type.OBJECT)) {
return a;
}
if (a.isArray()) {
if (b.isArray()) {
// Both reference arrays.
if (a.elementType().isReference()
&& b.elementType().isReference()) {
final Type t = intersectType(a.elementType(), b
.elementType());
if (t.isNull()) {
return Type.NULL;
}
return t.arrayType();
}
// Only one is a reference array.
if (a.elementType().isReference()
|| b.elementType().isReference()) {
return Type.NULL;
}
// Both primitive arrays, not equal.
return Type.NULL;
}
// Only one is an array.
return Type.NULL;
}
if (b.isArray()) {
// Only one is an array.
return Type.NULL;
}
// Neither is an array.
for (Type t = a; t != null; t = superclass(t)) {
if (t.equals(b)) {
// If a is a subtype of b, then return a.
return a;
}
}
for (Type t = b; t != null; t = superclass(t)) {
if (t.equals(a)) {
// If b is a subtype of a, then return b
return b;
}
}
return Type.NULL;
}
/**
* Returns the most refined common supertype for a bunch of <tt>Type</tt>s.
*/
public Type unionTypes(final Collection types) {
if (types.size() <= 0) {
return (Type.OBJECT);
}
final Iterator ts = types.iterator();
Type type = (Type) ts.next();
while (ts.hasNext()) {
type = this.unionType(type, (Type) ts.next());
}
return (type);
}
/**
* Returns the union of two types. The union of two types is their most
* refined common supertype. At worst, the union is <tt>Type.OBJECT</tt>
*/
public Type unionType(final Type a, final Type b) {
if (a.equals(b)) {
return a;
}
if (a.equals(Type.OBJECT) || b.equals(Type.OBJECT)) {
return Type.OBJECT;
}
if (a.isNull()) {
return b;
}
if (b.isNull()) {
return a;
}
// Handle funky integral types introduced during type inferencing.
if ((a.isIntegral() || a.equals(ClassHierarchy.POS_BYTE) || a
.equals(ClassHierarchy.POS_SHORT))
&& (b.isIntegral() || b.equals(ClassHierarchy.POS_BYTE) || b
.equals(ClassHierarchy.POS_SHORT))) {
final BitSet v1 = ClassHierarchy.typeToSet(a);
final BitSet v2 = ClassHierarchy.typeToSet(b);
v1.or(v2);
return (ClassHierarchy.setToType(v1));
}
Assert.isTrue(a.isReference() && b.isReference(), "Cannot union " + a
+ " and " + b);
if (a.isArray()) {
if (b.isArray()) {
// Both reference arrays.
if (a.elementType().isReference()
&& b.elementType().isReference()) {
final Type t = unionType(a.elementType(), b.elementType());
return t.arrayType();
}
// Only one is a reference array.
if (a.elementType().isReference()
|| b.elementType().isReference()) {
return Type.OBJECT;
}
// Both primitive arrays, not equal.
return Type.OBJECT;
}
// Only one is an array.
return Type.OBJECT;
}
if (b.isArray()) {
// Only one is an array.
return Type.OBJECT;
}
// Neither is an array.
final Set superOfA = new HashSet();
final Set superOfB = new HashSet();
for (Type t = a; t != null; t = superclass(t)) {
// if(TypeInference.DEBUG)
// System.out.println(" Superclass of " + a + " is " + t);
superOfA.add(t);
}
for (Type t = b; t != null; t = superclass(t)) {
if (superOfA.contains(t)) {
return t;
}
// if(TypeInference.DEBUG)
// System.out.println(" Superclass of " + b + " is " + t);
superOfB.add(t);
}
// if(TypeInference.DEBUG) {
// System.out.println("Superclasses of A: " + superOfA);
// System.out.println("Superclasses of B: " + superOfB);
// }
for (Type t = a; t != null; t = superclass(t)) {
if (superOfB.contains(t)) {
// Found a common superclass...
return t;
}
}
throw new RuntimeException("No common super type for " + a + " ("
+ superOfA + ")" + " and " + b + " (" + superOfB + ")");
}
class TypeNode extends GraphNode {
Type type;
public TypeNode(final Type type) {
// if(DEBUG)
// System.out.println("Creating TypeNode for: " + type);
this.type = type;
}
public String toString() {
return ("[" + type + "]");
}
}
/**
* Prints the class hierarchy (i.e. the "extends" hierarchy, interfaces may
* extends other interfaces) to a <tt>PrintWriter</tt>.
*/
public void printClasses(final PrintWriter out, final int indent) {
final TypeNode objectNode = this.getExtendsNode(Type.OBJECT);
indent(out, indent);
out.println(objectNode.type);
printSubclasses(objectNode.type, out, true, indent + 2);
}
/**
* Prints the implements hierarchy to a <tt>PrintWriter</tt>.
*/
public void printImplements(final PrintWriter out, int indent) {
// There are multiple roots to the implements graph.
indent += 2;
final Iterator roots = this.implementsGraph.roots().iterator();
while (roots.hasNext()) {
final TypeNode iNode = (TypeNode) roots.next();
indent(out, indent);
out.println(iNode.type);
printImplementors(iNode.type, out, true, indent + 2);
}
}
/**
* Print the implementors of a given interface. Do we even have more than
* one level?
*/
private void printImplementors(final Type iType, final PrintWriter out,
final boolean recurse, final int indent) {
final Iterator implementors = this.implementors(iType).iterator();
while (implementors.hasNext()) {
final Type implementor = (Type) implementors.next();
indent(out, indent);
out.println(implementor);
if (recurse) {
printImplementors(implementor, out, recurse, indent + 2);
}
}
}
/**
* Prints a bunch of spaces to a PrintWriter.
*/
private void indent(final PrintWriter out, final int indent) {
for (int i = 0; i < indent; i++) {
out.print(" ");
}
}
/**
* Prints the subclasses of a given to a PrintWriter.
*
* @param recurse
* Are all subclasses printed or only direct ones?
*/
private void printSubclasses(final Type classType, final PrintWriter out,
final boolean recurse, final int indent) {
final Iterator iter = this.subclasses(classType).iterator();
while (iter.hasNext()) {
final Type subclass = (Type) iter.next();
indent(out, indent);
out.println(subclass);
if (recurse) {
printSubclasses(subclass, out, recurse, indent + 2);
}
}
}
/**
* Determines whether or not a class's method is overriden by any of its
* subclasses.
*/
public boolean methodIsOverridden(final Type classType,
final NameAndType nat) {
final String methodName = nat.name();
final Type methodType = nat.type();
db("ClassHierarchy: Is " + classType + "." + methodName + methodType
+ " overridden?");
final Collection subclasses = this.subclasses(classType);
final Iterator iter = subclasses.iterator();
while (iter.hasNext()) {
final Type subclass = (Type) iter.next();
db("Examining subclass " + subclass);
// Obtain a ClassEditor for the class
ClassEditor ce = null;
try {
ce = context.editClass(subclass.className());
} catch (final ClassNotFoundException ex) {
db(ex.getMessage());
return (true);
}
// Examine each method in the subclass
final MethodInfo[] methods = ce.methods();
for (int i = 0; i < methods.length; i++) {
final MethodEditor me = context.editMethod(methods[i]);
if (me.name().equals(methodName)
&& me.type().equals(methodType)) {
db(" " + methodName + methodType + " is overridden by "
+ me.name() + me.type());
context.release(ce.classInfo());
return (true); // Method is overridden
}
}
// Recurse over subclasses
if (methodIsOverridden(subclass, nat)) {
context.release(ce.classInfo());
return (true);
}
context.release(ce.classInfo());
}
// Got through all subclasses and method was not overridden
db(" NO!");
return (false);
}
/**
* Returns the <tt>MemberRef</tt> of the method that would be invoked if a
* given method of a given type was invoked. Basically, dynamic dispatch is
* simulated.
*/
public MemberRef methodInvoked(final Type receiver, final NameAndType method) {
// Search up class hierarchy for a class that implements the
// method
for (Type type = receiver; type != null; type = superclass(type)) {
// Construct a MemberRef for the possible method
final MemberRef m = new MemberRef(type, method);
try {
context.editMethod(m);
return (m);
} catch (final NoSuchMethodException ex) {
continue; // Try superclass
}
}
// Hmm. No superclass method was found!
throw new IllegalArgumentException("No implementation of " + receiver
+ "." + method);
}
/**
* Given a set of bits representing the range of values some type has,
* determines what that Type is.
*/
public static Type setToType(final BitSet v) {
if (v.get(ClassHierarchy.MAX_INT)) {
return Type.INTEGER;
}
if (v.get(ClassHierarchy.MAX_CHAR)) {
if (v.get(ClassHierarchy.MIN_INT)
|| v.get(ClassHierarchy.MIN_SHORT)
|| v.get(ClassHierarchy.MIN_BYTE)) {
return Type.INTEGER;
} else {
return Type.CHARACTER;
}
}
if (v.get(ClassHierarchy.MAX_SHORT)) {
if (v.get(ClassHierarchy.MIN_INT)) {
return Type.INTEGER;
} else if (v.get(ClassHierarchy.MIN_SHORT)
|| v.get(ClassHierarchy.MIN_BYTE)) {
return Type.SHORT;
} else {
return ClassHierarchy.POS_SHORT;
}
}
if (v.get(ClassHierarchy.MAX_BYTE)) {
if (v.get(ClassHierarchy.MIN_INT)) {
return Type.INTEGER;
} else if (v.get(ClassHierarchy.MIN_SHORT)) {
return Type.SHORT;
} else if (v.get(ClassHierarchy.MIN_BYTE)) {
return Type.BYTE;
} else {
return ClassHierarchy.POS_BYTE;
}
}
if (v.get(ClassHierarchy.MAX_BOOL)) {
if (v.get(ClassHierarchy.MIN_INT)) {
return Type.INTEGER;
} else if (v.get(ClassHierarchy.MIN_SHORT)) {
return Type.SHORT;
} else if (v.get(ClassHierarchy.MIN_BYTE)) {
return Type.BYTE;
} else {
return Type.BOOLEAN;
}
}
if (v.get(ClassHierarchy.MIN_INT)) {
return Type.INTEGER;
}
if (v.get(ClassHierarchy.MIN_SHORT)) {
return Type.SHORT;
}
if (v.get(ClassHierarchy.MIN_BYTE)) {
return Type.BYTE;
}
return Type.BOOLEAN;
}
/**
* Returns a BitSet representing the possible values of a given integral
* type.
*/
public static BitSet typeToSet(final Type type) {
final BitSet v = new BitSet(ClassHierarchy.MAX_INT);
int lo;
int hi;
if (type.equals(Type.INTEGER)) {
lo = ClassHierarchy.MIN_INT;
hi = ClassHierarchy.MAX_INT;
} else if (type.equals(Type.CHARACTER)) {
lo = ClassHierarchy.MIN_CHAR;
hi = ClassHierarchy.MAX_CHAR;
} else if (type.equals(Type.SHORT)) {
lo = ClassHierarchy.MIN_SHORT;
hi = ClassHierarchy.MAX_SHORT;
} else if (type.equals(ClassHierarchy.POS_SHORT)) {
lo = ClassHierarchy.ZERO;
hi = ClassHierarchy.MAX_SHORT;
} else if (type.equals(Type.BYTE)) {
lo = ClassHierarchy.MIN_BYTE;
hi = ClassHierarchy.MAX_BYTE;
} else if (type.equals(ClassHierarchy.POS_BYTE)) {
lo = ClassHierarchy.ZERO;
hi = ClassHierarchy.MAX_BYTE;
} else if (type.equals(Type.BOOLEAN)) {
lo = ClassHierarchy.ZERO;
hi = ClassHierarchy.MAX_BOOL;
} else {
throw new RuntimeException();
}
for (int i = lo; i <= hi; i++) {
v.set(i);
}
return v;
}
/**
* Represents a method and a set of <tt>Type</tt>s. When the method is
* invoked on a receiver of any of these types, the method will resolve to
* that method.
*/
public class ResolvesToWith {
/**
* The method to which a call resolves
*/
public MemberRef method;
/**
* The types with which the call resolves to the above method
*/
public HashSet rTypes;
}
/**
* Returns a set of <tt>ResolvesToWith</tt> that represent all subclass
* methods that override a given method and the subclasses that when used as
* receivers resolve to that method.
*
* @see ResolvesToWith
*/
public Set resolvesToWith(final MemberRef method) {
Set resolvesTo = (Set) this.resolvesToCache.get(method);
if (resolvesTo == null) {
db("Resolving " + method);
resolvesTo = new HashSet(); // All methods it could resolve to
final ResolvesToWith rtw = new ResolvesToWith();
rtw.method = method;
rtw.rTypes = new HashSet();
// Remember that the method may be abstract, so the declaring
// class is not necessarily a resolving type.
// Basically, we go down the class and/or interface hierarchies
// looking for concrete implementations of this method.
MethodEditor me = null;
try {
me = context.editMethod(method);
} catch (final NoSuchMethodException ex1) {
// A method may not necessarily be implemented by its
// declaring class. For instance, an abstract class that
// implements an interface need not implement every method of
// the interface. Really?
db(" Hmm. Method is not implemented in declaring class");
}
// If the method is static or is a constructor, then it can only
// resolve to itself.
if ((me != null) && (me.isStatic() || me.isConstructor())) {
rtw.rTypes.add(method.declaringClass());
resolvesTo.add(rtw);
db(" Static method or constructor, resolves to itself");
} else {
// Now let's play with types. Examine every type that could
// implement this method. If it does, add it to the resolvesTo
// set. Make sure to take things like interfaces into account.
// When we find a overriding method, make a recursive call so
// we'll have that information in the cache.
final List types = new LinkedList();
types.add(method.declaringClass());
while (!types.isEmpty()) {
final Type type = (Type) types.remove(0);
db(" Examining type " + type);
ClassEditor ce = null;
try {
ce = context.editClass(type);
} catch (final ClassNotFoundException ex1) {
System.err.println("** Class not found: "
+ ex1.getMessage());
ex1.printStackTrace(System.err);
System.exit(1);
}
if (ce.isInterface()) {
// Consider all subinterfaces of this interface and all
// classes that implement this interface.
final Iterator subinterfaces = this.subclasses(type)
.iterator();
while (subinterfaces.hasNext()) {
final Type subinterface = (Type) subinterfaces
.next();
types.add(subinterface);
db(" Noting subinterface " + subinterface);
}
final Iterator implementors = this.implementors(type)
.iterator();
while (implementors.hasNext()) {
final Type implementor = (Type) implementors.next();
types.add(implementor);
db(" Noting implementor " + implementor);
}
} else {
// We've got a class. Does it override the method?
final NameAndType nat = method.nameAndType();
final MethodInfo[] methods = ce.methods();
boolean overridden = false;
for (int i = 0; i < methods.length; i++) {
final MethodEditor over = context
.editMethod(methods[i]);
final MemberRef ref = over.memberRef();
if (ref.nameAndType().equals(nat)) {
// This class implements the method.
if (!method.declaringClass().equals(type)) {
// Make a recursive call.
db(" Class " + type + " overrides "
+ method);
resolvesTo.addAll(resolvesToWith(ref));
overridden = true;
}
}
}
if (!overridden) {
db(" " + rtw.method + " called with " + type);
rtw.rTypes.add(type);
resolvesTo.add(rtw);
// Examine all subclasses of this class. They may
// override
// the method also.
final Iterator subclasses = this.subclasses(type)
.iterator();
while (subclasses.hasNext()) {
final Type subclass = (Type) subclasses.next();
types.add(subclass);
db(" Noting subclass " + subclass);
}
}
}
}
}
resolvesToCache.put(method, resolvesTo);
}
return (resolvesTo);
}
}