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 &gt;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}