1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Licensed to the Apache Software Foundation (ASF) under one or more
   7  * contributor license agreements.  See the NOTICE file distributed with
   8  * this work for additional information regarding copyright ownership.
   9  * The ASF licenses this file to You under the Apache License, Version 2.0
  10  * (the "License"); you may not use this file except in compliance with
  11  * the License.  You may obtain a copy of the License at
  12  *
  13  *      http://www.apache.org/licenses/LICENSE-2.0
  14  *
  15  * Unless required by applicable law or agreed to in writing, software
  16  * distributed under the License is distributed on an "AS IS" BASIS,
  17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18  * See the License for the specific language governing permissions and
  19  * limitations under the License.
  20  */
  21 
  22 package com.sun.org.apache.bcel.internal.generic;
  23 
  24 
  25 import com.sun.org.apache.bcel.internal.Constants;
  26 import com.sun.org.apache.bcel.internal.Repository;
  27 import com.sun.org.apache.bcel.internal.classfile.JavaClass;
  28 
  29 /**
  30  * Super class for object and array types.
  31  *
  32  * @author  <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
  33  */
  34 public abstract class ReferenceType extends Type {
  35   protected ReferenceType(byte t, String s) {
  36     super(t, s);
  37   }
  38 
  39   /** Class is non-abstract but not instantiable from the outside
  40    */
  41   ReferenceType() {
  42     super(Constants.T_OBJECT, "<null object>");
  43   }
  44 
  45   /**
  46    * Return true iff this type is castable to another type t as defined in
  47    * the JVM specification.  The case where this is Type.NULL is not
  48    * defined (see the CHECKCAST definition in the JVM specification).
  49    * However, because e.g. CHECKCAST doesn't throw a
  50    * ClassCastException when casting a null reference to any Object,
  51    * true is returned in this case.
  52    */
  53   public boolean isCastableTo(Type t) {
  54     if (this.equals(Type.NULL))
  55       return true;              // If this is ever changed in isAssignmentCompatible()
  56 
  57     return isAssignmentCompatibleWith(t);
  58     /* Yes, it's true: It's the same definition.
  59      * See vmspec2 AASTORE / CHECKCAST definitions.
  60      */
  61   }
  62 
  63   /**
  64    * Return true iff this is assignment compatible with another type t
  65    * as defined in the JVM specification; see the AASTORE definition
  66    * there.
  67    */
  68   public boolean isAssignmentCompatibleWith(Type t) {
  69     if (!(t instanceof ReferenceType))
  70       return false;
  71 
  72     ReferenceType T = (ReferenceType) t;
  73 
  74     if (this.equals(Type.NULL))
  75       return true; // This is not explicitely stated, but clear. Isn't it?
  76 
  77     /* If this is a class type then
  78      */
  79     if ((this instanceof ObjectType) && (((ObjectType) this).referencesClass())) {
  80       /* If T is a class type, then this must be the same class as T,
  81          or this must be a subclass of T;
  82       */
  83       if ((T instanceof ObjectType) && (((ObjectType) T).referencesClass())) {
  84         if (this.equals(T))
  85           return true;
  86 
  87         if (Repository.instanceOf(((ObjectType) this).getClassName(),
  88                                   ((ObjectType) T).getClassName()))
  89           return true;
  90       }
  91 
  92       /* If T is an interface type, this must implement interface T.
  93        */
  94       if ((T instanceof ObjectType) && (((ObjectType) T).referencesInterface())) {
  95         if (Repository.implementationOf(((ObjectType) this).getClassName(),
  96                                         ((ObjectType) T).getClassName()))
  97           return true;
  98       }
  99     }
 100 
 101     /* If this is an interface type, then:
 102      */
 103     if ((this instanceof ObjectType) && (((ObjectType) this).referencesInterface())) {
 104       /* If T is a class type, then T must be Object (2.4.7).
 105        */
 106       if ((T instanceof ObjectType) && (((ObjectType) T).referencesClass())) {
 107         if (T.equals(Type.OBJECT)) return true;
 108       }
 109 
 110       /* If T is an interface type, then T must be the same interface
 111        * as this or a superinterface of this (2.13.2).
 112        */
 113       if ((T instanceof ObjectType) && (((ObjectType) T).referencesInterface())) {
 114         if (this.equals(T)) return true;
 115         if (Repository.implementationOf(((ObjectType) this).getClassName(),
 116                                         ((ObjectType) T).getClassName()))
 117           return true;
 118       }
 119     }
 120 
 121     /* If this is an array type, namely, the type SC[], that is, an
 122      * array of components of type SC, then:
 123      */
 124     if (this instanceof ArrayType) {
 125       /* If T is a class type, then T must be Object (2.4.7).
 126        */
 127       if ((T instanceof ObjectType) && (((ObjectType) T).referencesClass())) {
 128         if (T.equals(Type.OBJECT)) return true;
 129       }
 130 
 131       /* If T is an array type TC[], that is, an array of components
 132        * of type TC, then one of the following must be true:
 133        */
 134       if (T instanceof ArrayType) {
 135         /* TC and SC are the same primitive type (2.4.1).
 136          */
 137         Type sc = ((ArrayType) this).getElementType();
 138         Type tc = ((ArrayType) this).getElementType();
 139 
 140         if (sc instanceof BasicType && tc instanceof BasicType && sc.equals(tc))
 141           return true;
 142 
 143         /* TC and SC are reference types (2.4.6), and type SC is
 144          * assignable to TC by these runtime rules.
 145          */
 146         if (tc instanceof ReferenceType && sc instanceof ReferenceType &&
 147             ((ReferenceType) sc).isAssignmentCompatibleWith((ReferenceType) tc))
 148           return true;
 149       }
 150 
 151       /* If T is an interface type, T must be one of the interfaces implemented by arrays (2.15). */
 152       // TODO: Check if this is still valid or find a way to dynamically find out which
 153       // interfaces arrays implement. However, as of the JVM specification edition 2, there
 154       // are at least two different pages where assignment compatibility is defined and
 155       // on one of them "interfaces implemented by arrays" is exchanged with "'Cloneable' or
 156       // 'java.io.Serializable'"
 157       if ((T instanceof ObjectType) && (((ObjectType) T).referencesInterface())) {
 158         for (int ii = 0; ii < Constants.INTERFACES_IMPLEMENTED_BY_ARRAYS.length; ii++) {
 159           if (T.equals(new ObjectType(Constants.INTERFACES_IMPLEMENTED_BY_ARRAYS[ii]))) return true;
 160         }
 161       }
 162     }
 163     return false; // default.
 164   }
 165 
 166   /**
 167    * This commutative operation returns the first common superclass (narrowest ReferenceType
 168    * referencing a class, not an interface).
 169    * If one of the types is a superclass of the other, the former is returned.
 170    * If "this" is Type.NULL, then t is returned.
 171    * If t is Type.NULL, then "this" is returned.
 172    * If "this" equals t ['this.equals(t)'] "this" is returned.
 173    * If "this" or t is an ArrayType, then Type.OBJECT is returned;
 174    * unless their dimensions match. Then an ArrayType of the same
 175    * number of dimensions is returned, with its basic type being the
 176    * first common super class of the basic types of "this" and t.
 177    * If "this" or t is a ReferenceType referencing an interface, then Type.OBJECT is returned.
 178    * If not all of the two classes' superclasses cannot be found, "null" is returned.
 179    * See the JVM specification edition 2, "4.9.2 The Bytecode Verifier".
 180    */
 181   public ReferenceType getFirstCommonSuperclass(ReferenceType t) {
 182     if (this.equals(Type.NULL)) return t;
 183     if (t.equals(Type.NULL)) return this;
 184     if (this.equals(t)) return this;
 185     /*
 186      * TODO: Above sounds a little arbitrary. On the other hand, there is
 187      * no object referenced by Type.NULL so we can also say all the objects
 188      * referenced by Type.NULL were derived from java.lang.Object.
 189      * However, the Java Language's "instanceof" operator proves us wrong:
 190      * "null" is not referring to an instance of java.lang.Object :)
 191      */
 192 
 193     /* This code is from a bug report by Konstantin Shagin <konst@cs.technion.ac.il> */
 194 
 195     if ((this instanceof ArrayType) && (t instanceof ArrayType)) {
 196       ArrayType arrType1 = (ArrayType) this;
 197       ArrayType arrType2 = (ArrayType) t;
 198       if (
 199           (arrType1.getDimensions() == arrType2.getDimensions()) &&
 200           arrType1.getBasicType() instanceof ObjectType &&
 201           arrType2.getBasicType() instanceof ObjectType) {
 202         return new ArrayType(
 203                              ((ObjectType) arrType1.getBasicType()).getFirstCommonSuperclass((ObjectType) arrType2.getBasicType()),
 204                              arrType1.getDimensions()
 205                              );
 206 
 207       }
 208     }
 209 
 210     if ((this instanceof ArrayType) || (t instanceof ArrayType))
 211       return Type.OBJECT;
 212     // TODO: Is there a proof of OBJECT being the direct ancestor of every ArrayType?
 213 
 214     if (((this instanceof ObjectType) && ((ObjectType) this).referencesInterface()) ||
 215         ((t instanceof ObjectType) && ((ObjectType) t).referencesInterface()))
 216       return Type.OBJECT;
 217     // TODO: The above line is correct comparing to the vmspec2. But one could
 218     // make class file verification a bit stronger here by using the notion of
 219     // superinterfaces or even castability or assignment compatibility.
 220 
 221 
 222     // this and t are ObjectTypes, see above.
 223     ObjectType thiz = (ObjectType) this;
 224     ObjectType other = (ObjectType) t;
 225     JavaClass[] thiz_sups = Repository.getSuperClasses(thiz.getClassName());
 226     JavaClass[] other_sups = Repository.getSuperClasses(other.getClassName());
 227 
 228     if ((thiz_sups == null) || (other_sups == null)) {
 229       return null;
 230     }
 231 
 232     // Waaahh...
 233     JavaClass[] this_sups = new JavaClass[thiz_sups.length + 1];
 234     JavaClass[] t_sups = new JavaClass[other_sups.length + 1];
 235     System.arraycopy(thiz_sups, 0, this_sups, 1, thiz_sups.length);
 236     System.arraycopy(other_sups, 0, t_sups, 1, other_sups.length);
 237     this_sups[0] = Repository.lookupClass(thiz.getClassName());
 238     t_sups[0] = Repository.lookupClass(other.getClassName());
 239 
 240     for (int i = 0; i < t_sups.length; i++) {
 241       for (int j = 0; j < this_sups.length; j++) {
 242         if (this_sups[j].equals(t_sups[i])) return new ObjectType(this_sups[j].getClassName());
 243       }
 244     }
 245 
 246     // Huh? Did you ask for Type.OBJECT's superclass??
 247     return null;
 248   }
 249 
 250   /**
 251    * This commutative operation returns the first common superclass (narrowest ReferenceType
 252    * referencing a class, not an interface).
 253    * If one of the types is a superclass of the other, the former is returned.
 254    * If "this" is Type.NULL, then t is returned.
 255    * If t is Type.NULL, then "this" is returned.
 256    * If "this" equals t ['this.equals(t)'] "this" is returned.
 257    * If "this" or t is an ArrayType, then Type.OBJECT is returned.
 258    * If "this" or t is a ReferenceType referencing an interface, then Type.OBJECT is returned.
 259    * If not all of the two classes' superclasses cannot be found, "null" is returned.
 260    * See the JVM specification edition 2, "4.9.2 The Bytecode Verifier".
 261    *
 262    * @deprecated use getFirstCommonSuperclass(ReferenceType t) which has
 263    *             slightly changed semantics.
 264    */
 265   public ReferenceType firstCommonSuperclass(ReferenceType t) {
 266     if (this.equals(Type.NULL)) return t;
 267     if (t.equals(Type.NULL)) return this;
 268     if (this.equals(t)) return this;
 269     /*
 270      * TODO: Above sounds a little arbitrary. On the other hand, there is
 271      * no object referenced by Type.NULL so we can also say all the objects
 272      * referenced by Type.NULL were derived from java.lang.Object.
 273      * However, the Java Language's "instanceof" operator proves us wrong:
 274      * "null" is not referring to an instance of java.lang.Object :)
 275      */
 276 
 277     if ((this instanceof ArrayType) || (t instanceof ArrayType))
 278       return Type.OBJECT;
 279     // TODO: Is there a proof of OBJECT being the direct ancestor of every ArrayType?
 280 
 281     if (((this instanceof ObjectType) && ((ObjectType) this).referencesInterface()) ||
 282         ((t instanceof ObjectType) && ((ObjectType) t).referencesInterface()))
 283       return Type.OBJECT;
 284     // TODO: The above line is correct comparing to the vmspec2. But one could
 285     // make class file verification a bit stronger here by using the notion of
 286     // superinterfaces or even castability or assignment compatibility.
 287 
 288 
 289     // this and t are ObjectTypes, see above.
 290     ObjectType thiz = (ObjectType) this;
 291     ObjectType other = (ObjectType) t;
 292     JavaClass[] thiz_sups = Repository.getSuperClasses(thiz.getClassName());
 293     JavaClass[] other_sups = Repository.getSuperClasses(other.getClassName());
 294 
 295     if ((thiz_sups == null) || (other_sups == null)) {
 296       return null;
 297     }
 298 
 299     // Waaahh...
 300     JavaClass[] this_sups = new JavaClass[thiz_sups.length + 1];
 301     JavaClass[] t_sups = new JavaClass[other_sups.length + 1];
 302     System.arraycopy(thiz_sups, 0, this_sups, 1, thiz_sups.length);
 303     System.arraycopy(other_sups, 0, t_sups, 1, other_sups.length);
 304     this_sups[0] = Repository.lookupClass(thiz.getClassName());
 305     t_sups[0] = Repository.lookupClass(other.getClassName());
 306 
 307     for (int i = 0; i < t_sups.length; i++) {
 308       for (int j = 0; j < this_sups.length; j++) {
 309         if (this_sups[j].equals(t_sups[i])) return new ObjectType(this_sups[j].getClassName());
 310       }
 311     }
 312 
 313     // Huh? Did you ask for Type.OBJECT's superclass??
 314     return null;
 315   }
 316 }