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 */ 017package org.apache.bcel.generic; 018 019import org.apache.bcel.Const; 020import org.apache.bcel.Repository; 021import org.apache.bcel.classfile.JavaClass; 022 023/** 024 * Super class for object and array types. 025 */ 026public abstract class ReferenceType extends Type { 027 028 /** 029 * Class is non-abstract but not instantiable from the outside 030 */ 031 ReferenceType() { 032 super(Const.T_OBJECT, "<null object>"); 033 } 034 035 protected ReferenceType(final byte t, final String s) { 036 super(t, s); 037 } 038 039 /** 040 * This commutative operation returns the first common superclass (narrowest ReferenceType referencing a class, not an 041 * interface). If one of the types is a superclass of the other, the former is returned. If "this" is Type.NULL, then t 042 * is returned. If t is Type.NULL, then "this" is returned. If "this" equals t ['this.equals(t)'] "this" is returned. If 043 * "this" or t is an ArrayType, then Type.OBJECT is returned. If "this" or t is a ReferenceType referencing an 044 * interface, then Type.OBJECT is returned. If not all of the two classes' superclasses cannot be found, "null" is 045 * returned. See the JVM specification edition 2, "�4.9.2 The Bytecode Verifier". 046 * 047 * @deprecated use getFirstCommonSuperclass(ReferenceType t) which has slightly changed semantics. 048 * @throws ClassNotFoundException on failure to find superclasses of this type, or the type passed as a parameter 049 */ 050 @Deprecated 051 public ReferenceType firstCommonSuperclass(final ReferenceType t) throws ClassNotFoundException { 052 if (this.equals(Type.NULL)) { 053 return t; 054 } 055 if (t.equals(Type.NULL) || this.equals(t)) { 056 return this; 057 /* 058 * TODO: Above sounds a little arbitrary. On the other hand, there is no object referenced by Type.NULL so we can also 059 * say all the objects referenced by Type.NULL were derived from {@link Object}. However, the Java Language's 060 * "instanceof" operator proves us wrong: "null" is not referring to an instance of {@link Object} :) 061 */ 062 } 063 if (this instanceof ArrayType || t instanceof ArrayType) { 064 return Type.OBJECT; 065 // TODO: Is there a proof of OBJECT being the direct ancestor of every ArrayType? 066 } 067 return getFirstCommonSuperclassInternal(t); 068 } 069 070 /** 071 * This commutative operation returns the first common superclass (narrowest ReferenceType referencing a class, not an 072 * interface). If one of the types is a superclass of the other, the former is returned. If "this" is Type.NULL, then t 073 * is returned. If t is Type.NULL, then "this" is returned. If "this" equals t ['this.equals(t)'] "this" is returned. If 074 * "this" or t is an ArrayType, then Type.OBJECT is returned; unless their dimensions match. Then an ArrayType of the 075 * same number of dimensions is returned, with its basic type being the first common super class of the basic types of 076 * "this" and t. If "this" or t is a ReferenceType referencing an interface, then Type.OBJECT is returned. If not all of 077 * the two classes' superclasses cannot be found, "null" is returned. See the JVM specification edition 2, "�4.9.2 The 078 * Bytecode Verifier". 079 * 080 * @throws ClassNotFoundException on failure to find superclasses of this type, or the type passed as a parameter 081 */ 082 public ReferenceType getFirstCommonSuperclass(final ReferenceType t) throws ClassNotFoundException { 083 if (this.equals(Type.NULL)) { 084 return t; 085 } 086 if (t.equals(Type.NULL) || this.equals(t)) { 087 return this; 088 /* 089 * TODO: Above sounds a little arbitrary. On the other hand, there is no object referenced by Type.NULL so we can also 090 * say all the objects referenced by Type.NULL were derived from {@link Object}. However, the Java Language's 091 * "instanceof" operator proves us wrong: "null" is not referring to an instance of {@link Object} :) 092 */ 093 } 094 /* This code is from a bug report by Konstantin Shagin <konst@cs.technion.ac.il> */ 095 if (this instanceof ArrayType && t instanceof ArrayType) { 096 final ArrayType arrType1 = (ArrayType) this; 097 final ArrayType arrType2 = (ArrayType) t; 098 if (arrType1.getDimensions() == arrType2.getDimensions() && arrType1.getBasicType() instanceof ObjectType 099 && 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}