169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal/*
269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * Javassist, a Java-bytecode translator toolkit.
369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved.
469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal *
569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * The contents of this file are subject to the Mozilla Public License Version
669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * 1.1 (the "License"); you may not use this file except in compliance with
769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * the License.  Alternatively, the contents of this file may be used under
869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * the terms of the GNU Lesser General Public License Version 2.1 or later.
969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal *
1069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * Software distributed under the License is distributed on an "AS IS" basis,
1169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
1269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * for the specific language governing rights and limitations under the
1369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * License.
1469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal */
1569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalpackage javassist.bytecode.analysis;
1669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
1769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport java.util.ArrayList;
1869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport java.util.HashMap;
1969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport java.util.IdentityHashMap;
2069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport java.util.Iterator;
2169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport java.util.Map;
2269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
2369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport javassist.ClassPool;
2469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport javassist.CtClass;
2569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport javassist.NotFoundException;
2669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
2769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal/**
2869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * Represents a JVM type in data-flow analysis. This abstraction is necessary since
2969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * a JVM type not only includes all normal Java types, but also a few special types
3069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * that are used by the JVM internally. See the static field types on this class for
3169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * more info on these special types.
3269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal *
3369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * All primitive and special types reuse the same instance, so identity comparison can
3469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * be used when examining them. Normal java types must use {@link #equals(Object)} to
3569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * compare type instances.
3669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal *
3769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * In most cases, applications which consume this API, only need to call {@link #getCtClass()}
3869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * to obtain the needed type information.
3969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal *
4069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * @author Jason T. Greene
4169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal */
4269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalpublic class Type {
4369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private final CtClass clazz;
4469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private final boolean special;
4569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
4669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private static final Map prims = new IdentityHashMap();
4769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /** Represents the double primitive type */
4869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static final Type DOUBLE = new Type(CtClass.doubleType);
4969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /** Represents the boolean primitive type */
5069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static final Type BOOLEAN = new Type(CtClass.booleanType);
5169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /** Represents the long primitive type */
5269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static final Type LONG = new Type(CtClass.longType);
5369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /** Represents the char primitive type */
5469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static final Type CHAR = new Type(CtClass.charType);
5569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /** Represents the byte primitive type */
5669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static final Type BYTE = new Type(CtClass.byteType);
5769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /** Represents the short primitive type */
5869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static final Type SHORT = new Type(CtClass.shortType);
5969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /** Represents the integer primitive type */
6069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static final Type INTEGER = new Type(CtClass.intType);
6169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /** Represents the float primitive type */
6269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static final Type FLOAT = new Type(CtClass.floatType);
6369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /** Represents the void primitive type */
6469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static final Type VOID = new Type(CtClass.voidType);
6569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
6669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
6769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Represents an unknown, or null type. This occurs when aconst_null is used.
6869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * It is important not to treat this type as java.lang.Object, since a null can
6969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * be assigned to any reference type. The analyzer will replace these with
7069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * an actual known type if it can be determined by a merged path with known type
7169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * information. If this type is encountered on a frame then it is guaranteed to
7269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * be null, and the type information is simply not available. Any attempts to
7369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * infer the type, without further information from the compiler would be a guess.
7469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
7569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static final Type UNINIT = new Type(null);
7669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
7769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
7869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Represents an internal JVM return address, which is used by the RET
7969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * instruction to return to a JSR that invoked the subroutine.
8069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
8169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static final Type RETURN_ADDRESS = new Type(null, true);
8269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
8369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /** A placeholder used by the analyzer for the second word position of a double-word type */
8469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static final Type TOP = new Type(null, true);
8569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
8669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
8769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Represents a non-accessible value. Code cannot access the value this type
8869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * represents. It occurs when bytecode reuses a local variable table
8969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * position with non-mergable types. An example would be compiled code which
9069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * uses the same position for a primitive type in one branch, and a reference type
9169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * in another branch.
9269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
9369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static final Type BOGUS = new Type(null, true);
9469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
9569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /** Represents the java.lang.Object reference type */
9669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static final Type OBJECT = lookupType("java.lang.Object");
9769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /** Represents the java.io.Serializable reference type */
9869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static final Type SERIALIZABLE = lookupType("java.io.Serializable");
9969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /** Represents the java.lang.Coneable reference type */
10069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static final Type CLONEABLE = lookupType("java.lang.Cloneable");
10169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /** Represents the java.lang.Throwable reference type */
10269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static final Type THROWABLE = lookupType("java.lang.Throwable");
10369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
10469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    static {
10569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        prims.put(CtClass.doubleType, DOUBLE);
10669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        prims.put(CtClass.longType, LONG);
10769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        prims.put(CtClass.charType, CHAR);
10869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        prims.put(CtClass.shortType, SHORT);
10969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        prims.put(CtClass.intType, INTEGER);
11069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        prims.put(CtClass.floatType, FLOAT);
11169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        prims.put(CtClass.byteType, BYTE);
11269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        prims.put(CtClass.booleanType, BOOLEAN);
11369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        prims.put(CtClass.voidType, VOID);
11469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
11569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
11669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
11769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
11869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Obtain the Type for a given class. If the class is a primitive,
11969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * the the unique type instance for the primitive will be returned.
12069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Otherwise a new Type instance representing the class is returned.
12169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
12269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @param clazz The java class
12369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @return a type instance for this class
12469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
12569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static Type get(CtClass clazz) {
12669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        Type type = (Type)prims.get(clazz);
12769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return type != null ? type : new Type(clazz);
12869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
12969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
13069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private static Type lookupType(String name) {
13169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        try {
13269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal             return new Type(ClassPool.getDefault().get(name));
13369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        } catch (NotFoundException e) {
13469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throw new RuntimeException(e);
13569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
13669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
13769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
13869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    Type(CtClass clazz) {
13969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        this(clazz, false);
14069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
14169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
14269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private Type(CtClass clazz, boolean special) {
14369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        this.clazz = clazz;
14469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        this.special = special;
14569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
14669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
14769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    // Used to indicate a merge internally triggered a change
14869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    boolean popChanged() {
14969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return false;
15069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
15169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
15269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
15369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Gets the word size of this type. Double-word types, such as long and double
15469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * will occupy two positions on the local variable table or stack.
15569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
15669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @return the number of words needed to hold this type
15769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
15869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public int getSize() {
15969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return clazz == CtClass.doubleType || clazz == CtClass.longType || this == TOP ? 2 : 1;
16069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
16169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
16269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
16369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Returns the class this type represents. If the type is special, null will be returned.
16469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
16569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @return the class for this type, or null if special
16669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
16769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public CtClass getCtClass() {
16869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return clazz;
16969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
17069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
17169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
17269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Returns whether or not this type is a normal java reference, i.e. it is or extends java.lang.Object.
17369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
17469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @return true if a java reference, false if a primitive or special
17569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
17669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public boolean isReference() {
17769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return !special && (clazz == null || !clazz.isPrimitive());
17869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
17969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
18069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
18169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Returns whether or not the type is special. A special type is one that is either used
18269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * for internal tracking, or is only used internally by the JVM.
18369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
18469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @return true if special, false if not
18569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
18669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public boolean isSpecial() {
18769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return special;
18869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
18969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
19069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
19169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Returns whether or not this type is an array.
19269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
19369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @return true if an array, false if not
19469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
19569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public boolean isArray() {
19669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return clazz != null && clazz.isArray();
19769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
19869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
19969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
20069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Returns the number of dimensions of this array. If the type is not an
20169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * array zero is returned.
20269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
20369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @return zero if not an array, otherwise the number of array dimensions.
20469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
20569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public int getDimensions() {
20669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (!isArray()) return 0;
20769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
20869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        String name = clazz.getName();
20969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        int pos = name.length() - 1;
21069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        int count = 0;
21169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        while (name.charAt(pos) == ']' ) {
21269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            pos -= 2;
21369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            count++;
21469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
21569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
21669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return count;
21769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
21869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
21969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
22069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Returns the array component if this type is an array. If the type
22169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * is not an array null is returned.
22269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
22369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @return the array component if an array, otherwise null
22469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
22569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public Type getComponent() {
22669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (this.clazz == null || !this.clazz.isArray())
22769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return null;
22869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
22969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        CtClass component;
23069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        try {
23169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            component = this.clazz.getComponentType();
23269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        } catch (NotFoundException e) {
23369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throw new RuntimeException(e);
23469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
23569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
23669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        Type type = (Type)prims.get(component);
23769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return (type != null) ? type : new Type(component);
23869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
23969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
24069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
24169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Determines whether this type is assignable, to the passed type.
24269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * A type is assignable to another if it is either the same type, or
24369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * a sub-type.
24469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
24569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @param type the type to test assignability to
24669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @return true if this is assignable to type, otherwise false
24769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
24869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public boolean isAssignableFrom(Type type) {
24969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (this == type)
25069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return true;
25169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
25269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if ((type == UNINIT && isReference()) || this == UNINIT && type.isReference())
25369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return true;
25469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
25569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (type instanceof MultiType)
25669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return ((MultiType)type).isAssignableTo(this);
25769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
25869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (type instanceof MultiArrayType)
25969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return ((MultiArrayType)type).isAssignableTo(this);
26069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
26169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
26269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        // Primitives and Special types must be identical
26369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (clazz == null || clazz.isPrimitive())
26469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return false;
26569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
26669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        try {
26769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return type.clazz.subtypeOf(clazz);
26869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        } catch (Exception e) {
26969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throw new RuntimeException(e);
27069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
27169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
27269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
27369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
27469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Finds the common base type, or interface which both this and the specified
27569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * type can be assigned. If there is more than one possible answer, then a {@link MultiType},
27669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * or a {@link MultiArrayType} is returned. Multi-types have special rules,
27769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * and successive merges and assignment tests on them will alter their internal state,
27869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * as well as other multi-types they have been merged with. This method is used by
27969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * the data-flow analyzer to merge the type state from multiple branches.
28069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
28169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @param type the type to merge with
28269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @return the merged type
28369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
28469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public Type merge(Type type) {
28569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (type == this)
28669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return this;
28769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (type == null)
28869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return this;
28969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (type == Type.UNINIT)
29069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return this;
29169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (this == Type.UNINIT)
29269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return type;
29369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
29469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        // Unequal primitives and special types can not be merged
29569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (! type.isReference() || ! this.isReference())
29669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return BOGUS;
29769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
29869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        // Centralize merging of multi-interface types
29969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (type instanceof MultiType)
30069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return type.merge(this);
30169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
30269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (type.isArray() && this.isArray())
30369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return mergeArray(type);
30469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
30569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        try {
30669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return mergeClasses(type);
30769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        } catch (NotFoundException e) {
30869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throw new RuntimeException(e);
30969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
31069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
31169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
31269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal   Type getRootComponent(Type type) {
31369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        while (type.isArray())
31469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            type = type.getComponent();
31569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
31669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return type;
31769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
31869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
31969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private Type createArray(Type rootComponent, int dims) {
32069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (rootComponent instanceof MultiType)
32169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return new MultiArrayType((MultiType) rootComponent, dims);
32269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
32369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        String name = arrayName(rootComponent.clazz.getName(), dims);
32469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
32569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        Type type;
32669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        try {
32769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            type = Type.get(getClassPool(rootComponent).get(name));
32869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        } catch (NotFoundException e) {
32969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throw new RuntimeException(e);
33069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
33169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
33269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return type;
33369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
33469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
33569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    String arrayName(String component, int dims) {
33669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     // Using char[] since we have no StringBuilder in JDK4, and StringBuffer is slow.
33769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        // Although, this is more efficient even if we did have one.
33869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        int i = component.length();
33969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        int size = i + dims * 2;
34069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        char[] string = new char[size];
34169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        component.getChars(0, i, string, 0);
34269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        while (i < size) {
34369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            string[i++] = '[';
34469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            string[i++] = ']';
34569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
34669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        component = new String(string);
34769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return component;
34869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
34969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
35069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private ClassPool getClassPool(Type rootComponent) {
35169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        ClassPool pool = rootComponent.clazz.getClassPool();
35269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return pool != null ? pool : ClassPool.getDefault();
35369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
35469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
35569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private Type mergeArray(Type type) {
35669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        Type typeRoot = getRootComponent(type);
35769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        Type thisRoot = getRootComponent(this);
35869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        int typeDims = type.getDimensions();
35969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        int thisDims = this.getDimensions();
36069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
36169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        // Array commponents can be merged when the dimensions are equal
36269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (typeDims == thisDims) {
36369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            Type mergedComponent = thisRoot.merge(typeRoot);
36469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
36569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            // If the components can not be merged (a primitive component mixed with a different type)
36669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            // then Object is the common type.
36769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            if (mergedComponent == Type.BOGUS)
36869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                return Type.OBJECT;
36969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
37069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return createArray(mergedComponent, thisDims);
37169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
37269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
37369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        Type targetRoot;
37469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        int targetDims;
37569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
37669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (typeDims < thisDims) {
37769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            targetRoot = typeRoot;
37869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            targetDims = typeDims;
37969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        } else {
38069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            targetRoot = thisRoot;
38169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            targetDims = thisDims;
38269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
38369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
38469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        // Special case, arrays are cloneable and serializable, so prefer them when dimensions differ
38569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (eq(CLONEABLE.clazz, targetRoot.clazz) || eq(SERIALIZABLE.clazz, targetRoot.clazz))
38669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return createArray(targetRoot, targetDims);
38769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
38869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return createArray(OBJECT, targetDims);
38969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
39069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
39169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private static CtClass findCommonSuperClass(CtClass one, CtClass two) throws NotFoundException {
39269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        CtClass deep = one;
39369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        CtClass shallow = two;
39469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        CtClass backupShallow = shallow;
39569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        CtClass backupDeep = deep;
39669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
39769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        // Phase 1 - Find the deepest hierarchy, set deep and shallow correctly
39869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        for (;;) {
39969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            // In case we get lucky, and find a match early
40069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            if (eq(deep, shallow) && deep.getSuperclass() != null)
40169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                return deep;
40269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
40369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            CtClass deepSuper = deep.getSuperclass();
40469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            CtClass shallowSuper = shallow.getSuperclass();
40569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
40669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            if (shallowSuper == null) {
40769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                // right, now reset shallow
40869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                shallow = backupShallow;
40969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                break;
41069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            }
41169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
41269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            if (deepSuper == null) {
41369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                // wrong, swap them, since deep is now useless, its our tmp before we swap it
41469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                deep = backupDeep;
41569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                backupDeep = backupShallow;
41669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                backupShallow = deep;
41769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
41869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                deep = shallow;
41969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                shallow = backupShallow;
42069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                break;
42169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            }
42269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
42369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            deep = deepSuper;
42469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            shallow = shallowSuper;
42569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
42669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
42769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        // Phase 2 - Move deepBackup up by (deep end - deep)
42869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        for (;;) {
42969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            deep = deep.getSuperclass();
43069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            if (deep == null)
43169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                break;
43269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
43369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            backupDeep = backupDeep.getSuperclass();
43469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
43569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
43669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        deep = backupDeep;
43769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
43869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        // Phase 3 - The hierarchy positions are now aligned
43969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        // The common super class is easy to find now
44069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        while (!eq(deep, shallow)) {
44169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            deep = deep.getSuperclass();
44269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            shallow = shallow.getSuperclass();
44369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
44469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
44569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return deep;
44669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
44769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
44869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private Type mergeClasses(Type type) throws NotFoundException {
44969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        CtClass superClass = findCommonSuperClass(this.clazz, type.clazz);
45069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
45169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        // If its Object, then try and find a common interface(s)
45269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (superClass.getSuperclass() == null) {
45369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            Map interfaces = findCommonInterfaces(type);
45469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            if (interfaces.size() == 1)
45569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                return new Type((CtClass) interfaces.values().iterator().next());
45669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            if (interfaces.size() > 1)
45769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                return new MultiType(interfaces);
45869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
45969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            // Only Object is in common
46069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return new Type(superClass);
46169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
46269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
46369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        // Check for a common interface that is not on the found supertype
46469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        Map commonDeclared = findExclusiveDeclaredInterfaces(type, superClass);
46569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (commonDeclared.size() > 0) {
46669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return new MultiType(commonDeclared, new Type(superClass));
46769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
46869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
46969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return new Type(superClass);
47069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
47169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
47269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private Map findCommonInterfaces(Type type) {
47369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        Map typeMap = getAllInterfaces(type.clazz, null);
47469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        Map thisMap = getAllInterfaces(this.clazz, null);
47569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
47669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return findCommonInterfaces(typeMap, thisMap);
47769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
47869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
47969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private Map findExclusiveDeclaredInterfaces(Type type, CtClass exclude) {
48069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        Map typeMap = getDeclaredInterfaces(type.clazz, null);
48169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        Map thisMap = getDeclaredInterfaces(this.clazz, null);
48269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        Map excludeMap = getAllInterfaces(exclude, null);
48369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
48469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        Iterator i = excludeMap.keySet().iterator();
48569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        while (i.hasNext()) {
48669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            Object intf = i.next();
48769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            typeMap.remove(intf);
48869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            thisMap.remove(intf);
48969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
49069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
49169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return findCommonInterfaces(typeMap, thisMap);
49269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
49369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
49469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
49569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    Map findCommonInterfaces(Map typeMap, Map alterMap) {
49669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        Iterator i = alterMap.keySet().iterator();
49769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        while (i.hasNext()) {
49869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            if (! typeMap.containsKey(i.next()))
49969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                i.remove();
50069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
50169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
50269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        // Reduce to subinterfaces
50369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        // This does not need to be recursive since we make a copy,
50469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        // and that copy contains all super types for the whole hierarchy
50569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        i = new ArrayList(alterMap.values()).iterator();
50669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        while (i.hasNext()) {
50769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            CtClass intf = (CtClass) i.next();
50869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            CtClass[] interfaces;
50969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            try {
51069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                interfaces = intf.getInterfaces();
51169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            } catch (NotFoundException e) {
51269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                throw new RuntimeException(e);
51369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            }
51469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
51569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            for (int c = 0; c < interfaces.length; c++)
51669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                alterMap.remove(interfaces[c].getName());
51769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
51869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
51969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return alterMap;
52069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
52169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
52269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    Map getAllInterfaces(CtClass clazz, Map map) {
52369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (map == null)
52469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            map = new HashMap();
52569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
52669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (clazz.isInterface())
52769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            map.put(clazz.getName(), clazz);
52869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        do {
52969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            try {
53069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                CtClass[] interfaces = clazz.getInterfaces();
53169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                for (int i = 0; i < interfaces.length; i++) {
53269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                    CtClass intf = interfaces[i];
53369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                    map.put(intf.getName(), intf);
53469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                    getAllInterfaces(intf, map);
53569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                }
53669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
53769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                clazz = clazz.getSuperclass();
53869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            } catch (NotFoundException e) {
53969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                throw new RuntimeException(e);
54069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            }
54169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        } while (clazz != null);
54269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
54369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return map;
54469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
54569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
54669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    Map getDeclaredInterfaces(CtClass clazz, Map map) {
54769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (map == null)
54869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            map = new HashMap();
54969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
55069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (clazz.isInterface())
55169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            map.put(clazz.getName(), clazz);
55269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
55369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        CtClass[] interfaces;
55469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        try {
55569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            interfaces = clazz.getInterfaces();
55669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        } catch (NotFoundException e) {
55769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throw new RuntimeException(e);
55869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
55969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
56069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        for (int i = 0; i < interfaces.length; i++) {
56169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            CtClass intf = interfaces[i];
56269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            map.put(intf.getName(), intf);
56369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            getDeclaredInterfaces(intf, map);
56469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
56569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
56669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return map;
56769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
56869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
56969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public boolean equals(Object o) {
57069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (! (o instanceof Type))
57169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return false;
57269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
57369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return o.getClass() == getClass() && eq(clazz, ((Type)o).clazz);
57469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
57569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
57669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    static boolean eq(CtClass one, CtClass two) {
57769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return one == two || (one != null && two != null && one.getName().equals(two.getName()));
57869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
57969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
58069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public String toString() {
58169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (this == BOGUS)
58269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return "BOGUS";
58369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (this == UNINIT)
58469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return "UNINIT";
58569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (this == RETURN_ADDRESS)
58669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return "RETURN ADDRESS";
58769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (this == TOP)
58869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return "TOP";
58969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
59069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return clazz == null ? "null" : clazz.getName();
59169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
59269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal}
593