001/* 002 * This file is part of the Jikes RVM project (http://jikesrvm.org). 003 * 004 * This file is licensed to You under the Eclipse Public License (EPL); 005 * You may not use this file except in compliance with the License. You 006 * may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/eclipse-1.0.php 009 * 010 * See the COPYRIGHT.txt file distributed with this work for information 011 * regarding copyright ownership. 012 */ 013package org.jikesrvm.compilers.opt; 014 015import static org.jikesrvm.compilers.opt.bc2ir.IRGenOptions.DBG_TYPE; 016import static org.jikesrvm.compilers.opt.driver.OptConstants.MAYBE; 017import static org.jikesrvm.compilers.opt.driver.OptConstants.NO; 018import static org.jikesrvm.compilers.opt.driver.OptConstants.YES; 019 020import org.jikesrvm.VM; 021import org.jikesrvm.classloader.Atom; 022import org.jikesrvm.classloader.MethodReference; 023import org.jikesrvm.classloader.RVMClass; 024import org.jikesrvm.classloader.RVMMethod; 025import org.jikesrvm.classloader.TypeReference; 026import org.jikesrvm.compilers.opt.ir.operand.ClassConstantOperand; 027import org.jikesrvm.compilers.opt.ir.operand.DoubleConstantOperand; 028import org.jikesrvm.compilers.opt.ir.operand.FloatConstantOperand; 029import org.jikesrvm.compilers.opt.ir.operand.IntConstantOperand; 030import org.jikesrvm.compilers.opt.ir.operand.LongConstantOperand; 031import org.jikesrvm.compilers.opt.ir.operand.StringConstantOperand; 032import org.jikesrvm.compilers.opt.util.Stack; 033import org.jikesrvm.runtime.RuntimeEntrypoints; 034import org.jikesrvm.runtime.Statics; 035import org.vmmagic.unboxed.Offset; 036 037public final class ClassLoaderProxy { 038 039 /** 040 * Returns a common superclass of the two types. 041 * NOTE: If both types are references, but are not both loaded, then this 042 * may be a conservative approximation (java.lang.Object). 043 * 044 * @param t1 first type 045 * @param t2 second type 046 * @return a common superclass or {@code null} if there's none 047 */ 048 public static TypeReference findCommonSuperclass(TypeReference t1, TypeReference t2) { 049 if (t1 == t2) { 050 return t1; 051 } 052 053 if (t1.isPrimitiveType() || t2.isPrimitiveType()) { 054 if (t1.isIntLikeType() && t2.isIntLikeType()) { 055 // 2 non-identical int like types, return the largest 056 if (t1.isIntType() || t2.isIntType()) { 057 return TypeReference.Int; 058 } else if (t1.isCharType() || t2.isCharType()) { 059 return TypeReference.Char; 060 } else if (t1.isShortType() || t2.isShortType()) { 061 return TypeReference.Short; 062 } else if (t1.isByteType() || t2.isByteType()) { 063 return TypeReference.Byte; 064 } else { 065 if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED); 066 return null; 067 } 068 } else if (t1.isWordLikeType() && t2.isWordLikeType()) { 069 return TypeReference.Word; 070 } else { 071 // other primitive and unboxed types have no commonality so return null 072 return null; 073 } 074 } 075 076 //Neither t1 nor t2 are primitive or unboxed types at this point 077 078 // Is either t1 or t2 null? Null is assignable to all types so the type of 079 // the other operand is the most precise 080 if (t1 == TypeReference.NULL_TYPE) { 081 return t2; 082 } else if (t2 == TypeReference.NULL_TYPE) { 083 return t1; 084 } 085 086 if (DBG_TYPE) { 087 VM.sysWrite("finding common supertype of " + t1 + " and " + t2); 088 } 089 090 // Strip off all array junk. 091 int arrayDimensions = 0; 092 while (t1.isArrayType() && t2.isArrayType()) { 093 ++arrayDimensions; 094 t1 = t1.getArrayElementType(); 095 t2 = t2.getArrayElementType(); 096 } 097 // at this point, they are not both array types. 098 // if one is a primitive, then we want an object array of one less 099 // dimensionality 100 if (t1.isPrimitiveType() || t2.isPrimitiveType()) { 101 TypeReference type = TypeReference.JavaLangObject; 102 if (t1 == t2) { 103 //Unboxed types are wrapped in their own array objects 104 if (t1.isUnboxedType()) { 105 arrayDimensions++; 106 type = t1; 107 } else { 108 if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED); 109 } 110 } 111 --arrayDimensions; 112 while (arrayDimensions-- > 0) { 113 type = type.getArrayTypeForElementType(); 114 } 115 if (DBG_TYPE) { 116 VM.sysWrite("one is a primitive array, so supertype is " + type); 117 } 118 return type; 119 } 120 121 // At this point neither t1 or t2 is a primitive or word type and either 122 // one or the other maybe an array type 123 124 // is this a case of arrays with different dimensionalities? 125 if (t1.isArrayType() || t2.isArrayType()) { 126 // one is a class type, while the other is an array 127 TypeReference type = TypeReference.JavaLangObject; 128 while (arrayDimensions-- > 0) { 129 type = type.getArrayTypeForElementType(); 130 } 131 if (DBG_TYPE) { 132 VM.sysWrite("differing dimensionalities for arrays, so supertype is " + type); 133 } 134 return type; 135 } 136 // At this point they both must be class types. 137 138 // technique: push heritage of each type on a separate stack, 139 // then find the highest point in the stack where they differ. 140 RVMClass c1 = (RVMClass) t1.peekType(); 141 RVMClass c2 = (RVMClass) t2.peekType(); 142 if (c1 != null && c2 != null) { 143 // The ancestor hierarchy is available, so do this exactly 144 Stack<RVMClass> s1 = new Stack<RVMClass>(); 145 do { 146 s1.push(c1); 147 c1 = c1.getSuperClass(); 148 } while (c1 != null); 149 Stack<RVMClass> s2 = new Stack<RVMClass>(); 150 do { 151 s2.push(c2); 152 c2 = c2.getSuperClass(); 153 } while (c2 != null); 154 if (DBG_TYPE) { 155 VM.sysWrite("stack 1: " + s1); 156 } 157 if (DBG_TYPE) { 158 VM.sysWrite("stack 2: " + s2); 159 } 160 TypeReference best = TypeReference.JavaLangObject; 161 while (!s1.empty() && !s2.empty()) { 162 RVMClass temp = s1.pop(); 163 if (temp == s2.pop()) { 164 best = temp.getTypeRef(); 165 } else { 166 break; 167 } 168 } 169 if (DBG_TYPE) { 170 VM.sysWrite("common supertype of the two classes is " + best); 171 } 172 while (arrayDimensions-- > 0) { 173 best = best.getArrayTypeForElementType(); 174 } 175 return best; 176 } else { 177 if (DBG_TYPE && c1 == null) { 178 VM.sysWrite(c1 + " is not loaded, using Object as common supertype"); 179 } 180 if (DBG_TYPE && c2 == null) { 181 VM.sysWrite(c2 + " is not loaded, using Object as common supertype"); 182 } 183 TypeReference common = TypeReference.JavaLangObject; 184 while (arrayDimensions-- > 0) { 185 common = common.getArrayTypeForElementType(); 186 } 187 return common; 188 } 189 } 190 191 /** 192 * Return Constants.YES if the parent type is defintely a supertype 193 * of the child type. 194 * <p> Return Constants.NO if the parent type is definitely not 195 * a supertype of the child type. 196 * <p> Return Constants.MAYBE if the question cannot be currently answered 197 * (for example if one/both of the classes is not resolved) 198 * 199 * <p> Takes into account the special 'null-type', which corresponds to a {@code null} 200 * constant. 201 * 202 * @param parentType parent type 203 * @param childType child type 204 * @return Constants.YES, Constants.NO, or Constants.MAYBE 205 */ 206 public static byte includesType(TypeReference parentType, TypeReference childType) { 207 // First handle some cases that we can answer without needing to 208 // look at the type hierarchy 209 // NOTE: The ordering of these tests is critical! 210 if (childType == TypeReference.NULL_TYPE) { 211 // Sanity assertion that a null isn't being assigned to an unboxed type 212 if (VM.VerifyAssertions && parentType.isReferenceType()) VM._assert(!parentType.isWordLikeType()); 213 return parentType.isReferenceType() ? YES : NO; 214 } else if (parentType == TypeReference.NULL_TYPE) { 215 return NO; 216 } else if (parentType == childType) { 217 return YES; 218 } else if (parentType == TypeReference.Word && childType.isWordLikeType()) { 219 return YES; 220 } else if (parentType.isPrimitiveType() || childType.isPrimitiveType()) { 221 return NO; 222 } else if (parentType == TypeReference.JavaLangObject) { 223 return YES; 224 } else { 225 // Unboxed types are handled in the word and primitive type case 226 if (VM.VerifyAssertions) { 227 VM._assert(!parentType.isWordLikeType() && !childType.isWordLikeType()); 228 } 229 // Oh well, we're going to have to try to actually look 230 // at the type hierarchy. 231 // IMPORTANT: We aren't allowed to cause dynamic class loading, 232 // so we have to roll some of this ourselves 233 // instead of simply calling RuntimeEntrypoints.instanceOf 234 // (which is allowed/required to load classes to answer the question). 235 try { 236 if (parentType.isArrayType()) { 237 if (childType == TypeReference.JavaLangObject) { 238 return MAYBE; // arrays are subtypes of Object. 239 } else if (!childType.isArrayType()) { 240 return NO; 241 } else { 242 TypeReference parentET = parentType.getInnermostElementType(); 243 if (parentET == TypeReference.JavaLangObject) { 244 int LHSDimension = parentType.getDimensionality(); 245 int RHSDimension = childType.getDimensionality(); 246 if ((RHSDimension > LHSDimension) || 247 (RHSDimension == LHSDimension && childType.getInnermostElementType().isClassType())) { 248 return YES; 249 } else { 250 return NO; 251 } 252 } else { 253 // parentType is [^k of something other than Object 254 // If dimensionalities are equal, then we can reduce 255 // to isAssignableWith(parentET, childET). 256 // If the dimensionalities are not equal then the answer is NO 257 if (parentType.getDimensionality() == childType.getDimensionality()) { 258 return includesType(parentET, childType.getInnermostElementType()); 259 } else { 260 return NO; 261 } 262 } 263 } 264 } else { // parentType.isClassType() 265 if (!childType.isClassType()) { 266 // parentType is known to not be java.lang.Object. 267 return NO; 268 } else { 269 RVMClass childClass = (RVMClass) childType.peekType(); 270 RVMClass parentClass = (RVMClass) parentType.peekType(); 271 if (childClass != null && parentClass != null) { 272 if (parentClass.isResolved() && childClass.isResolved() || 273 (VM.writingBootImage && parentClass.isInBootImage() && childClass.isInBootImage())) { 274 if (parentClass.isInterface()) { 275 if (RuntimeEntrypoints.isAssignableWith(parentClass, childClass)) { 276 return YES; 277 } else { 278 // If child is not a final class, it is 279 // possible that a subclass will implement parent. 280 return childClass.isFinal() ? NO : MAYBE; 281 } 282 } else if (childClass.isInterface()) { 283 // parent is a proper class, child is an interface 284 return MAYBE; 285 } else { 286 // parent & child are both proper classes. 287 if (RuntimeEntrypoints.isAssignableWith(parentClass, childClass)) { 288 return YES; 289 } 290 // If child is a final class, then 291 // !instanceOfClass(parent, child) lets us return NO. 292 // However, if child is not final, then it might have 293 // subclasses so we can't return NO out of hand. 294 // But, if the reverse instanceOf is also false, then we know 295 // that parent and child are completely 296 // unrelated and we can return NO. 297 if (childClass.isFinal()) { 298 return NO; 299 } else { 300 if (RuntimeEntrypoints.isAssignableWith(childClass, parentClass)) { 301 return MAYBE; 302 } else { 303 return NO; 304 } 305 } 306 } 307 } 308 } 309 return MAYBE; 310 } 311 } 312 } catch (Throwable e) { 313 e.printStackTrace(); 314 OptimizingCompilerException.UNREACHABLE(); 315 return MAYBE; 316 } 317 } 318 } 319 320 // -------------------------------------------------------------------------- 321 // Querry classloader data structures 322 // -------------------------------------------------------------------------- 323 324 /** 325 * Find the method of the given class that matches the given descriptor. 326 * 327 * @param cls the method's class 328 * @param ref name and descriptor of the method 329 * @return a matching method or {@code null} if none was found 330 */ 331 public static RVMMethod lookupMethod(RVMClass cls, MethodReference ref) { 332 RVMMethod newmeth = null; 333 if (cls.isResolved() && !cls.isInterface()) { 334 Atom mn = ref.getName(); 335 Atom md = ref.getDescriptor(); 336 for (; (newmeth == null) && (cls != null); cls = cls.getSuperClass()) { 337 newmeth = cls.findDeclaredMethod(mn, md); 338 } 339 } 340 return newmeth; 341 } 342 343 // -------------------------------------------------------------------------- 344 // Constant pool access 345 // -------------------------------------------------------------------------- 346 347 public static IntConstantOperand getIntFromConstantPool(RVMClass klass, int index) { 348 Offset offset = klass.getLiteralOffset(index); 349 int val = Statics.getSlotContentsAsInt(offset); 350 return new IntConstantOperand(val); 351 } 352 353 public static DoubleConstantOperand getDoubleFromConstantPool(RVMClass klass, int index) { 354 Offset offset = klass.getLiteralOffset(index); 355 long val_raw = Statics.getSlotContentsAsLong(offset); 356 double val = Double.longBitsToDouble(val_raw); 357 return new DoubleConstantOperand(val, offset); 358 } 359 360 public static FloatConstantOperand getFloatFromConstantPool(RVMClass klass, int index) { 361 Offset offset = klass.getLiteralOffset(index); 362 int val_raw = Statics.getSlotContentsAsInt(offset); 363 float val = Float.intBitsToFloat(val_raw); 364 return new FloatConstantOperand(val, offset); 365 } 366 367 public static LongConstantOperand getLongFromConstantPool(RVMClass klass, int index) { 368 Offset offset = klass.getLiteralOffset(index); 369 long val = Statics.getSlotContentsAsLong(offset); 370 return new LongConstantOperand(val); 371 } 372 373 public static StringConstantOperand getStringFromConstantPool(RVMClass klass, int index) { 374 Offset offset = klass.getLiteralOffset(index); 375 try { 376 String val; 377 val = (String) Statics.getSlotContentsAsObject(offset); 378 return new StringConstantOperand(val, offset); 379 } catch (ClassCastException e) { 380 throw new Error("Corrupt JTOC at offset " + offset.toInt(), e); 381 } 382 } 383 384 public static ClassConstantOperand getClassFromConstantPool(RVMClass klass, int index) { 385 Offset offset = klass.getLiteralOffset(index); 386 try { 387 Class<?> val = (Class<?>) Statics.getSlotContentsAsObject(offset); 388 return new ClassConstantOperand(val, offset); 389 } catch (ClassCastException e) { 390 throw new Error("Corrupt JTOC at offset " + offset.toInt(), e); 391 } 392 } 393}