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.classloader; 014 015import org.jikesrvm.VM; 016import org.jikesrvm.mm.mminterface.MemoryManager; 017import org.jikesrvm.objectmodel.TIB; 018import org.jikesrvm.runtime.Magic; 019import org.vmmagic.pragma.Uninterruptible; 020 021/** 022 * Data structures and code for fast dynamic type checking. 023 * <p> 024 * As a convention, we convert all dynamic type checking 025 * operations into the following question: LHS :?= RHS 026 * (i.e. can an instance of the RHS class be stored in a 027 * variable of the LHS class or interface.) This question 028 * arises for four bytecodes: instanceof, checkcast, aastore 029 * and invokeinterface and entry into catch blocks. 030 * This gives us a uniform terminology, but in some cases 031 * (instanceof) can be somewhat counter-intuitive since despite 032 * the fact that the Java source code is written as 033 * <code>x instanceof C</code>, for the purposes of dynamic type checking 034 * <code>x</code> is the RHS and <code>C</code> is the LHS! 035 * <p> 036 * The idea of the optimizations presented below is to treat 037 * each context in which these queries arises as a special 038 * case to be optimised in isolation. Consider the following 039 * taxonomy of dynamic type checking conexts: 040 * <p> 041 * (1) Is the LHS unknown at compile time? True only for aastore? 042 * If so, the following test will be fast in most instances: 043 * is the runtime type of the LHS array the same as compile-time 044 * type of the variable that contains it? If so, the Java-to-bytecode 045 * compiler (and the verifier) guarantees that the test passes. 046 * Unfortunately, this test can only be used in two of three cases: 047 * when the LHS variable is a field or a parameter. When the LHS is 048 * in a local variable the Java-to-bytecode compiler has thrown away 049 * the necessary type information. 050 * <p> 051 * (2) Otherwise, is the LHS an array? 052 * If so, there are three sub-cases 053 * <ul> 054 * <li>(2a) LHS is [^k primitive: 055 * If so, the dimensionality of the RHS must be k 056 * and the baseclass of the RHS must be the same primitive 057 * <li>(2b) LHS is [^k class: 058 * If so, the dimensionality of the RHS must be k 059 * and the baseclass of the RHS must be assignable with class (see #3) 060 * _OR_ the dimensionality of the RHS must be >k 061 * and the baseclass of the LHS is java.lang.Cloneable or java.io.Serializable 062 * <li>(2c) LHS is [^k Ljava.lang.Object: 063 * If so, either the dimensionality of the RHS is greater than k 064 * or, this dimensionality is k and the baseclass is NOT primitive 065 * </ul> 066 * <p> 067 * (3) Otherwise, is the LHS unresolved? 068 * If so, fall back to calling RuntimeEntrypoints.instanceOf at runtime which will 069 * load/resolve the types and then call DynamicTypeCheck.instanceOf. 070 * <p> 071 * (4) Otherwise, is the LHS an interface? 072 * If so, query the doesImplement array of the RHS's TIB at the entry 073 * for the interface ID. If a class does not directly implement any 074 * interfaces then it inherits the doesImplement array from its superclass. 075 * <p> 076 * (5) Otherwise, is the depth of the LHS greater than 077 * MIN_SUPERCLASS_IDS_SIZE? If so, if LHS depth is greater that 078 * RHS's superclassIds.length, the test fails. Else, see #6. 079 * <p> 080 * (6) Otherwise. If the LHS depth component of the RHS's superclassIds 081 * array is the LHS class ID, the test succeeds. Else, it fails. 082 * <p> 083 * For details about the expansion of type checks in the optimizing compiler, see 084 * the transformation from high level IR to low level IR. 085 * 086 * @see RVMType 087 * @see RVMClass 088 * @see RVMArray 089 */ 090public class DynamicTypeCheck { 091 092 /** 093 * Minimum length of the superclassIds array in TIB. 094 * Note: this array is padded to save a index out of 095 * bounds test for classes with shallow class depth. 096 */ 097 public static final int MIN_SUPERCLASS_IDS_SIZE = 6; // a short[], so multiple of 2. 098 099 /** 100 * Minimum length of the doesImplements array in TIB. 101 * Note: this array is padded to save a index out of 102 * bounds test for the first 32 * MIN_DOES_IMPLEMENT_SIZE interfaces loaded. 103 */ 104 public static final int MIN_DOES_IMPLEMENT_SIZE = 5; // an int[] 105 106 /** 107 * Create the superclass Id vector for a RVMType. 108 * 109 * @param t a RVMType to create a superclass Id vector for 110 * @return the superclass Id vector 111 */ 112 static short[] buildSuperclassIds(RVMType t) { 113 int depth = t.getTypeDepth(); 114 short[] tsi; 115 if (t.isJavaLangObjectType()) { 116 if (VM.VerifyAssertions) VM._assert(depth == 0); 117 tsi = MemoryManager.newNonMovingShortArray(1); 118 } else { 119 int size = MIN_SUPERCLASS_IDS_SIZE <= depth ? depth + 1 : MIN_SUPERCLASS_IDS_SIZE; 120 tsi = MemoryManager.newNonMovingShortArray(size); 121 RVMType p; 122 if (t.isArrayType() || t.asClass().isInterface()) { 123 p = RVMType.JavaLangObjectType; 124 } else { 125 p = t.asClass().getSuperClass(); 126 } 127 short[] psi = p.getSuperclassIds(); 128 for (int i = 0; i < depth; i++) { 129 tsi[i] = psi[i]; 130 } 131 } 132 int id = t.getId(); 133 if (VM.VerifyAssertions) VM._assert(id <= 0xFFFF); // when this fails, make superclassIds int[] 134 tsi[depth] = (short) id; 135 return tsi; 136 } 137 138 private static int[] arrayDoesImplement; 139 140 /** 141 * Create the doesImplement vector for a RVMArray. 142 * All arrays implement exactly java.io.Serializable and java.lang.Cloneable. 143 * 144 * @param t a RVMArray to create a doesImplement vector for 145 * @return the doesImplement vector 146 */ 147 static int[] buildDoesImplement(RVMArray t) { 148 if (arrayDoesImplement == null) { 149 int cloneIdx = RVMType.JavaLangCloneableType.getDoesImplementIndex(); 150 int serialIdx = RVMType.JavaIoSerializableType.getDoesImplementIndex(); 151 int size = Math.max(cloneIdx, serialIdx); 152 size = Math.max(MIN_DOES_IMPLEMENT_SIZE, size + 1); 153 int[] tmp = MemoryManager.newNonMovingIntArray(size); 154 tmp[cloneIdx] = RVMType.JavaLangCloneableType.getDoesImplementBitMask(); 155 tmp[serialIdx] |= RVMType.JavaIoSerializableType.getDoesImplementBitMask(); 156 arrayDoesImplement = tmp; 157 } 158 return arrayDoesImplement; 159 } 160 161 /** 162 * Create the doesImplement vector for a RVMClass. 163 * 164 * @param t a RVMClass to create a doesImplement vector for 165 * @return the doesImplement vector 166 */ 167 static int[] buildDoesImplement(RVMClass t) { 168 if (t.isJavaLangObjectType()) { 169 // object implements no interfaces. 170 return MemoryManager.newNonMovingIntArray(MIN_DOES_IMPLEMENT_SIZE); 171 } 172 173 RVMClass[] superInterfaces = t.getDeclaredInterfaces(); 174 175 if (!t.isInterface() && superInterfaces.length == 0) { 176 // I add nothing new; share with parent. 177 return t.getSuperClass().getDoesImplement(); 178 } 179 180 // I need one of my own; first figure out how big it needs to be. 181 int size; 182 if (t.isInterface()) { 183 size = Math.max(MIN_DOES_IMPLEMENT_SIZE, t.getDoesImplementIndex() + 1); 184 } else { 185 size = t.getSuperClass().getDoesImplement().length; 186 } 187 for (RVMClass superInterface : superInterfaces) { 188 size = Math.max(size, superInterface.getDoesImplement().length); 189 } 190 191 // then create and populate it 192 int[] mine = MemoryManager.newNonMovingIntArray(size); 193 if (t.isInterface()) { 194 mine[t.getDoesImplementIndex()] = t.getDoesImplementBitMask(); 195 } else { 196 int[] parent = t.getSuperClass().getDoesImplement(); 197 for (int j = 0; j < parent.length; j++) { 198 mine[j] |= parent[j]; 199 } 200 } 201 for (RVMClass superInterface : superInterfaces) { 202 int[] parent = superInterface.getDoesImplement(); 203 for (int j = 0; j < parent.length; j++) { 204 mine[j] |= parent[j]; 205 } 206 } 207 208 return mine; 209 } 210 211 /** 212 * LHSclass is a fully loaded class or interface. 213 * Is rhsTIB the TIB of an instanceof LHSclass? 214 * 215 * @param LHSclass a fully loaded class or interface class 216 * @param rhsTIB the TIB of an object that might be an instance of LHSclass 217 * @return <code>true</code> if the object is an instance of LHSClass 218 * or <code>false</code> if it is not 219 */ 220 public static boolean instanceOfNonArray(RVMClass LHSclass, TIB rhsTIB) { 221 if (LHSclass.isInterface()) { 222 return instanceOfInterface(LHSclass, rhsTIB); 223 } else { 224 return instanceOfClass(LHSclass, rhsTIB); 225 } 226 } 227 228 /** 229 * LHSclass is a fully loaded class. 230 * Is rhsTIB the TIB of a subclass of LHSclass? 231 * 232 * @param LHSclass a (fully loaded) class 233 * @param rhsTIB the TIB of an object that might be an instance of LHSclass 234 * @return <code>true</code> if the object is an instance of LHSClass 235 * or <code>false</code> if it is not 236 */ 237 @Uninterruptible 238 public static boolean instanceOfClass(RVMClass LHSclass, TIB rhsTIB) { 239 if (VM.VerifyAssertions) { 240 VM._assert(rhsTIB != null); 241 VM._assert(rhsTIB.getSuperclassIds() != null); 242 } 243 short[] superclassIds = Magic.objectAsShortArray(rhsTIB.getSuperclassIds()); 244 int LHSDepth = LHSclass.getTypeDepth(); 245 if (LHSDepth >= superclassIds.length) return false; 246 int LHSId = LHSclass.getId(); 247 return (superclassIds[LHSDepth] & 0xFFFF) == LHSId; 248 } 249 250 /** 251 * LHSclass is a fully loaded interface. 252 * Is rhsTIB the TIB of a class that implements LHSclass? 253 * 254 * @param LHSclass a class (that is a fully loaded interface) 255 * @param rhsTIB the TIB of an object that might be an instance of LHSclass 256 * @return <code>true</code> if the object is an instance of LHSClass 257 * or <code>false</code> if it is not 258 */ 259 public static boolean instanceOfInterface(RVMClass LHSclass, TIB rhsTIB) { 260 int[] doesImplement = rhsTIB.getDoesImplement(); 261 int idx = LHSclass.getDoesImplementIndex(); 262 int mask = LHSclass.getDoesImplementBitMask(); 263 return idx < doesImplement.length && ((doesImplement[idx] & mask) != 0); 264 } 265 266 /** 267 * Can we store an object of type RHSType in a variable of type LHSType? 268 * Assumption. LHSType and RHSType are already resolved. 269 * 270 * @param LHSType the left-hand-side type 271 * @param RHSType the right-hand-size type 272 * @return <code>true</code> if we can store an object of 273 * RHSType into a variable of type LSType 274 * or <code>false</code> if we cannot. 275 */ 276 public static boolean instanceOfResolved(RVMType LHSType, RVMType RHSType) { 277 int LHSDimension = LHSType.getDimensionality(); 278 int RHSDimension = RHSType.getDimensionality(); 279 if (LHSDimension < 0 || RHSDimension < 0) return false; 280 if (LHSDimension == 0) { 281 return instanceOfNonArray(LHSType.asClass(), RHSType.getTypeInformationBlock()); 282 } 283 RVMType LHSInnermostElementType = LHSType.asArray().getInnermostElementType(); 284 if (LHSInnermostElementType == RVMType.JavaLangObjectType) { 285 if (RHSDimension < LHSDimension) return false; 286 if (RHSDimension > LHSDimension) return true; 287 return RHSType.asArray().getInnermostElementType().isClassType(); // !primitive 288 } else if (!(LHSInnermostElementType.isPrimitiveType() || LHSInnermostElementType.isUnboxedType())) { 289 if (RHSDimension == LHSDimension) { 290 RVMType RHSInnermostElementType = RHSType.asArray().getInnermostElementType(); 291 if ((RHSInnermostElementType.isPrimitiveType() || RHSInnermostElementType.isUnboxedType())) return false; 292 return instanceOfNonArray(LHSInnermostElementType.asClass(), RHSInnermostElementType.getTypeInformationBlock()); 293 } else { 294 // All array types implicitly implement java.lang.Cloneable and java.io.Serializable 295 // so if LHS is if lesser dimensionality then this check must succeed if its innermost 296 // element type is one of these special interfaces. 297 return (LHSDimension < RHSDimension && 298 (LHSInnermostElementType == RVMType.JavaLangCloneableType || 299 LHSInnermostElementType == RVMType.JavaIoSerializableType)); 300 } 301 } else { 302 return false; 303 } 304 } 305}