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