/* * ClassRangeType (c) 1998 Jochen Hoenicke * * You may distribute under the terms of the GNU General Public License. * * IN NO EVENT SHALL JOCHEN HOENICKE BE LIABLE TO ANY PARTY FOR DIRECT, * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF JOCHEN HOENICKE * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * JOCHEN HOENICKE SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" * BASIS, AND JOCHEN HOENICKE HAS NO OBLIGATION TO PROVIDE MAINTENANCE, * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * * $Id$ */ package jode; import sun.tools.java.*; /** * This class represents an object type which isn't fully known. * The real object type lies in a range of types between topType * and bottomType.

* * For a totally unknown type topType is tObject and bottomType is * null. It is always garanteed that topType is an Array or an Object * and that bottomType is null or an Array or an Object.

* * @author Jochen Hoenicke * @date 98/08/06 */ public class ClassRangeType extends MyType { final Type bottomType; final Type topType; public ClassRangeType(Type bottomType, Type topType) { super(103, "-"); if (bottomType != null && bottomType.getTypeCode() == 103) bottomType = ((ClassRangeType)bottomType).bottomType; if (topType != null && topType.getTypeCode() == 103) topType = ((ClassRangeType)topType).topType; this.bottomType = bottomType; this.topType = topType; } public static Type createRangeType(Type bottom, Type top) { // TODO: XXX calculate < top, ...> \cap <..., bottom> // e.g. top , bottom result // x , null // tUnknown, object // Fahrrad , Fahrzeug error // Fahrzeug, Fahrrad // int , Fahrrad error if (bottom != null && bottom.getTypeCode() == 103) { bottom = ((ClassRangeType)bottom).bottomType; } if (top != null && top.getTypeCode() == 103) { top = ((ClassRangeType)top).topType; } /* First the trivial cases */ if (top == tError || bottom == tError) return tError; /* This is always okay (right open interval, maybe left open) */ if (top == null) return new ClassRangeType(bottom,top); /* -> * if bottom is tObject, its okay. */ if (bottom == top) return bottom; if (top.getTypeCode() <= 4 && bottom == null) return top; if (bottom != null && bottom.getTypeCode() <= 4 && top.getTypeCode() <= bottom.getTypeCode()) return bottom; if (top.getTypeCode() != 9 && top.getTypeCode() != 10) return tError; if (bottom == null || bottom == tObject) return new ClassRangeType(tObject, top); /* now bottom != null and top != null */ if (bottom.getTypeCode() == 9 && top.getTypeCode() == 9) { Type type = createRangeType(bottom.getElementType(), top.getElementType()); if (type == tError) return tError; return tArray(type); } if (bottom.getTypeCode() != 10 || top.getTypeCode() != 10) return tError; if (bottom == top) return bottom; ClassDeclaration c1 = new ClassDeclaration(bottom.getClassName()); ClassDeclaration c2 = new ClassDeclaration(top.getClassName()); try { if (c1.getClassDefinition(env).superClassOf(env, c2) || c1.getClassDefinition(env).implementedBy(env, c2)) return new ClassRangeType(bottom, top); } catch (ClassNotFound ex) { } return tError; } public Type getElementType() { Type bottom = bottomType != null ? bottomType.getElementType() : null; Type top = topType != null ? topType.getElementType() : null; return new ClassRangeType(bottom, top); } /** * Returns the specialized type of t1 and t2, e.g * null , xx -> xx * tObject, object -> object * int , short -> short * tArray(tObject), tArray(tUnknown) -> tArray(tObject) * tArray(tUnknown), tObject -> tArray(tUnknown) */ public static Type getSpecializedType(Type t1, Type t2) { if (t1 == null || t2 == tError) return t2; if (t2 == null || t1 == tError) return t1; if (t1.getTypeCode() == 103) { t1 = ((ClassRangeType)t1).bottomType; if (t1 == null) return t2; } if (t2.getTypeCode() == 103) { t2 = ((ClassRangeType)t2).bottomType; if (t2 == null) return t1; } if (t1 == t2) return t1; if (t1.getTypeCode() <= 4 && t2.getTypeCode() <= 4) { if (t1.getTypeCode() < t2.getTypeCode()) return t1; else return t2; } if ((t1.getTypeCode() != 9 && t1.getTypeCode() != 10) || (t2.getTypeCode() != 9 && t2.getTypeCode() != 10)) return tError; if (t1 == MyType.tObject) return t2; if (t2 == MyType.tObject) return t1; if (t1.getTypeCode() == 9 && t2.getTypeCode() == 9) return tArray(getSpecializedType(t1.getElementType(), t2.getElementType())); if (t1.getTypeCode() != 10 || t2.getTypeCode() != 10) return tError; /* Now we have two classes or interfaces. The result should * be the object that is the the child of both objects resp * implements both interfaces. * * I currently only handle the simple case where one of the * two objects implements the other or is a child of it. * * Forget the following setences, java tells us if the local * is an interface or an object. * * There are really complicated cases that are currently * ignored: imaging, c1 and c2 are both disjunct interfaces * and there are some object which implements them both. * There is no way for us to guess which. * * What can we do about this? We probably need something more * powerful than a simple class range. * But maybe this isn't needed at all. How should someone * use an object which implements two interfaces in a local * variable without casting? The information which object * to use must be somewhere in the method. * * But think of this code fragment: * * class Foo implements i1, i2 { ... } * * class Bar { * Foo getFoo() { ... } * void someFunction() { * while ((Foo foo = getFoo()) != null) { * foo.interface1Method(); * foo.interface2Method(); * } * } * } * * Since the while condition is moved to the bottom of * the loop, the type information of foo is only available * after the two interface methods are called. * The current code would produce tError. */ ClassDeclaration c1 = new ClassDeclaration(t1.getClassName()); ClassDeclaration c2 = new ClassDeclaration(t2.getClassName()); try { if (c1.getClassDefinition(env).superClassOf(env, c2)) return t2; if (c2.getClassDefinition(env).superClassOf(env, c1)) return t1; if (c1.getClassDefinition(env).implementedBy(env, c2)) return t2; if (c2.getClassDefinition(env).implementedBy(env, c1)) return t1; } catch (ClassNotFound ex) { } return tError; } /** * Returns the generalized type of t1 and t2, e.g * tObject, tString -> tObject * int , short -> int * tArray(tObject), tArray(tUnknown) -> tArray(tUnknown) * tArray(tUnknown), tObject -> tObject * tUnknown, tString -> tString !! * null , tString -> tString !! */ public static Type getGeneralizedType(Type t1, Type t2) { if (t1 != null && t1.getTypeCode() == 103) t1 = ((ClassRangeType)t1).topType; if (t2 != null && t2.getTypeCode() == 103) t2 = ((ClassRangeType)t2).topType; if (t1 == t2 || t1 == tError || t2 == null) return t1; if (t2 == tError || t1 == null) return t2; if (t1.getTypeCode() <= 4 && t2.getTypeCode() <= 4) { if (t1.getTypeCode() < t2.getTypeCode()) return t1; else return t2; } if ((t1.getTypeCode() != 9 && t1.getTypeCode() != 10) || (t1.getTypeCode() != 9 && t1.getTypeCode() != 10)) return tError; if (t1 == MyType.tObject) return t1; if (t2 == MyType.tObject) return t2; if (t1.getTypeCode() == 9 && t2.getTypeCode() == 9) return tArray(getGeneralizedType(t1.getElementType(), t2.getElementType())); if (t1.getTypeCode() != 10 || t2.getTypeCode() != 10) return tError; /* This code is not always correct: * We don't want a real super type in all cases, but maybe only * an interface which both objects implement. Think of this: * * interface I; * class C1 implements I; * class C2 implements I; * * { * I var; * if (cond) * var = getC1(); * else * var = getC2(); * return var.interfaceMethod(); * } * * The current code would first assign the type object to * var and then produce a type error when interfaceMethod * is called. * * Now we have proved that we need some better concept for * types. (Maybe a set of types for the upper and lower * bound) */ ClassDeclaration c1 = new ClassDeclaration(t1.getClassName()); ClassDeclaration c2 = new ClassDeclaration(t2.getClassName()); try { /* if one of the two types is an interface which * is implemented by the other type the interface * is the result. */ if (c1.getClassDefinition(env).implementedBy(env, c2)) return t1; if (c2.getClassDefinition(env).implementedBy(env, c1)) return t2; ClassDefinition c = c1.getClassDefinition(env); while(c != null && !c.superClassOf(env, c2)) { c = c.getSuperClass(env).getClassDefinition(env); } if (c != null) return tClass(c.getName()); } catch (ClassNotFound ex) { } return tObject; } public Type getIntersection(ClassRangeType type) { Type bottom = getSpecializedType(bottomType, type.bottomType); Type top = getGeneralizedType(topType, type.topType); Type newType = createRangeType(bottom,top); if (newType == tError) { System.err.println("intersecting "+ this +" and "+ type + " to <" + bottom + "-" + top + "> to "); Thread.dumpStack(); } return newType; } public boolean intersects(ClassRangeType type) { return getIntersection(type) != tError; } public String typeString(String string, boolean flag1, boolean flag2) { // if (verbose??) return "<"+bottomType+"-"+topType+">" + string; // else // return bottomType.typeString(string, flag1, flag2); } // public String toString() // { // return "<"+bottomType+"-"+topType+">"; // } }