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}