/* Type Copyright (C) 1998-1999 Jochen Hoenicke. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id$ */ package jode; import jode.bytecode.ClassInfo; ///#ifdef JDK12 ///import java.lang.ref.WeakReference; ///import java.lang.ref.ReferenceQueue; ///import java.util.Map; ///import java.util.HashMap; ///#else import java.util.Hashtable; ///#endif /** * This is my type class. It differs from java.lang.class, in * that it maintains a type range. This type range may be implicit or * explicit.
* * * Think of this global type hierarchie: *
 *
 *          tUnknown
 *          /   |   \
 *         /    |    \
 *  tObject  boolean  int
 *    /  \            /  \
 *   /  tArray     short char
 * other             | 
 * classes          byte 
 * 
* * int implements the "interface" tBoolInt. boolean and byte * implement the "interface" tBoolByte which extends tBoolInt.
* * The type tBoolInt is , the type tBoolByte is * (hard coded in getTop). The * type tUInt is .
* * Note that tUnknown is no valid type, so we can replace * with * with ,
* * Arrays extend Object and implement java.lang.Cloneable and * java.io.Serializable, as defined in jls. * * The main operation on a type range is the intersection. To do this * on class ranges we need three more operations: specialization, * generalization and createRange.

* * specialization chooses the common sub type of the two types. It * is used to find the start point of the intersected interval.

* * generalization chooses the common super type of two types. It * is used to find the end point of the intersected interval.

