View Javadoc

1   /*
2    * Copyright  2000-2004 The Apache Software Foundation
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License"); 
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License. 
15   *
16   */
17  package org.apache.bcel.generic;
18  
19  import org.apache.bcel.Constants;
20  import org.apache.bcel.Repository;
21  import org.apache.bcel.classfile.JavaClass;
22  
23  /***
24   * Super class for object and array types.
25   *
26   * @version $Id: ReferenceType.java 386056 2006-03-15 11:31:56Z tcurdt $
27   * @author  <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
28   */
29  public abstract class ReferenceType extends Type {
30  
31      protected ReferenceType(byte t, 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(Constants.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( Type t ) throws ClassNotFoundException {
55          if (this.equals(Type.NULL)) {
56              return true; // 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( Type t ) throws ClassNotFoundException {
73          if (!(t instanceof ReferenceType)) {
74              return false;
75          }
76          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                 Type sc = ((ArrayType) this).getElementType();
145                 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 (int ii = 0; ii < Constants.INTERFACES_IMPLEMENTED_BY_ARRAYS.length; ii++) {
165                     if (T.equals(new ObjectType(Constants.INTERFACES_IMPLEMENTED_BY_ARRAYS[ii]))) {
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( 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             ArrayType arrType1 = (ArrayType) this;
213             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).referencesInterface())
227                 || ((t instanceof ObjectType) && ((ObjectType) t).referencesInterface())) {
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         ObjectType thiz = (ObjectType) this;
235         ObjectType other = (ObjectType) t;
236         JavaClass[] thiz_sups = Repository.getSuperClasses(thiz.getClassName());
237         JavaClass[] other_sups = Repository.getSuperClasses(other.getClassName());
238         if ((thiz_sups == null) || (other_sups == null)) {
239             return null;
240         }
241         // Waaahh...
242         JavaClass[] this_sups = new JavaClass[thiz_sups.length + 1];
243         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 (int i = 0; i < t_sups.length; i++) {
249             for (int j = 0; j < this_sups.length; j++) {
250                 if (this_sups[j].equals(t_sups[i])) {
251                     return new ObjectType(this_sups[j].getClassName());
252                 }
253             }
254         }
255         // Huh? Did you ask for Type.OBJECT's superclass??
256         return null;
257     }
258 
259 
260     /***
261      * This commutative operation returns the first common superclass (narrowest ReferenceType
262      * referencing a class, not an interface).
263      * If one of the types is a superclass of the other, the former is returned.
264      * If "this" is Type.NULL, then t is returned.
265      * If t is Type.NULL, then "this" is returned.
266      * If "this" equals t ['this.equals(t)'] "this" is returned.
267      * If "this" or t is an ArrayType, then Type.OBJECT is returned.
268      * If "this" or t is a ReferenceType referencing an interface, then Type.OBJECT is returned.
269      * If not all of the two classes' superclasses cannot be found, "null" is returned.
270      * See the JVM specification edition 2, "ß4.9.2 The Bytecode Verifier".
271      *
272      * @deprecated use getFirstCommonSuperclass(ReferenceType t) which has
273      *             slightly changed semantics.
274      * @throws ClassNotFoundException on failure to find superclasses of this
275      *  type, or the type passed as a parameter
276      */
277     public ReferenceType firstCommonSuperclass( 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         ObjectType thiz = (ObjectType) this;
307         ObjectType other = (ObjectType) t;
308         JavaClass[] thiz_sups = Repository.getSuperClasses(thiz.getClassName());
309         JavaClass[] other_sups = Repository.getSuperClasses(other.getClassName());
310         if ((thiz_sups == null) || (other_sups == null)) {
311             return null;
312         }
313         // Waaahh...
314         JavaClass[] this_sups = new JavaClass[thiz_sups.length + 1];
315         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 (int i = 0; i < t_sups.length; i++) {
321             for (int j = 0; j < this_sups.length; j++) {
322                 if (this_sups[j].equals(t_sups[i])) {
323                     return new ObjectType(this_sups[j].getClassName());
324                 }
325             }
326         }
327         // Huh? Did you ask for Type.OBJECT's superclass??
328         return null;
329     }
330 }