Mirror of the JODE repository
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.
 
 
 
 
 
 
jode/jode/src/net/sf/jode/type/Type.java

741 lines
23 KiB

/* Type Copyright (C) 1998-2005 Jochen Hoenicke.
*
* This program 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, 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; see the file COPYING.LESSER. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package net.sf.jode.type;
import net.sf.jode.GlobalOptions;
import net.sf.jode.bytecode.ClassPath;
import net.sf.jode.bytecode.ClassInfo;
import net.sf.jode.bytecode.TypeSignature;
import net.sf.jode.decompiler.ClassDeclarer;
import net.sf.jode.util.UnifyHash;
///#def COLLECTIONS java.util
import java.util.Iterator;
import java.util.Arrays;
///#enddef
/**
* This is my type class. It differs from java.lang.class, in that it
* represents a set of types. Since most times this set is infinite, it
* needs a special representation. <br>
*
* The main operation on a type sets are tSuperType, tSubType and
* intersection.
*
* @author Jochen Hoenicke */
public class Type {
public static final int TC_BOOLEAN = 0;
public static final int TC_BYTE = 1;
public static final int TC_CHAR = 2;
public static final int TC_SHORT = 3;
public static final int TC_INT = 4;
public static final int TC_LONG = 5;
public static final int TC_FLOAT = 6;
public static final int TC_DOUBLE = 7;
public static final int TC_NULL = 8;
public static final int TC_ARRAY = 9;
public static final int TC_CLASS = 10;
public static final int TC_VOID = 11;
public static final int TC_METHOD = 12;
public static final int TC_ERROR = 13;
public static final int TC_UNKNOWN = 101;
public static final int TC_RANGE = 103;
public static final int TC_INTEGER = 107;
public static final int TC_SYSCLASS = 108;
public static final int TC_CLASSIFACE = 109;
public static final int TC_PARAMETER = 110;
private static final UnifyHash classHash = new UnifyHash();
private static final UnifyHash arrayHash = new UnifyHash();
/**
* This type represents the singleton set containing the boolean type.
*/
public static final Type tBoolean = new IntegerType(IntegerType.IT_Z);
/**
* This type represents the singleton set containing the byte type.
*/
public static final Type tByte = new IntegerType(IntegerType.IT_B);
/**
* This type represents the singleton set containing the char type.
*/
public static final Type tChar = new IntegerType(IntegerType.IT_C);
/**
* This type represents the singleton set containing the short type.
*/
public static final Type tShort = new IntegerType(IntegerType.IT_S);
/**
* This type represents the singleton set containing the int type.
*/
public static final Type tInt = new IntegerType(IntegerType.IT_I);
/**
* This type represents the singleton set containing the long type.
*/
public static final Type tLong = new Type(TC_LONG);
/**
* This type represents the singleton set containing the float type.
*/
public static final Type tFloat = new Type(TC_FLOAT);
/**
* This type represents the singleton set containing the double type.
*/
public static final Type tDouble = new Type(TC_DOUBLE);
/**
* This type represents the void type. It is really not a type at
* all.
*/
public static final Type tVoid = new Type(TC_VOID);
/**
* This type represents the empty set, and probably means, that something
* has gone wrong.
*/
public static final Type tError = new Type(TC_ERROR);
/**
* This type represents the set of all possible types.
*/
public static final Type tUnknown = new Type(TC_UNKNOWN);
/**
* This type represents the set of all integer types, up to 32 bit.
*/
public static final Type tUInt = new IntegerType(IntegerType.IT_I
| IntegerType.IT_B
| IntegerType.IT_C
| IntegerType.IT_S);
/**
* This type represents the set of the boolean and int type.
*/
public static final Type tBoolInt = new IntegerType(IntegerType.IT_I
| IntegerType.IT_Z);
/**
* This type represents the set of boolean and all integer types,
* up to 32 bit.
*/
public static final Type tBoolUInt= new IntegerType(IntegerType.IT_I
| IntegerType.IT_B
| IntegerType.IT_C
| IntegerType.IT_S
| IntegerType.IT_Z);
/**
* This type represents the set of the boolean and byte type.
*/
public static final Type tBoolByte= new IntegerType(IntegerType.IT_B
| IntegerType.IT_Z);
public final static ClassType[] EMPTY_IFACES = new ClassType[0];
/**
* This type represents the singleton set containing
* <code>java.lang.Object</code>.
*/
public static final SystemClassType tObject =
tSystemClass("java.lang.Object",
null, EMPTY_IFACES, false, false);
/**
* This type represents the singleton set containing the special
* null type (the type of null).
*/
public static final ReferenceType tNull = new NullType();
/**
* This type represents the set of all reference types, including
* class types, array types, interface types and the null type.
*/
public static final Type tUObject = tRange(tObject, tNull);
/**
* This type represents the singleton set containing
* <code>java.lang.Comparable</code>.
*/
public static final SystemClassType tSerializable =
tSystemClass("java.io.Serializable",
null, EMPTY_IFACES, false, true);
/**
* This type represents the singleton set containing
* <code>java.lang.Comparable</code>.
*/
public static final SystemClassType tCloneable =
tSystemClass("java.lang.Cloneable",
null, EMPTY_IFACES, false, true);
static final ClassType[] arrayIfaces = {
tCloneable, tSerializable
};
/**
* This type represents the singleton set containing
* <code>java.lang.String</code>.
*/
/*FIXME */
public static final ClassType tString =
tClass(new ClassPath("reflection:"), "java/lang/String");
public static final ClassType tStringBuffer =
tClass(new ClassPath("reflection:"), "java/lang/StringBuffer");
public static final ClassType tStringBuilder =
tClass(new ClassPath("reflection:"), "java/lang/StringBuilder");
/**
* Generate the singleton set of the type represented by the given
* string.
* @param classpath the current classpath.
* @param type the type signature (or method signature).
* @deprecated give a classdeclarer instead.
* @return a singleton set containing the given type.
*/
public static final Type tType(ClassPath cp, String type) {
return tType(cp, null, type);
}
/**
* Generate the singleton set of the type represented by the given
* string.
* @param declarer a class declarer for the current context.
* @param type the type signature (or method signature).
* @return a singleton set containing the given type.
*/
public static final Type tType(ClassDeclarer declarer,
String signature) {
return tType(declarer.getClassPath(), null, signature);
}
public static final ClassInfoType tClassSig(ClassPath cp, GenericDeclarer declarer,
String signature)
{
Type[] generics = null;
ClassInfoType outer = null;
int index = signature.indexOf('.');
if (index >= 0) {
outer = tClassSig(cp, declarer, signature.substring(0, index));
signature = signature.substring(index+1);
if (outer == null)
return null;
}
index = signature.indexOf('<');
if (index >= 0) {
/* parse parameter types */
String[] genericNames = TypeSignature
.getArgumentTypes(signature.substring(index));
signature = signature.substring(0, index);
generics = new Type[genericNames.length];
for (int i = 0; i < generics.length; i++) {
String name = genericNames[i];
char c = name.charAt(0);
if (c == '*')
generics[i] = Type.tUObject;
else if (c == '+')
generics[i] = Type.tType(cp, declarer, name.substring(1))
.getSubType();
else if (c == '-')
generics[i] = Type.tType(cp, declarer, name.substring(1))
.getSuperType();
else
generics[i] = Type.tType(cp, declarer, name);
}
}
if (outer != null) {
ClassInfo[] inner = outer.getClassInfo().getClasses();
for (int i = 0; i < inner.length; i++) {
if (inner[i].getClassName().equals(signature))
return tClass(inner[i], generics, outer);
}
System.err.println("Inner class "+signature+" in class "+outer
+" not found.");
return null;
}
return tClass(cp, signature, generics);
}
/**
* Generate the singleton set of the type represented by the given
* string.
* @param cp current classpath.
* @param genericType the class containing generics of current context.
* @param type the type signature (or method signature).
* @return a singleton set containing the given type.
*/
public static final Type tType(ClassPath cp, GenericDeclarer declarer,
String signature) {
if (signature == null || signature.length() == 0)
return tError;
switch(signature.charAt(0)) {
case 'Z':
return tBoolean;
case 'B':
return tByte;
case 'C':
return tChar;
case 'S':
return tShort;
case 'I':
return tInt;
case 'F':
return tFloat;
case 'J':
return tLong;
case 'D':
return tDouble;
case 'V':
return tVoid;
case '[':
return tArray(tType(cp, declarer, signature.substring(1)));
case 'L': {
int endIndex = signature.length()-1;
if (signature.charAt(endIndex) != ';')
return tError;
Type type = tClassSig(cp, declarer,
signature.substring(1, endIndex));
if (type == null)
return tError;
return type;
}
case 'T': {
int index = signature.indexOf(';');
if (index != signature.length()-1)
return tError;
if (declarer == null)
return tUnknown; /* XXX: Should be a type variable */
Type type = declarer.getGeneric(signature.substring(1, index));
if (type == null)
return tError;
return type;
}
}
throw new InternalError("Unknown type signature: "+signature);
}
/**
* Generate the singleton set of the type represented by the given
* class name.
* @param className the full qualified name of the class.
* The packages may be separated by `.' or `/'.
* @return a singleton set containing the given type.
*/
public static final ClassInfoType tClass(ClassPath classPath,
String className,
Type[] generics) {
return tClass(classPath.getClassInfo(className.replace('/','.')),
generics);
}
/**
* @deprecated
*/
public static final ClassType tClass(ClassPath classPath,
String className){
return tClass(classPath, className, null);
}
/**
* Generate the singleton set of the type represented by the given
* class name.
* @param clazzname the interned full qualified name of the class.
* The packages mus be separated by `.'.
* @return a singleton set containing the given type.
*/
public static final SystemClassType tSystemClass
(String clazzName, ClassType superClass, ClassType[] ifaces,
boolean isFinal, boolean isInterface) {
return new SystemClassType(clazzName, superClass, ifaces,
isFinal, isInterface);
}
/**
* Generate the singleton set of the type represented by the given
* class info.
* @param clazzinfo the net.sf.jode.bytecode.ClassInfo.
* @return a singleton set containing the given type.
*/
public static final ClassInfoType tClass(ClassInfo clazzinfo,
Type[] generics,
ClassInfoType outer) {
int hash = clazzinfo.hashCode();
if (outer != null)
hash = hash * 11 + outer.hashCode();
if (generics != null) {
for (int i = 0; i < generics.length; i++)
hash = hash * 11 + generics[i].hashCode();
}
Iterator iter = classHash.iterateHashCode(hash);
while (iter.hasNext()) {
ClassInfoType type = (ClassInfoType) iter.next();
if (type.getClassInfo() == clazzinfo
&& type.outerClass == outer
&& Arrays.equals(generics, type.genericInstances))
return type;
}
ClassInfoType type = new ClassInfoType(clazzinfo, generics, outer);
classHash.put(hash, type);
return type;
}
/**
* Generate the singleton set of the type represented by the given
* class info.
* @param clazzinfo the net.sf.jode.bytecode.ClassInfo.
* @return a singleton set containing the given type.
*/
public static final ClassInfoType tClass(ClassInfo clazzinfo,
Type[] generics) {
return tClass(clazzinfo, generics, null);
}
/**
* Generate the singleton set of the type represented by the given
* class info.
* @param clazzinfo the net.sf.jode.bytecode.ClassInfo.
* @return a singleton set containing the given type.
* deprecated This should be removed if generics work.
*/
public static final ClassInfoType tClass(ClassInfo clazzinfo) {
return tClass(clazzinfo, null);
}
/**
* Generate/look up the set of the array type whose element types
* are in the given type set.
* @param type the element types (which may be the empty set tError).
* @return the set of array types (which may be the empty set tError).
*/
public static final Type tArray(Type type) {
if (type == tError)
return type;
int hash = type.hashCode();
Iterator iter = arrayHash.iterateHashCode(hash);
while (iter.hasNext()) {
ArrayType arrType = (ArrayType) iter.next();
if (arrType.getElementType().equals(type))
return arrType;
}
ArrayType arrType = new ArrayType(type);
arrayHash.put(hash, arrType);
return arrType;
}
/**
* Generate/look up the method type for the given signature
* @param signature the method descriptor.
* @return a method type (a singleton set).
*/
public static MethodType tMethod(ClassPath cp, GenericDeclarer declarer,
String signature, Type[] generics) {
return new MethodType(cp, declarer, signature, generics);
}
/**
* Generate the range type from bottom to top. This should
* represent all reference types, that can be casted to bottom by
* a widening cast and where top can be casted to. You should not
* use this method directly; use tSubType, tSuperType and
* intersection instead, which is more general.
* @param bottom the bottom type.
* @param top the top type.
* @return the range type.
*/
public static final Type tRange(ReferenceType bottom,
ReferenceType top) {
return new RangeType(bottom, top);
}
/**
* Generate the set of types, to which one of the types in type can
* be casted to by a widening cast. The following holds:
* <ul><li>tSuperType(tObject) = tObject </li>
* <li>tSuperType(tError) = tError </li>
* <li>type.intersection(tSuperType(type)).equals(type)
* (this means type is a subset of tSuperType(type).</li>
* <li>tSuperType(tNull) = tUObject</li>
* <li>tSuperType(tChar) = {tChar, tInt } </li></ul>
* @param type a set of types.
* @return the super types of type.
*/
public static Type tSuperType(Type type) {
return type.getSuperType();
}
/**
* Generate the set of types, which can be casted to one of the
* types in type by a widening cast. The following holds:
* <ul><li>tSubType(tObject) = tUObject </li>
* <li>tSubType(tError) = tError </li>
* <li>type.intersection(tSubType(type)).equals(type)
* (this means type is a subset of tSubType(type).</li>
* <li>tSubType(tNull) = tNull</li>
* <li>tSubType({tBoolean, tShort}) = { tBoolean, tByte, tShort }</li></ul>
* @param type a set of types.
* @return the sub types of type.
*/
public static Type tSubType(Type type) {
return type.getSubType();
}
/**
* The typecode of this type. This should be one of the TC_ constants.
*/
final int typecode;
/**
* Create a new type with the given type code.
*/
protected Type(int tc) {
typecode = tc;
}
/**
* The sub types of this type.
* @return tSubType(this).
*/
public Type getSubType() {
return this;
}
/**
* The super types of this type.
* @return tSuperType(this).
*/
public Type getSuperType() {
return this;
}
/**
* Returns the hint type of this type set. This returns the singleton
* set containing only the `most likely' type in this set. This doesn't
* work for <code>tError</code> or <code>tUnknown</code>, and may lead
* to errors for certain range types.
* @return the hint type.
*/
public Type getHint() {
return getCanonic();
}
/**
* Returns the canonic type of this type set. The intention is, to
* return for each expression the type, that the java compiler would
* assign to this expression.
* @return the canonic type.
*/
public Type getCanonic() {
return this;
}
/**
* Returns the type code of this type. Don't use this; it is
* merily needed by the sub types (and the bytecode verifier, which
* has its own type merging methods).
* @return the type code of the type.
*/
public final int getTypeCode() {
return typecode;
}
/**
* Returns the number of stack/local entries an object of this type
* occupies.
* @return 0 for tVoid, 2 for tDouble and tLong and
* 1 for every other type.
*/
public int stackSize()
{
switch(typecode) {
case TC_VOID:
return 0;
case TC_ERROR:
default:
return 1;
case TC_DOUBLE:
case TC_LONG:
return 2;
}
}
/**
* Returns true, if all types in this type set are a super type of
* at least one type in the type set given as parameter.
*/
public boolean isSuperTypeOf(Type type) {
return this == type;
}
/**
* Returns true, if all types in this type are possibly super
* types of the given type. If we don't have the full hierarchy
* of this type, assume it is.
*/
public boolean maybeSuperTypeOf(ClassType type) {
return isSuperTypeOf(type);
}
/**
* Intersect this set of types with another type set and return the
* intersection.
* @param type the other type set.
* @return the intersection, tError, if the intersection is empty.
*/
public Type intersection(Type type) {
if (this == tError || type == tError)
return tError;
if (this == tUnknown)
return type;
if (type == tUnknown || this == type)
return this;
/* We have two different singleton sets now.
*/
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_TYPES) != 0)
GlobalOptions.err.println("intersecting "+ this +" and "+ type
+ " to <error>");
return tError;
}
/**
* Checks if we need to cast to a middle type, before we can cast from
* fromType to this type. For example it is impossible to cast a
* String to a StringBuffer, but if we cast to Object in between this
* is allowed (it doesn't make much sense though).
* @return the middle type, or null if it is not necessary.
*/
public Type getCastHelper(Type fromType) {
return null;
}
/**
* Checks if this type represents a valid singleton type.
*/
public boolean isValidType() {
return typecode <= TC_DOUBLE;
}
/**
* Checks if this is a class or array type (but not a null type).
* @XXX remove this?
* @return true if this is a class or array type.
*/
public boolean isClassType() {
return false;
}
/**
* Check if this type set and the other type set are not disjunct.
* @param type the other type set.
* @return true if this they aren't disjunct.
*/
public boolean isOfType(Type type) {
return this.intersection(type) != Type.tError;
}
/**
* Check if this type set contains the given class.
* @param clazz the class to check.
* @return true if clazz is contained in this type.
*/
public boolean containsClass(ClassInfo clazz) {
return false;
}
/**
* Generates the default name, that is the `natural' choice for
* local of this type.
* @return the default name of a local of this type.
*/
public String getDefaultName() {
switch (typecode) {
case TC_LONG:
return "l";
case TC_FLOAT:
return "f";
case TC_DOUBLE:
return "d";
default:
return "local";
}
}
/**
* Generates the default value, that is the initial value of a field
* of this type.
* @return the default value of a field of this type.
*/
public Object getDefaultValue() {
switch (typecode) {
case TC_LONG:
return new Long(0);
case TC_FLOAT:
return new Float(0);
case TC_DOUBLE:
return new Double(0);
default:
return null;
}
}
/**
* Returns the type signature of this type. You should only call
* this on singleton types.
* @return the type (or method) signature of this type.
*/
public String getTypeSignature() {
switch (typecode) {
case TC_LONG:
return "J";
case TC_FLOAT:
return "F";
case TC_DOUBLE:
return "D";
default:
return "?";
}
}
/**
* Returns the java.lang.Class representing this type. You should
* only call this on singleton types.
* @return the Class object representing this type.
*/
public Class getTypeClass() throws ClassNotFoundException {
switch (typecode) {
case TC_LONG:
return Long.TYPE;
case TC_FLOAT:
return Float.TYPE;
case TC_DOUBLE:
return Double.TYPE;
default:
throw new InternalError("getTypeClass() called on illegal type");
}
}
/**
* Returns a string representation describing this type set.
* @return a string representation describing this type set.
*/
public String toString() {
switch (typecode) {
case TC_LONG:
return "long";
case TC_FLOAT:
return "float";
case TC_DOUBLE:
return "double";
case TC_NULL:
return "null";
case TC_VOID:
return "void";
case TC_UNKNOWN:
return "<unknown>";
case TC_ERROR:
default:
return "<error>";
}
}
}