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