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