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.compilers.opt;
014    
015    import org.jikesrvm.VM;
016    import org.jikesrvm.Constants;
017    import org.jikesrvm.classloader.Atom;
018    import org.jikesrvm.classloader.RVMClass;
019    import org.jikesrvm.classloader.RVMMethod;
020    import org.jikesrvm.classloader.MethodReference;
021    import org.jikesrvm.classloader.TypeReference;
022    import org.jikesrvm.compilers.opt.bc2ir.IRGenOptions;
023    import org.jikesrvm.compilers.opt.driver.OptConstants;
024    import org.jikesrvm.compilers.opt.ir.operand.ClassConstantOperand;
025    import org.jikesrvm.compilers.opt.ir.operand.DoubleConstantOperand;
026    import org.jikesrvm.compilers.opt.ir.operand.FloatConstantOperand;
027    import org.jikesrvm.compilers.opt.ir.operand.IntConstantOperand;
028    import org.jikesrvm.compilers.opt.ir.operand.LongConstantOperand;
029    import org.jikesrvm.compilers.opt.ir.operand.StringConstantOperand;
030    import org.jikesrvm.compilers.opt.util.Stack;
031    import org.jikesrvm.runtime.RuntimeEntrypoints;
032    import org.jikesrvm.runtime.Statics;
033    import org.vmmagic.unboxed.Offset;
034    
035    /**
036     **/
037    public final class ClassLoaderProxy implements Constants, OptConstants {
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 (IRGenOptions.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 (IRGenOptions.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 (IRGenOptions.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 (IRGenOptions.DBG_TYPE) {
155            VM.sysWrite("stack 1: " + s1);
156          }
157          if (IRGenOptions.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 (IRGenOptions.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 (IRGenOptions.DBG_TYPE && c1 == null) {
178            VM.sysWrite(c1 + " is not loaded, using Object as common supertype");
179          }
180          if (IRGenOptions.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;            // placate jikes.
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      public static RVMMethod lookupMethod(RVMClass cls, MethodReference ref) {
328        RVMMethod newmeth = null;
329        if (cls.isResolved() && !cls.isInterface()) {
330          Atom mn = ref.getName();
331          Atom md = ref.getDescriptor();
332          for (; (newmeth == null) && (cls != null); cls = cls.getSuperClass()) {
333            newmeth = cls.findDeclaredMethod(mn, md);
334          }
335        }
336        return newmeth;
337      }
338    
339      // --------------------------------------------------------------------------
340      // Constant pool access
341      // --------------------------------------------------------------------------
342    
343      /**
344       * Get the integer stored at a particular index of a class's constant
345       * pool.
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      /**
354       * Get the double stored at a particular index of a class's constant
355       * pool.
356       */
357      public static DoubleConstantOperand getDoubleFromConstantPool(RVMClass klass, int index) {
358        Offset offset = klass.getLiteralOffset(index);
359        long val_raw = Statics.getSlotContentsAsLong(offset);
360        double val = Double.longBitsToDouble(val_raw);
361        return new DoubleConstantOperand(val, offset);
362      }
363    
364      /**
365       * Get the float stored at a particular index of a class's constant
366       * pool.
367       */
368      public static FloatConstantOperand getFloatFromConstantPool(RVMClass klass, int index) {
369        Offset offset = klass.getLiteralOffset(index);
370        int val_raw = Statics.getSlotContentsAsInt(offset);
371        float val = Float.intBitsToFloat(val_raw);
372        return new FloatConstantOperand(val, offset);
373      }
374    
375      /**
376       * Get the long stored at a particular index of a class's constant
377       * pool.
378       */
379      public static LongConstantOperand getLongFromConstantPool(RVMClass klass, int index) {
380        Offset offset = klass.getLiteralOffset(index);
381        long val = Statics.getSlotContentsAsLong(offset);
382        return new LongConstantOperand(val, offset);
383      }
384    
385      /**
386       * Get the String stored at a particular index of a class's constant
387       * pool.
388       */
389      public static StringConstantOperand getStringFromConstantPool(RVMClass klass, int index) {
390        Offset offset = klass.getLiteralOffset(index);
391        try {
392          String val;
393          val = (String) Statics.getSlotContentsAsObject(offset);
394          return new StringConstantOperand(val, offset);
395        } catch (ClassCastException e) {
396          throw new Error("Corrupt JTOC at offset " + offset.toInt(), e);
397        }
398      }
399    
400      /**
401       * Get the Class stored at a particular index of a class's constant
402       * pool.
403       */
404      public static ClassConstantOperand getClassFromConstantPool(RVMClass klass, int index) {
405        Offset offset = klass.getLiteralOffset(index);
406        try {
407          Class<?> val = (Class<?>) Statics.getSlotContentsAsObject(offset);
408          return new ClassConstantOperand(val, offset);
409        } catch (ClassCastException e) {
410          throw new Error("Corrupt JTOC at offset " + offset.toInt(), e);
411        }
412      }
413    }