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