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