* * When the new interval is created with createRangeType * the start and end points are adjusted so that they only consists of * possible types. * * @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; protected static JodeEnvironment env; ///#ifdef JDK12 /// private static final Map classHash = new HashMap(); /// private static final ReferenceQueue classQueue = new ReferenceQueue(); /// private static final Map arrayHash = new HashMap(); /// private static final ReferenceQueue arrayQueue = new ReferenceQueue(); ///#else private static final Hashtable classHash = new Hashtable(); private static final Hashtable arrayHash = new Hashtable(); ///#endif public static final Type tBoolean = new IntegerType(IntegerType.IT_Z); public static final Type tByte = new IntegerType(IntegerType.IT_B); public static final Type tChar = new IntegerType(IntegerType.IT_C); public static final Type tShort = new IntegerType(IntegerType.IT_S); public static final Type tInt = new IntegerType(IntegerType.IT_I); public static final Type tLong = new Type(TC_LONG); public static final Type tFloat = new Type(TC_FLOAT); public static final Type tDouble = new Type(TC_DOUBLE); public static final Type tVoid = new Type(TC_VOID); public static final Type tError = new Type(TC_ERROR); public static final Type tUnknown = new Type(TC_UNKNOWN); public static final Type tUInt = new IntegerType(IntegerType.IT_I | IntegerType.IT_B | IntegerType.IT_C | IntegerType.IT_S); public static final Type tBoolInt = new IntegerType(IntegerType.IT_I | IntegerType.IT_Z); public static final Type tBoolUInt= new IntegerType(IntegerType.IT_I | IntegerType.IT_B | IntegerType.IT_C | IntegerType.IT_S | IntegerType.IT_Z); public static final Type tBoolByte= new IntegerType(IntegerType.IT_B | IntegerType.IT_Z); public static final ClassInterfacesType tObject = tClass("java.lang.Object"); public static final ReferenceType tNull = new NullType(); public static final Type tUObject = tRange(tObject, tNull); public static final Type tString = tClass("java.lang.String"); public static final Type tStringBuffer = tClass("java.lang.StringBuffer"); public static final Type tJavaLangClass = tClass("java.lang.Class"); public static final Type tType(String type) { if (type == null || type.length() == 0) return tError; switch(type.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(type.substring(1))); case 'L': int index = type.indexOf(';'); if (index != type.length()-1) return tError; return tClass(type.substring(1, index)); case '(': return new MethodType(type); } throw new AssertError("Unknown type signature: "+type); } public static final ClassInterfacesType tClass(String clazzname) { return tClass(ClassInfo.forName(clazzname.replace('/','.'))); } public static final ClassInterfacesType tClass(ClassInfo clazzinfo) { ///#ifdef JDK12 /// java.lang.ref.Reference died; /// while ((died = classQueue.poll()) != null) /// classHash.values().remove(died); /// WeakReference ref = (WeakReference) classHash.get(clazzinfo); /// Object result = (ref == null) ? null : ref.get(); ///#else Object result = classHash.get(clazzinfo); ///#endif if (result == null) { result = new ClassInterfacesType(clazzinfo); ///#ifdef JDK12 /// classHash.put(clazzinfo, new WeakReference(result, classQueue)); ///#else classHash.put(clazzinfo, result); ///#endif } return (ClassInterfacesType) result; } public static final Type tArray(Type type) { if (type == tError) return type; ///#ifdef JDK12 /// java.lang.ref.Reference died; /// while ((died = arrayQueue.poll()) != null) /// arrayHash.values().remove(died); /// WeakReference ref = (WeakReference) arrayHash.get(type); /// Type result = (ref == null) ? null : (Type) ref.get(); ///#else Type result = (Type) arrayHash.get(type); ///#endif if (result == null) { result = new ArrayType(type); ///#ifdef JDK12 /// arrayHash.put(type, new WeakReference(result, arrayQueue)); ///#else arrayHash.put(type, result); ///#endif } return result; } public static final Type tRange(ReferenceType bottom, ReferenceType top) { return new RangeType(bottom, top); } public static Type tSuperType(Type type) { return type.getSuperType(); } public static Type tSubType(Type type) { return type.getSubType(); } public static Type tClassOrArray(String ident) { if (ident.charAt(0) == '[') return Type.tType(ident); else return Type.tClass(ident); } public static void setEnvironment(JodeEnvironment e) { env = e; } int typecode; /** * Create a new type with the given type code. */ protected Type(int typecode) { this.typecode = typecode; } public Type getSubType() { return this; } public Type getSuperType() { return this; } // public Type getBottom() { // return this; // } // public Type getTop() { // return this; // } public Type getHint() { return this; } /** * @return the type code of the type. */ public final int getTypeCode() { return typecode; } public int stackSize() { switch(typecode) { case TC_VOID: case TC_ERROR: return 0; default: return 1; case TC_DOUBLE: case TC_LONG: return 2; } } /** * Intersect this type with another type and return the new type. * @param type the other type. * @return the intersection, or tError, if a type conflict happens. */ public Type intersection(Type type) { if (this == tError || type == tError) return tError; if (this == tUnknown) return type; if (type == tUnknown || this == type) return this; if (Decompiler.isTypeDebugging) Decompiler.err.println("intersecting "+ this +" and "+ type + " to "); return tError; } /** * Checks if we need to cast to a middle type, before we can cast from * fromType to this type. * @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 type instead of a list * of minimum types. */ public boolean isValidType() { return typecode <= TC_DOUBLE; } /** * Checks if this type represents a class or an array of a class */ public boolean isClassType() { return false; } /** * Check if this and <unknown -- type&rt; are not disjunct. * @param type a simple type; this mustn't be a range type. * @return true if this is the case. */ public boolean isOfType(Type type) { return this.intersection(type) != Type.tError; } /** * Marks this type as used, so that the class is imported. */ public void useType() { /* No action needed for simple types */ } public String getDefaultName() { switch (typecode) { case TC_LONG: return "l"; case TC_FLOAT: return "f"; case TC_DOUBLE: return "d"; default: return "local"; } } 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; } } public String getTypeSignature() { switch (typecode) { case TC_LONG: return "J"; case TC_FLOAT: return "F"; case TC_DOUBLE: return "D"; default: return "?"; } } 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 ""; case TC_ERROR: default: return ""; } } }