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