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$
28  */
29 public abstract class ReferenceType extends Type {
30 
ReferenceType(final byte t, final String s)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      */
ReferenceType()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      */
isCastableTo( final Type t )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      */
isAssignmentCompatibleWith( final Type t )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      */
getFirstCommonSuperclass( final ReferenceType t )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
firstCommonSuperclass( final ReferenceType t )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 }
331