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