001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 * 017 */ 018package org.apache.bcel.generic; 019 020import org.apache.bcel.Const; 021import org.apache.bcel.Repository; 022import org.apache.bcel.classfile.JavaClass; 023 024/** 025 * Super class for object and array types. 026 * 027 */ 028public abstract class ReferenceType extends Type { 029 030 protected ReferenceType(final byte t, final String s) { 031 super(t, s); 032 } 033 034 035 /** Class is non-abstract but not instantiable from the outside 036 */ 037 ReferenceType() { 038 super(Const.T_OBJECT, "<null object>"); 039 } 040 041 042 /** 043 * Return true iff this type is castable to another type t as defined in 044 * the JVM specification. The case where this is Type.NULL is not 045 * defined (see the CHECKCAST definition in the JVM specification). 046 * However, because e.g. CHECKCAST doesn't throw a 047 * ClassCastException when casting a null reference to any Object, 048 * true is returned in this case. 049 * 050 * @throws ClassNotFoundException if any classes or interfaces required 051 * to determine assignment compatibility can't be found 052 */ 053 public boolean isCastableTo( final Type t ) throws ClassNotFoundException { 054 if (this.equals(Type.NULL)) { 055 return t instanceof ReferenceType; // If this is ever changed in isAssignmentCompatible() 056 } 057 return isAssignmentCompatibleWith(t); 058 /* Yes, it's true: It's the same definition. 059 * See vmspec2 AASTORE / CHECKCAST definitions. 060 */ 061 } 062 063 064 /** 065 * Return true iff this is assignment compatible with another type t 066 * as defined in the JVM specification; see the AASTORE definition 067 * there. 068 * @throws ClassNotFoundException if any classes or interfaces required 069 * to determine assignment compatibility can't be found 070 */ 071 public boolean isAssignmentCompatibleWith( final Type t ) throws ClassNotFoundException { 072 if (!(t instanceof ReferenceType)) { 073 return false; 074 } 075 final ReferenceType T = (ReferenceType) t; 076 if (this.equals(Type.NULL)) { 077 return true; // This is not explicitely stated, but clear. Isn't it? 078 } 079 /* If this is a class type then 080 */ 081 if ((this instanceof ObjectType) && (((ObjectType) this).referencesClassExact())) { 082 /* If T is a class type, then this must be the same class as T, 083 or this must be a subclass of T; 084 */ 085 if ((T instanceof ObjectType) && (((ObjectType) T).referencesClassExact())) { 086 if (this.equals(T)) { 087 return true; 088 } 089 if (Repository.instanceOf(((ObjectType) this).getClassName(), ((ObjectType) T) 090 .getClassName())) { 091 return true; 092 } 093 } 094 /* If T is an interface type, this must implement interface T. 095 */ 096 if ((T instanceof ObjectType) && (((ObjectType) T).referencesInterfaceExact())) { 097 if (Repository.implementationOf(((ObjectType) this).getClassName(), 098 ((ObjectType) T).getClassName())) { 099 return true; 100 } 101 } 102 } 103 /* If this is an interface type, then: 104 */ 105 if ((this instanceof ObjectType) && (((ObjectType) this).referencesInterfaceExact())) { 106 /* If T is a class type, then T must be Object (�2.4.7). 107 */ 108 if ((T instanceof ObjectType) && (((ObjectType) T).referencesClassExact())) { 109 if (T.equals(Type.OBJECT)) { 110 return true; 111 } 112 } 113 /* If T is an interface type, then T must be the same interface 114 * as this or a superinterface of this (�2.13.2). 115 */ 116 if ((T instanceof ObjectType) && (((ObjectType) T).referencesInterfaceExact())) { 117 if (this.equals(T)) { 118 return true; 119 } 120 if (Repository.implementationOf(((ObjectType) this).getClassName(), 121 ((ObjectType) T).getClassName())) { 122 return true; 123 } 124 } 125 } 126 /* If this is an array type, namely, the type SC[], that is, an 127 * array of components of type SC, then: 128 */ 129 if (this instanceof ArrayType) { 130 /* If T is a class type, then T must be Object (�2.4.7). 131 */ 132 if ((T instanceof ObjectType) && (((ObjectType) T).referencesClassExact())) { 133 if (T.equals(Type.OBJECT)) { 134 return true; 135 } 136 } 137 /* If T is an array type TC[], that is, an array of components 138 * of type TC, then one of the following must be true: 139 */ 140 if (T instanceof ArrayType) { 141 /* TC and SC are the same primitive type (�2.4.1). 142 */ 143 final Type sc = ((ArrayType) this).getElementType(); 144 final Type tc = ((ArrayType) T).getElementType(); 145 if (sc instanceof BasicType && tc instanceof BasicType && sc.equals(tc)) { 146 return true; 147 } 148 /* TC and SC are reference types (�2.4.6), and type SC is 149 * assignable to TC by these runtime rules. 150 */ 151 if (tc instanceof ReferenceType && sc instanceof ReferenceType 152 && ((ReferenceType) sc).isAssignmentCompatibleWith(tc)) { 153 return true; 154 } 155 } 156 /* If T is an interface type, T must be one of the interfaces implemented by arrays (�2.15). */ 157 // TODO: Check if this is still valid or find a way to dynamically find out which 158 // interfaces arrays implement. However, as of the JVM specification edition 2, there 159 // are at least two different pages where assignment compatibility is defined and 160 // on one of them "interfaces implemented by arrays" is exchanged with "'Cloneable' or 161 // 'java.io.Serializable'" 162 if ((T instanceof ObjectType) && (((ObjectType) T).referencesInterfaceExact())) { 163 for (final String element : Const.getInterfacesImplementedByArrays()) { 164 if (T.equals(ObjectType.getInstance(element))) { 165 return true; 166 } 167 } 168 } 169 } 170 return false; // default. 171 } 172 173 174 /** 175 * This commutative operation returns the first common superclass (narrowest ReferenceType 176 * referencing a class, not an interface). 177 * If one of the types is a superclass of the other, the former is returned. 178 * If "this" is Type.NULL, then t is returned. 179 * If t is Type.NULL, then "this" is returned. 180 * If "this" equals t ['this.equals(t)'] "this" is returned. 181 * If "this" or t is an ArrayType, then Type.OBJECT is returned; 182 * unless their dimensions match. Then an ArrayType of the same 183 * number of dimensions is returned, with its basic type being the 184 * first common super class of the basic types of "this" and t. 185 * If "this" or t is a ReferenceType referencing an interface, then Type.OBJECT is returned. 186 * If not all of the two classes' superclasses cannot be found, "null" is returned. 187 * See the JVM specification edition 2, "�4.9.2 The Bytecode Verifier". 188 * 189 * @throws ClassNotFoundException on failure to find superclasses of this 190 * type, or the type passed as a parameter 191 */ 192 public ReferenceType getFirstCommonSuperclass( final ReferenceType t ) throws ClassNotFoundException { 193 if (this.equals(Type.NULL)) { 194 return t; 195 } 196 if (t.equals(Type.NULL)) { 197 return this; 198 } 199 if (this.equals(t)) { 200 return this; 201 /* 202 * TODO: Above sounds a little arbitrary. On the other hand, there is 203 * no object referenced by Type.NULL so we can also say all the objects 204 * referenced by Type.NULL were derived from java.lang.Object. 205 * However, the Java Language's "instanceof" operator proves us wrong: 206 * "null" is not referring to an instance of java.lang.Object :) 207 */ 208 } 209 /* This code is from a bug report by Konstantin Shagin <konst@cs.technion.ac.il> */ 210 if ((this instanceof ArrayType) && (t instanceof ArrayType)) { 211 final ArrayType arrType1 = (ArrayType) this; 212 final ArrayType arrType2 = (ArrayType) t; 213 if ((arrType1.getDimensions() == arrType2.getDimensions()) 214 && arrType1.getBasicType() instanceof ObjectType 215 && arrType2.getBasicType() instanceof ObjectType) { 216 return new ArrayType(((ObjectType) arrType1.getBasicType()) 217 .getFirstCommonSuperclass((ObjectType) arrType2.getBasicType()), arrType1 218 .getDimensions()); 219 } 220 } 221 if ((this instanceof ArrayType) || (t instanceof ArrayType)) { 222 return Type.OBJECT; 223 // TODO: Is there a proof of OBJECT being the direct ancestor of every ArrayType? 224 } 225 if (((this instanceof ObjectType) && ((ObjectType) this).referencesInterfaceExact()) 226 || ((t instanceof ObjectType) && ((ObjectType) t).referencesInterfaceExact())) { 227 return Type.OBJECT; 228 // TODO: The above line is correct comparing to the vmspec2. But one could 229 // make class file verification a bit stronger here by using the notion of 230 // superinterfaces or even castability or assignment compatibility. 231 } 232 // this and t are ObjectTypes, see above. 233 final ObjectType thiz = (ObjectType) this; 234 final ObjectType other = (ObjectType) t; 235 final JavaClass[] thiz_sups = Repository.getSuperClasses(thiz.getClassName()); 236 final JavaClass[] other_sups = Repository.getSuperClasses(other.getClassName()); 237 if ((thiz_sups == null) || (other_sups == null)) { 238 return null; 239 } 240 // Waaahh... 241 final JavaClass[] this_sups = new JavaClass[thiz_sups.length + 1]; 242 final JavaClass[] t_sups = new JavaClass[other_sups.length + 1]; 243 System.arraycopy(thiz_sups, 0, this_sups, 1, thiz_sups.length); 244 System.arraycopy(other_sups, 0, t_sups, 1, other_sups.length); 245 this_sups[0] = Repository.lookupClass(thiz.getClassName()); 246 t_sups[0] = Repository.lookupClass(other.getClassName()); 247 for (final JavaClass t_sup : t_sups) { 248 for (final JavaClass this_sup : this_sups) { 249 if (this_sup.equals(t_sup)) { 250 return ObjectType.getInstance(this_sup.getClassName()); 251 } 252 } 253 } 254 // Huh? Did you ask for Type.OBJECT's superclass?? 255 return null; 256 } 257 258 /** 259 * This commutative operation returns the first common superclass (narrowest ReferenceType 260 * referencing a class, not an interface). 261 * If one of the types is a superclass of the other, the former is returned. 262 * If "this" is Type.NULL, then t is returned. 263 * If t is Type.NULL, then "this" is returned. 264 * If "this" equals t ['this.equals(t)'] "this" is returned. 265 * If "this" or t is an ArrayType, then Type.OBJECT is returned. 266 * If "this" or t is a ReferenceType referencing an interface, then Type.OBJECT is returned. 267 * If not all of the two classes' superclasses cannot be found, "null" is returned. 268 * See the JVM specification edition 2, "�4.9.2 The Bytecode Verifier". 269 * 270 * @deprecated use getFirstCommonSuperclass(ReferenceType t) which has 271 * slightly changed semantics. 272 * @throws ClassNotFoundException on failure to find superclasses of this 273 * type, or the type passed as a parameter 274 */ 275 @Deprecated 276 public ReferenceType firstCommonSuperclass( final ReferenceType t ) throws ClassNotFoundException { 277 if (this.equals(Type.NULL)) { 278 return t; 279 } 280 if (t.equals(Type.NULL)) { 281 return this; 282 } 283 if (this.equals(t)) { 284 return this; 285 /* 286 * TODO: Above sounds a little arbitrary. On the other hand, there is 287 * no object referenced by Type.NULL so we can also say all the objects 288 * referenced by Type.NULL were derived from java.lang.Object. 289 * However, the Java Language's "instanceof" operator proves us wrong: 290 * "null" is not referring to an instance of java.lang.Object :) 291 */ 292 } 293 if ((this instanceof ArrayType) || (t instanceof ArrayType)) { 294 return Type.OBJECT; 295 // TODO: Is there a proof of OBJECT being the direct ancestor of every ArrayType? 296 } 297 if (((this instanceof ObjectType) && ((ObjectType) this).referencesInterface()) 298 || ((t instanceof ObjectType) && ((ObjectType) t).referencesInterface())) { 299 return Type.OBJECT; 300 // TODO: The above line is correct comparing to the vmspec2. But one could 301 // make class file verification a bit stronger here by using the notion of 302 // superinterfaces or even castability or assignment compatibility. 303 } 304 // this and t are ObjectTypes, see above. 305 final ObjectType thiz = (ObjectType) this; 306 final ObjectType other = (ObjectType) t; 307 final JavaClass[] thiz_sups = Repository.getSuperClasses(thiz.getClassName()); 308 final JavaClass[] other_sups = Repository.getSuperClasses(other.getClassName()); 309 if ((thiz_sups == null) || (other_sups == null)) { 310 return null; 311 } 312 // Waaahh... 313 final JavaClass[] this_sups = new JavaClass[thiz_sups.length + 1]; 314 final JavaClass[] t_sups = new JavaClass[other_sups.length + 1]; 315 System.arraycopy(thiz_sups, 0, this_sups, 1, thiz_sups.length); 316 System.arraycopy(other_sups, 0, t_sups, 1, other_sups.length); 317 this_sups[0] = Repository.lookupClass(thiz.getClassName()); 318 t_sups[0] = Repository.lookupClass(other.getClassName()); 319 for (final JavaClass t_sup : t_sups) { 320 for (final JavaClass this_sup : this_sups) { 321 if (this_sup.equals(t_sup)) { 322 return ObjectType.getInstance(this_sup.getClassName()); 323 } 324 } 325 } 326 // Huh? Did you ask for Type.OBJECT's superclass?? 327 return null; 328 } 329}