    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 *  Unless required by applicable law or agreed to in writing, software
    012 *  distributed under the License is distributed on an "AS IS" BASIS,
    013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 *  See the License for the specific language governing permissions and
    015 *  limitations under the License.
    016 *
    017 */
    018package org.apache.bcel.generic;
    019
    020import org.apache.bcel.Const;
    021import org.apache.bcel.Repository;
    022import org.apache.bcel.classfile.JavaClass;
    023
    024/**
    025 * Super class for object and array types.
    026 *
    027 * @version $Id$
    028 */
    029public abstract class ReferenceType extends Type {
    030
    031    protected ReferenceType(final byte t, final String s) {
    032        super(t, s);
    033    }
    034
    035
    036    /** Class is non-abstract but not instantiable from the outside
    037     */
    038    ReferenceType() {
    039        super(Const.T_OBJECT, "<null object>");
    040    }
    041
    042
    043    /**
    044     * Return true iff this type is castable to another type t as defined in
    045     * the JVM specification.  The case where this is Type.NULL is not
    046     * defined (see the CHECKCAST definition in the JVM specification).
    047     * However, because e.g. CHECKCAST doesn't throw a
    048     * ClassCastException when casting a null reference to any Object,
    049     * true is returned in this case.
    050     *
    051     * @throws ClassNotFoundException if any classes or interfaces required
    052     *  to determine assignment compatibility can't be found
    053     */
    054    public boolean isCastableTo( final Type t ) throws ClassNotFoundException {
    055        if (this.equals(Type.NULL)) {
    056            return t instanceof ReferenceType; // If this is ever changed in isAssignmentCompatible()
    057        }
    058        return isAssignmentCompatibleWith(t);
    059        /* Yes, it's true: It's the same definition.
    060         * See vmspec2 AASTORE / CHECKCAST definitions.
    061         */
    062    }
    063
    064
    065    /**
    066     * Return true iff this is assignment compatible with another type t
    067     * as defined in the JVM specification; see the AASTORE definition
    068     * there.
    069     * @throws ClassNotFoundException if any classes or interfaces required
    070     *  to determine assignment compatibility can't be found
    071     */
    072    public boolean isAssignmentCompatibleWith( final Type t ) throws ClassNotFoundException {
    073        if (!(t instanceof ReferenceType)) {
    074            return false;
    075        }
    076        final ReferenceType T = (ReferenceType) t;
    077        if (this.equals(Type.NULL)) {
    078            return true; // This is not explicitely stated, but clear. Isn't it?
    079        }
    080        /* If this is a class type then
    081         */
    082        if ((this instanceof ObjectType) && (((ObjectType) this).referencesClassExact())) {
    083            /* If T is a class type, then this must be the same class as T,
    084             or this must be a subclass of T;
    085             */
    086            if ((T instanceof ObjectType) && (((ObjectType) T).referencesClassExact())) {
    087                if (this.equals(T)) {
    088                    return true;
    089                }
    090                if (Repository.instanceOf(((ObjectType) this).getClassName(), ((ObjectType) T)
    091                        .getClassName())) {
    092                    return true;
    093                }
    094            }
    095            /* If T is an interface type, this must implement interface T.
    096             */
    097            if ((T instanceof ObjectType) && (((ObjectType) T).referencesInterfaceExact())) {
    098                if (Repository.implementationOf(((ObjectType) this).getClassName(),
    099                        ((ObjectType) T).getClassName())) {
    100                    return true;
    101                }
    102            }
    103        }
    104        /* If this is an interface type, then:
    105         */
    106        if ((this instanceof ObjectType) && (((ObjectType) this).referencesInterfaceExact())) {
    107            /* If T is a class type, then T must be Object (�2.4.7).
    108             */
    109            if ((T instanceof ObjectType) && (((ObjectType) T).referencesClassExact())) {
    110                if (T.equals(Type.OBJECT)) {
    111                    return true;
    112                }
    113            }
    114            /* If T is an interface type, then T must be the same interface
    115             * as this or a superinterface of this (�2.13.2).
    116             */
    117            if ((T instanceof ObjectType) && (((ObjectType) T).referencesInterfaceExact())) {
    118                if (this.equals(T)) {
    119                    return true;
    120                }
    121                if (Repository.implementationOf(((ObjectType) this).getClassName(),
    122                        ((ObjectType) T).getClassName())) {
    123                    return true;
    124                }
    125            }
    126        }
    127        /* If this is an array type, namely, the type SC[], that is, an
    128         * array of components of type SC, then:
    129         */
    130        if (this instanceof ArrayType) {
    131            /* If T is a class type, then T must be Object (�2.4.7).
    132             */
    133            if ((T instanceof ObjectType) && (((ObjectType) T).referencesClassExact())) {
    134                if (T.equals(Type.OBJECT)) {
    135                    return true;
    136                }
    137            }
    138            /* If T is an array type TC[], that is, an array of components
    139             * of type TC, then one of the following must be true:
    140             */
    141            if (T instanceof ArrayType) {
    142                /* TC and SC are the same primitive type (�2.4.1).
    143                 */
    144                final Type sc = ((ArrayType) this).getElementType();
    145                final Type tc = ((ArrayType) T).getElementType();
    146                if (sc instanceof BasicType && tc instanceof BasicType && sc.equals(tc)) {
    147                    return true;
    148                }
    149                /* TC and SC are reference types (�2.4.6), and type SC is
    150                 * assignable to TC by these runtime rules.
    151                 */
    152                if (tc instanceof ReferenceType && sc instanceof ReferenceType
    153                        && ((ReferenceType) sc).isAssignmentCompatibleWith(tc)) {
    154                    return true;
    155                }
    156            }
    157            /* If T is an interface type, T must be one of the interfaces implemented by arrays (�2.15). */
    158            // TODO: Check if this is still valid or find a way to dynamically find out which
    159            // interfaces arrays implement. However, as of the JVM specification edition 2, there
    160            // are at least two different pages where assignment compatibility is defined and
    161            // on one of them "interfaces implemented by arrays" is exchanged with "'Cloneable' or
    162            // 'java.io.Serializable'"
    163            if ((T instanceof ObjectType) && (((ObjectType) T).referencesInterfaceExact())) {
    164                for (final String element : Const.getInterfacesImplementedByArrays()) {
    165                    if (T.equals(ObjectType.getInstance(element))) {
    166                        return true;
    167                    }
    168                }
    169            }
    170        }
    171        return false; // default.
    172    }
    173
    174
    175    /**
    176     * This commutative operation returns the first common superclass (narrowest ReferenceType
    177     * referencing a class, not an interface).
    178     * If one of the types is a superclass of the other, the former is returned.
    179     * If "this" is Type.NULL, then t is returned.
    180     * If t is Type.NULL, then "this" is returned.
    181     * If "this" equals t ['this.equals(t)'] "this" is returned.
    182     * If "this" or t is an ArrayType, then Type.OBJECT is returned;
    183     * unless their dimensions match. Then an ArrayType of the same
    184     * number of dimensions is returned, with its basic type being the
    185     * first common super class of the basic types of "this" and t.
    186     * If "this" or t is a ReferenceType referencing an interface, then Type.OBJECT is returned.
    187     * If not all of the two classes' superclasses cannot be found, "null" is returned.
    188     * See the JVM specification edition 2, "�4.9.2 The Bytecode Verifier".
    189     *
    190     * @throws ClassNotFoundException on failure to find superclasses of this
    191     *  type, or the type passed as a parameter
    192     */
    193    public ReferenceType getFirstCommonSuperclass( final ReferenceType t ) throws ClassNotFoundException {
    194        if (this.equals(Type.NULL)) {
    195            return t;
    196        }
    197        if (t.equals(Type.NULL)) {
    198            return this;
    199        }
    200        if (this.equals(t)) {
    201            return this;
    202            /*
    203             * TODO: Above sounds a little arbitrary. On the other hand, there is
    204             * no object referenced by Type.NULL so we can also say all the objects
    205             * referenced by Type.NULL were derived from java.lang.Object.
    206             * However, the Java Language's "instanceof" operator proves us wrong:
    207             * "null" is not referring to an instance of java.lang.Object :)
    208             */
    209        }
    210        /* This code is from a bug report by Konstantin Shagin <konst@cs.technion.ac.il> */
    211        if ((this instanceof ArrayType) && (t instanceof ArrayType)) {
    212            final ArrayType arrType1 = (ArrayType) this;
    213            final ArrayType arrType2 = (ArrayType) t;
    214            if ((arrType1.getDimensions() == arrType2.getDimensions())
    215                    && arrType1.getBasicType() instanceof ObjectType
    216                    && arrType2.getBasicType() instanceof ObjectType) {
    217                return new ArrayType(((ObjectType) arrType1.getBasicType())
    218                        .getFirstCommonSuperclass((ObjectType) arrType2.getBasicType()), arrType1
    219                        .getDimensions());
    220            }
    221        }
    222        if ((this instanceof ArrayType) || (t instanceof ArrayType)) {
    223            return Type.OBJECT;
    224            // TODO: Is there a proof of OBJECT being the direct ancestor of every ArrayType?
    225        }
    226        if (((this instanceof ObjectType) && ((ObjectType) this).referencesInterfaceExact())
    227                || ((t instanceof ObjectType) && ((ObjectType) t).referencesInterfaceExact())) {
    228            return Type.OBJECT;
    229            // TODO: The above line is correct comparing to the vmspec2. But one could
    230            // make class file verification a bit stronger here by using the notion of
    231            // superinterfaces or even castability or assignment compatibility.
    232        }
    233        // this and t are ObjectTypes, see above.
    234        final ObjectType thiz = (ObjectType) this;
    235        final ObjectType other = (ObjectType) t;
    236        final JavaClass[] thiz_sups = Repository.getSuperClasses(thiz.getClassName());
    237        final JavaClass[] other_sups = Repository.getSuperClasses(other.getClassName());
    238        if ((thiz_sups == null) || (other_sups == null)) {
    239            return null;
    240        }
    241        // Waaahh...
    242        final JavaClass[] this_sups = new JavaClass[thiz_sups.length + 1];
    243        final JavaClass[] t_sups = new JavaClass[other_sups.length + 1];
    244        System.arraycopy(thiz_sups, 0, this_sups, 1, thiz_sups.length);
    245        System.arraycopy(other_sups, 0, t_sups, 1, other_sups.length);
    246        this_sups[0] = Repository.lookupClass(thiz.getClassName());
    247        t_sups[0] = Repository.lookupClass(other.getClassName());
    248        for (final JavaClass t_sup : t_sups) {
    249            for (final JavaClass this_sup : this_sups) {
    250                if (this_sup.equals(t_sup)) {
    251                    return ObjectType.getInstance(this_sup.getClassName());
    252                }
    253            }
    254        }
    255        // Huh? Did you ask for Type.OBJECT's superclass??
    256        return null;
    257    }
    258
    259    /**
    260     * This commutative operation returns the first common superclass (narrowest ReferenceType
    261     * referencing a class, not an interface).
    262     * If one of the types is a superclass of the other, the former is returned.
    263     * If "this" is Type.NULL, then t is returned.
    264     * If t is Type.NULL, then "this" is returned.
    265     * If "this" equals t ['this.equals(t)'] "this" is returned.
    266     * If "this" or t is an ArrayType, then Type.OBJECT is returned.
    267     * If "this" or t is a ReferenceType referencing an interface, then Type.OBJECT is returned.
    268     * If not all of the two classes' superclasses cannot be found, "null" is returned.
    269     * See the JVM specification edition 2, "�4.9.2 The Bytecode Verifier".
    270     *
    271     * @deprecated use getFirstCommonSuperclass(ReferenceType t) which has
    272     *             slightly changed semantics.
    273     * @throws ClassNotFoundException on failure to find superclasses of this
    274     *  type, or the type passed as a parameter
    275     */
    276    @Deprecated
    277    public ReferenceType firstCommonSuperclass( final ReferenceType t ) throws ClassNotFoundException {
    278        if (this.equals(Type.NULL)) {
    279            return t;
    280        }
    281        if (t.equals(Type.NULL)) {
    282            return this;
    283        }
    284        if (this.equals(t)) {
    285            return this;
    286            /*
    287             * TODO: Above sounds a little arbitrary. On the other hand, there is
    288             * no object referenced by Type.NULL so we can also say all the objects
    289             * referenced by Type.NULL were derived from java.lang.Object.
    290             * However, the Java Language's "instanceof" operator proves us wrong:
    291             * "null" is not referring to an instance of java.lang.Object :)
    292             */
    293        }
    294        if ((this instanceof ArrayType) || (t instanceof ArrayType)) {
    295            return Type.OBJECT;
    296            // TODO: Is there a proof of OBJECT being the direct ancestor of every ArrayType?
    297        }
    298        if (((this instanceof ObjectType) && ((ObjectType) this).referencesInterface())
    299                || ((t instanceof ObjectType) && ((ObjectType) t).referencesInterface())) {
    300            return Type.OBJECT;
    301            // TODO: The above line is correct comparing to the vmspec2. But one could
    302            // make class file verification a bit stronger here by using the notion of
    303            // superinterfaces or even castability or assignment compatibility.
    304        }
    305        // this and t are ObjectTypes, see above.
    306        final ObjectType thiz = (ObjectType) this;
    307        final ObjectType other = (ObjectType) t;
    308        final JavaClass[] thiz_sups = Repository.getSuperClasses(thiz.getClassName());
    309        final JavaClass[] other_sups = Repository.getSuperClasses(other.getClassName());
    310        if ((thiz_sups == null) || (other_sups == null)) {
    311            return null;
    312        }
    313        // Waaahh...
    314        final JavaClass[] this_sups = new JavaClass[thiz_sups.length + 1];
    315        final JavaClass[] t_sups = new JavaClass[other_sups.length + 1];
    316        System.arraycopy(thiz_sups, 0, this_sups, 1, thiz_sups.length);
    317        System.arraycopy(other_sups, 0, t_sups, 1, other_sups.length);
    318        this_sups[0] = Repository.lookupClass(thiz.getClassName());
    319        t_sups[0] = Repository.lookupClass(other.getClassName());
    320        for (final JavaClass t_sup : t_sups) {
    321            for (final JavaClass this_sup : this_sups) {
    322                if (this_sup.equals(t_sup)) {
    323                    return ObjectType.getInstance(this_sup.getClassName());
    324                }
    325            }
    326        }
    327        // Huh? Did you ask for Type.OBJECT's superclass??
    328        return null;
    329    }
    330}