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 }