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