ReferenceType.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  *  Unless required by applicable law or agreed to in writing, software
  12.  *  distributed under the License is distributed on an "AS IS" BASIS,
  13.  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  *  See the License for the specific language governing permissions and
  15.  *  limitations under the License.
  16.  */
  17. package org.apache.bcel.generic;

  18. import org.apache.bcel.Const;
  19. import org.apache.bcel.Repository;
  20. import org.apache.bcel.classfile.JavaClass;

  21. /**
  22.  * Super class for object and array types.
  23.  */
  24. public abstract class ReferenceType extends Type {

  25.     /**
  26.      * Class is non-abstract but not instantiable from the outside
  27.      */
  28.     ReferenceType() {
  29.         super(Const.T_OBJECT, "<null object>");
  30.     }

  31.     protected ReferenceType(final byte t, final String s) {
  32.         super(t, s);
  33.     }

  34.     /**
  35.      * This commutative operation returns the first common superclass (narrowest ReferenceType referencing a class, not an
  36.      * interface). If one of the types is a superclass of the other, the former is returned. If "this" is NULL, then t
  37.      * is returned. If t is NULL, then "this" is returned. If "this" equals t ['this.equals(t)'] "this" is returned. If
  38.      * "this" or t is an ArrayType, then {@link #OBJECT} is returned. If "this" or t is a ReferenceType referencing an
  39.      * interface, then {@link #OBJECT} is returned. If not all of the two classes' superclasses cannot be found, "null" is
  40.      * returned. See the JVM specification edition 2, "�4.9.2 The Bytecode Verifier".
  41.      *
  42.      * @deprecated use getFirstCommonSuperclass(ReferenceType t) which has slightly changed semantics.
  43.      * @throws ClassNotFoundException on failure to find superclasses of this type, or the type passed as a parameter
  44.      */
  45.     @Deprecated
  46.     public ReferenceType firstCommonSuperclass(final ReferenceType t) throws ClassNotFoundException {
  47.         if (equals(NULL)) {
  48.             return t;
  49.         }
  50.         if (t.equals(NULL) || equals(t)) {
  51.             return this;
  52.             /*
  53.              * TODO: Above sounds a little arbitrary. On the other hand, there is no object referenced by {@link #NULL} so we can also
  54.              * say all the objects referenced by {@link #NULL} were derived from {@link Object}. However, the Java Language's
  55.              * "instanceof" operator proves us wrong: "null" is not referring to an instance of {@link Object} :)
  56.              */
  57.         }
  58.         if (this instanceof ArrayType || t instanceof ArrayType) {
  59.             return OBJECT;
  60.             // TODO: Is there a proof of {@link #OBJECT} being the direct ancestor of every ArrayType?
  61.         }
  62.         return getFirstCommonSuperclassInternal(t);
  63.     }

  64.     /**
  65.      * This commutative operation returns the first common superclass (narrowest ReferenceType referencing a class, not an
  66.      * interface). If one of the types is a superclass of the other, the former is returned. If "this" is NULL, then t
  67.      * is returned. If t is NULL, then "this" is returned. If "this" equals t ['this.equals(t)'] "this" is returned. If
  68.      * "this" or t is an ArrayType, then {@link #OBJECT} is returned; unless their dimensions match. Then an ArrayType of the
  69.      * same number of dimensions is returned, with its basic type being the first common super class of the basic types of
  70.      * "this" and t. If "this" or t is a ReferenceType referencing an interface, then {@link #OBJECT} is returned. If not all of
  71.      * the two classes' superclasses cannot be found, "null" is returned. See the JVM specification edition 2, "�4.9.2 The
  72.      * Bytecode Verifier".
  73.      *
  74.      * @throws ClassNotFoundException on failure to find superclasses of this type, or the type passed as a parameter
  75.      */
  76.     public ReferenceType getFirstCommonSuperclass(final ReferenceType t) throws ClassNotFoundException {
  77.         if (equals(NULL)) {
  78.             return t;
  79.         }
  80.         if (t.equals(NULL) || equals(t)) {
  81.             return this;
  82.             /*
  83.              * TODO: Above sounds a little arbitrary. On the other hand, there is no object referenced by {@link #NULL} so we can also
  84.              * say all the objects referenced by {@link #NULL} were derived from {@link Object}. However, the Java Language's
  85.              * "instanceof" operator proves us wrong: "null" is not referring to an instance of {@link Object} :)
  86.              */
  87.         }
  88.         /* This code is from a bug report by Konstantin Shagin <konst@cs.technion.ac.il> */
  89.         if (this instanceof ArrayType && t instanceof ArrayType) {
  90.             final ArrayType arrType1 = (ArrayType) this;
  91.             final ArrayType arrType2 = (ArrayType) t;
  92.             if (arrType1.getDimensions() == arrType2.getDimensions() && arrType1.getBasicType() instanceof ObjectType
  93.                 && arrType2.getBasicType() instanceof ObjectType) {
  94.                 return new ArrayType(((ObjectType) arrType1.getBasicType()).getFirstCommonSuperclass((ObjectType) arrType2.getBasicType()),
  95.                     arrType1.getDimensions());
  96.             }
  97.         }
  98.         if (this instanceof ArrayType || t instanceof ArrayType) {
  99.             return OBJECT;
  100.             // TODO: Is there a proof of {@link #OBJECT} being the direct ancestor of every ArrayType?
  101.         }
  102.         return getFirstCommonSuperclassInternal(t);
  103.     }

  104.     private ReferenceType getFirstCommonSuperclassInternal(final ReferenceType t) throws ClassNotFoundException {
  105.         if (this instanceof ObjectType && ((ObjectType) this).referencesInterfaceExact()
  106.             || t instanceof ObjectType && ((ObjectType) t).referencesInterfaceExact()) {
  107.             return OBJECT;
  108.             // TODO: The above line is correct comparing to the vmspec2. But one could
  109.             // make class file verification a bit stronger here by using the notion of
  110.             // superinterfaces or even castability or assignment compatibility.
  111.         }
  112.         // this and t are ObjectTypes, see above.
  113.         final ObjectType thiz = (ObjectType) this;
  114.         final ObjectType other = (ObjectType) t;
  115.         final JavaClass[] thizSups = Repository.getSuperClasses(thiz.getClassName());
  116.         final JavaClass[] otherSups = Repository.getSuperClasses(other.getClassName());
  117.         if (thizSups == null || otherSups == null) {
  118.             return null;
  119.         }
  120.         // Waaahh...
  121.         final JavaClass[] thisSups = new JavaClass[thizSups.length + 1];
  122.         final JavaClass[] tSups = new JavaClass[otherSups.length + 1];
  123.         System.arraycopy(thizSups, 0, thisSups, 1, thizSups.length);
  124.         System.arraycopy(otherSups, 0, tSups, 1, otherSups.length);
  125.         thisSups[0] = Repository.lookupClass(thiz.getClassName());
  126.         tSups[0] = Repository.lookupClass(other.getClassName());
  127.         for (final JavaClass tSup : tSups) {
  128.             for (final JavaClass thisSup : thisSups) {
  129.                 if (thisSup.equals(tSup)) {
  130.                     return ObjectType.getInstance(thisSup.getClassName());
  131.                 }
  132.             }
  133.         }
  134.         // Huh? Did you ask for OBJECT's superclass??
  135.         return null;
  136.     }

  137.     /**
  138.      * Return true iff this is assignment compatible with another type t as defined in the JVM specification; see the
  139.      * AASTORE definition there.
  140.      *
  141.      * @throws ClassNotFoundException if any classes or interfaces required to determine assignment compatibility can't be
  142.      *         found
  143.      */
  144.     public boolean isAssignmentCompatibleWith(final Type t) throws ClassNotFoundException {
  145.         if (!(t instanceof ReferenceType)) {
  146.             return false;
  147.         }
  148.         final ReferenceType T = (ReferenceType) t;
  149.         if (equals(NULL)) {
  150.             return true; // This is not explicitly stated, but clear. Isn't it?
  151.         }
  152.         /*
  153.          * If this is a class type then
  154.          */
  155.         if (this instanceof ObjectType && ((ObjectType) this).referencesClassExact()) {
  156.             /*
  157.              * If T is a class type, then this must be the same class as T, or this must be a subclass of T;
  158.              */
  159.             if (T instanceof ObjectType && ((ObjectType) T).referencesClassExact()
  160.                 && (equals(T) || Repository.instanceOf(((ObjectType) this).getClassName(), ((ObjectType) T).getClassName()))) {
  161.                 return true;
  162.             }
  163.             /*
  164.              * If T is an interface type, this must implement interface T.
  165.              */
  166.             if (T instanceof ObjectType && ((ObjectType) T).referencesInterfaceExact()
  167.                 && Repository.implementationOf(((ObjectType) this).getClassName(), ((ObjectType) T).getClassName())) {
  168.                 return true;
  169.             }
  170.         }
  171.         /*
  172.          * If this is an interface type, then:
  173.          */
  174.         if (this instanceof ObjectType && ((ObjectType) this).referencesInterfaceExact()) {
  175.             /*
  176.              * If T is a class type, then T must be Object (�2.4.7).
  177.              */
  178.             if (T instanceof ObjectType && ((ObjectType) T).referencesClassExact() && T.equals(OBJECT)) {
  179.                 return true;
  180.             }
  181.             /*
  182.              * If T is an interface type, then T must be the same interface as this or a superinterface of this (�2.13.2).
  183.              */
  184.             if (T instanceof ObjectType && ((ObjectType) T).referencesInterfaceExact()
  185.                 && (equals(T) || Repository.implementationOf(((ObjectType) this).getClassName(), ((ObjectType) T).getClassName()))) {
  186.                 return true;
  187.             }
  188.         }
  189.         /*
  190.          * If this is an array type, namely, the type SC[], that is, an array of components of type SC, then:
  191.          */
  192.         if (this instanceof ArrayType) {
  193.             /*
  194.              * If T is a class type, then T must be Object (�2.4.7).
  195.              */
  196.             if (T instanceof ObjectType && ((ObjectType) T).referencesClassExact() && T.equals(OBJECT)) {
  197.                 return true;
  198.             }
  199.             /*
  200.              * If T is an array type TC[], that is, an array of components of type TC, then one of the following must be true:
  201.              */
  202.             if (T instanceof ArrayType) {
  203.                 /*
  204.                  * TC and SC are the same primitive type (�2.4.1).
  205.                  */
  206.                 final Type sc = ((ArrayType) this).getElementType();
  207.                 final Type tc = ((ArrayType) T).getElementType();
  208.                 if (sc instanceof BasicType && tc instanceof BasicType && sc.equals(tc)) {
  209.                     return true;
  210.                 }
  211.                 /*
  212.                  * TC and SC are reference types (�2.4.6), and type SC is assignable to TC by these runtime rules.
  213.                  */
  214.                 if (tc instanceof ReferenceType && sc instanceof ReferenceType && ((ReferenceType) sc).isAssignmentCompatibleWith(tc)) {
  215.                     return true;
  216.                 }
  217.             }
  218.             /* If T is an interface type, T must be one of the interfaces implemented by arrays (�2.15). */
  219.             // TODO: Check if this is still valid or find a way to dynamically find out which
  220.             // interfaces arrays implement. However, as of the JVM specification edition 2, there
  221.             // are at least two different pages where assignment compatibility is defined and
  222.             // on one of them "interfaces implemented by arrays" is exchanged with "'Cloneable' or
  223.             // 'java.io.Serializable'"
  224.             if (T instanceof ObjectType && ((ObjectType) T).referencesInterfaceExact()) {
  225.                 for (final String element : Const.getInterfacesImplementedByArrays()) {
  226.                     if (T.equals(ObjectType.getInstance(element))) {
  227.                         return true;
  228.                     }
  229.                 }
  230.             }
  231.         }
  232.         return false; // default.
  233.     }

  234.     /**
  235.      * Return true iff this type is castable to another type t as defined in the JVM specification. The case where this is
  236.      * {@link #NULL} is not defined (see the CHECKCAST definition in the JVM specification). However, because e.g. CHECKCAST
  237.      * doesn't throw a ClassCastException when casting a null reference to any Object, true is returned in this case.
  238.      *
  239.      * @throws ClassNotFoundException if any classes or interfaces required to determine assignment compatibility can't be
  240.      *         found
  241.      */
  242.     public boolean isCastableTo(final Type t) throws ClassNotFoundException {
  243.         if (equals(NULL)) {
  244.             return t instanceof ReferenceType; // If this is ever changed in isAssignmentCompatible()
  245.         }
  246.         return isAssignmentCompatibleWith(t);
  247.         /*
  248.          * Yes, it's true: It's the same definition. See vmspec2 AASTORE / CHECKCAST definitions.
  249.          */
  250.     }
  251. }