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