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