ReferenceType xref

View Javadoc

    1   /*
    2    * Copyright  2000-2004 The Apache Software Foundation
    3    *
    4    *  Licensed under the Apache License, Version 2.0 (the "License");
    5    *  you may not use this file except in compliance with the License.
    6    *  You may obtain a copy of the License at
    7    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   *  Unless required by applicable law or agreed to in writing, software
    11   *  distributed under the License is distributed on an "AS IS" BASIS,
    12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   *  See the License for the specific language governing permissions and
    14   *  limitations under the License.
    15   *
    16   */
    17  package org.apache.bcel.generic;
    18
    19  import org.apache.bcel.Constants;
    20  import org.apache.bcel.Repository;
    21  import org.apache.bcel.classfile.JavaClass;
    22
    23  /***
    24   * Super class for object and array types.
    25   *
    26   * @version $Id: ReferenceType.java 386056 2006-03-15 11:31:56Z tcurdt $
    27   * @author  <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
    28   */
    29  public abstract class ReferenceType extends Type {
    30
    31      protected ReferenceType(byte t, String s) {
    32          super(t, s);
    33      }
    34
    35
    36      /*** Class is non-abstract but not instantiable from the outside
    37       */
    38      ReferenceType() {
    39          super(Constants.T_OBJECT, "<null object>");
    40      }
    41
    42
    43      /***
    44       * Return true iff this type is castable to another type t as defined in
    45       * the JVM specification.  The case where this is Type.NULL is not
    46       * defined (see the CHECKCAST definition in the JVM specification).
    47       * However, because e.g. CHECKCAST doesn't throw a
    48       * ClassCastException when casting a null reference to any Object,
    49       * true is returned in this case.
    50       *
    51       * @throws ClassNotFoundException if any classes or interfaces required
    52       *  to determine assignment compatibility can't be found
    53       */
    54      public boolean isCastableTo( Type t ) throws ClassNotFoundException {
    55          if (this.equals(Type.NULL)) {
    56              return true; // If this is ever changed in isAssignmentCompatible()
    57          }
    58          return isAssignmentCompatibleWith(t);
    59          /* Yes, it's true: It's the same definition.
    60           * See vmspec2 AASTORE / CHECKCAST definitions.
    61           */
    62      }
    63
    64
    65      /***
    66       * Return true iff this is assignment compatible with another type t
    67       * as defined in the JVM specification; see the AASTORE definition
    68       * there.
    69       * @throws ClassNotFoundException if any classes or interfaces required
    70       *  to determine assignment compatibility can't be found
    71       */
    72      public boolean isAssignmentCompatibleWith( Type t ) throws ClassNotFoundException {
    73          if (!(t instanceof ReferenceType)) {
    74              return false;
    75          }
    76          ReferenceType T = (ReferenceType) t;
    77          if (this.equals(Type.NULL)) {
    78              return true; // This is not explicitely stated, but clear. Isn't it?
    79          }
    80          /* If this is a class type then
    81           */
    82          if ((this instanceof ObjectType) && (((ObjectType) this).referencesClassExact())) {
    83              /* If T is a class type, then this must be the same class as T,
    84               or this must be a subclass of T;
    85               */
    86              if ((T instanceof ObjectType) && (((ObjectType) T).referencesClassExact())) {
    87                  if (this.equals(T)) {
    88                      return true;
    89                  }
    90                  if (Repository.instanceOf(((ObjectType) this).getClassName(), ((ObjectType) T)
    91                          .getClassName())) {
    92                      return true;
    93                  }
    94              }
    95              /* If T is an interface type, this must implement interface T.
    96               */
    97              if ((T instanceof ObjectType) && (((ObjectType) T).referencesInterfaceExact())) {
    98                  if (Repository.implementationOf(((ObjectType) this).getClassName(),
    99                          ((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                 Type sc = ((ArrayType) this).getElementType();
    145                 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 (int ii = 0; ii < Constants.INTERFACES_IMPLEMENTED_BY_ARRAYS.length; ii++) {
    165                     if (T.equals(new ObjectType(Constants.INTERFACES_IMPLEMENTED_BY_ARRAYS[ii]))) {
    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( 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             ArrayType arrType1 = (ArrayType) this;
    213             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).referencesInterface())
    227                 || ((t instanceof ObjectType) && ((ObjectType) t).referencesInterface())) {
    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         ObjectType thiz = (ObjectType) this;
    235         ObjectType other = (ObjectType) t;
    236         JavaClass[] thiz_sups = Repository.getSuperClasses(thiz.getClassName());
    237         JavaClass[] other_sups = Repository.getSuperClasses(other.getClassName());
    238         if ((thiz_sups == null) || (other_sups == null)) {
    239             return null;
    240         }
    241         // Waaahh...
    242         JavaClass[] this_sups = new JavaClass[thiz_sups.length + 1];
    243         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 (int i = 0; i < t_sups.length; i++) {
    249             for (int j = 0; j < this_sups.length; j++) {
    250                 if (this_sups[j].equals(t_sups[i])) {
    251                     return new ObjectType(this_sups[j].getClassName());
    252                 }
    253             }
    254         }
    255         // Huh? Did you ask for Type.OBJECT's superclass??
    256         return null;
    257     }
    258
    259
    260     /***
    261      * This commutative operation returns the first common superclass (narrowest ReferenceType
    262      * referencing a class, not an interface).
    263      * If one of the types is a superclass of the other, the former is returned.
    264      * If "this" is Type.NULL, then t is returned.
    265      * If t is Type.NULL, then "this" is returned.
    266      * If "this" equals t ['this.equals(t)'] "this" is returned.
    267      * If "this" or t is an ArrayType, then Type.OBJECT is returned.
    268      * If "this" or t is a ReferenceType referencing an interface, then Type.OBJECT is returned.
    269      * If not all of the two classes' superclasses cannot be found, "null" is returned.
    270      * See the JVM specification edition 2, "ß4.9.2 The Bytecode Verifier".
    271      *
    272      * @deprecated use getFirstCommonSuperclass(ReferenceType t) which has
    273      *             slightly changed semantics.
    274      * @throws ClassNotFoundException on failure to find superclasses of this
    275      *  type, or the type passed as a parameter
    276      */
    277     public ReferenceType firstCommonSuperclass( 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         ObjectType thiz = (ObjectType) this;
    307         ObjectType other = (ObjectType) t;
    308         JavaClass[] thiz_sups = Repository.getSuperClasses(thiz.getClassName());
    309         JavaClass[] other_sups = Repository.getSuperClasses(other.getClassName());
    310         if ((thiz_sups == null) || (other_sups == null)) {
    311             return null;
    312         }
    313         // Waaahh...
    314         JavaClass[] this_sups = new JavaClass[thiz_sups.length + 1];
    315         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 (int i = 0; i < t_sups.length; i++) {
    321             for (int j = 0; j < this_sups.length; j++) {
    322                 if (this_sups[j].equals(t_sups[i])) {
    323                     return new ObjectType(this_sups[j].getClassName());
    324                 }
    325             }
    326         }
    327         // Huh? Did you ask for Type.OBJECT's superclass??
    328         return null;
    329     }
    330 }