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