/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Common Public License (CPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/cpl1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.compilers.opt;
import org.jikesrvm.VM;
import org.jikesrvm.VM_Constants;
import org.jikesrvm.classloader.VM_Atom;
import org.jikesrvm.classloader.VM_Class;
import org.jikesrvm.classloader.VM_Method;
import org.jikesrvm.classloader.VM_MethodReference;
import org.jikesrvm.classloader.VM_TypeReference;
import org.jikesrvm.compilers.opt.ir.OPT_ClassConstantOperand;
import org.jikesrvm.compilers.opt.ir.OPT_DoubleConstantOperand;
import org.jikesrvm.compilers.opt.ir.OPT_FloatConstantOperand;
import org.jikesrvm.compilers.opt.ir.OPT_IRGenOptions;
import org.jikesrvm.compilers.opt.ir.OPT_IntConstantOperand;
import org.jikesrvm.compilers.opt.ir.OPT_LongConstantOperand;
import org.jikesrvm.compilers.opt.ir.OPT_StringConstantOperand;
import org.jikesrvm.runtime.VM_Runtime;
import org.jikesrvm.runtime.VM_Statics;
import org.vmmagic.unboxed.Offset;
// TODO - Deal with Subarch
/**
**/
public final class OPT_ClassLoaderProxy implements VM_Constants, OPT_Constants {
/**
* Returns a common superclass of the two types.
* NOTE: If both types are references, but are not both loaded, then this
* may be a conservative approximation (java.lang.Object).
* If there is no common superclass, than null is returned.
*/
public static VM_TypeReference findCommonSuperclass(VM_TypeReference t1, VM_TypeReference t2) {
if (t1 == t2) {
return t1;
}
if (t1.isPrimitiveType() || t2.isPrimitiveType()) {
if (t1.isIntLikeType() && t2.isIntLikeType()) {
// 2 non-identical int like types, return the largest
if (t1.isIntType() || t2.isIntType()) {
return VM_TypeReference.Int;
} else if (t1.isCharType() || t2.isCharType()) {
return VM_TypeReference.Char;
} else if (t1.isShortType() || t2.isShortType()) {
return VM_TypeReference.Short;
} else if (t1.isByteType() || t2.isByteType()) {
return VM_TypeReference.Byte;
} else {
// Unreachable
if (VM.VerifyAssertions) VM._assert(false);
return null;
}
} else if (t1.isWordType() && t2.isWordType()) {
return VM_TypeReference.Word;
} else {
// other primitive and unboxed types have no commonality so return null
return null;
}
}
//Neither t1 nor t2 are primitive or unboxed types at this point
// Is either t1 or t2 null? Null is assignable to all types so the type of
// the other operand is the most precise
if (t1 == VM_TypeReference.NULL_TYPE) {
return t2;
} else if (t2 == VM_TypeReference.NULL_TYPE) {
return t1;
}
if (OPT_IRGenOptions.DBG_TYPE) {
VM.sysWrite("finding common supertype of " + t1 + " and " + t2);
}
// Strip off all array junk.
int arrayDimensions = 0;
while (t1.isArrayType() && t2.isArrayType()) {
++arrayDimensions;
t1 = t1.getArrayElementType();
t2 = t2.getArrayElementType();
}
// at this point, they are not both array types.
// if one is a primitive, then we want an object array of one less
// dimensionality
if (t1.isPrimitiveType() || t2.isPrimitiveType()) {
VM_TypeReference type = VM_TypeReference.JavaLangObject;
if (t1 == t2) {
//Unboxed types are wrapped in their own array objects
if (t1.isUnboxedType()) {
arrayDimensions++;
type = t1;
} else {
if (VM.VerifyAssertions) VM._assert(false);
}
}
--arrayDimensions;
while (arrayDimensions-- > 0) {
type = type.getArrayTypeForElementType();
}
if (OPT_IRGenOptions.DBG_TYPE) {
VM.sysWrite("one is a primitive array, so supertype is " + type);
}
return type;
}
// At this point neither t1 or t2 is a primitive or word type and either
// one or the other maybe an array type
// is this a case of arrays with different dimensionalities?
if (t1.isArrayType() || t2.isArrayType()) {
// one is a class type, while the other is an array
VM_TypeReference type = VM_TypeReference.JavaLangObject;
while (arrayDimensions-- > 0) {
type = type.getArrayTypeForElementType();
}
if (OPT_IRGenOptions.DBG_TYPE) {
VM.sysWrite("differing dimensionalities for arrays, so supertype is " + type);
}
return type;
}
// At this point they both must be class types.
// technique: push heritage of each type on a separate stack,
// then find the highest point in the stack where they differ.
VM_Class c1 = (VM_Class) t1.peekType();
VM_Class c2 = (VM_Class) t2.peekType();
if (c1 != null && c2 != null) {
// The ancestor hierarchy is available, so do this exactly
OPT_Stack<VM_Class> s1 = new OPT_Stack<VM_Class>();
do {
s1.push(c1);
c1 = c1.getSuperClass();
} while (c1 != null);
OPT_Stack<VM_Class> s2 = new OPT_Stack<VM_Class>();
do {
s2.push(c2);
c2 = c2.getSuperClass();
} while (c2 != null);
if (OPT_IRGenOptions.DBG_TYPE) {
VM.sysWrite("stack 1: " + s1);
}
if (OPT_IRGenOptions.DBG_TYPE) {
VM.sysWrite("stack 2: " + s2);
}
VM_TypeReference best = VM_TypeReference.JavaLangObject;
while (!s1.empty() && !s2.empty()) {
VM_Class temp = s1.pop();
if (temp == s2.pop()) {
best = temp.getTypeRef();
} else {
break;
}
}
if (OPT_IRGenOptions.DBG_TYPE) {
VM.sysWrite("common supertype of the two classes is " + best);
}
while (arrayDimensions-- > 0) {
best = best.getArrayTypeForElementType();
}
return best;
} else {
if (OPT_IRGenOptions.DBG_TYPE && c1 == null) {
VM.sysWrite(c1 + " is not loaded, using Object as common supertype");
}
if (OPT_IRGenOptions.DBG_TYPE && c2 == null) {
VM.sysWrite(c2 + " is not loaded, using Object as common supertype");
}
VM_TypeReference common = VM_TypeReference.JavaLangObject;
while (arrayDimensions-- > 0) {
common = common.getArrayTypeForElementType();
}
return common;
}
}
/**
* Return OPT_Constants.YES if the parent type is defintely a supertype
* of the child type.
* <p> Return OPT_Constants.NO if the parent type is definitely not
* a supertype of the child type.
* <p> Return OPT_Constants.MAYBE if the question cannot be currently answered
* (for example if one/both of the classes is not resolved)
*
* <p> Takes into account the special 'null-type', which corresponds to a null
* constant.
*
* @param parentType parent type
* @param childType child type
* @return OPT_Constants.YES, OPT_Constants.NO, or OPT_Constants.MAYBE
*/
public static byte includesType(VM_TypeReference parentType, VM_TypeReference childType) {
// First handle some cases that we can answer without needing to
// look at the type hierarchy
// NOTE: The ordering of these tests is critical!
if (childType == VM_TypeReference.NULL_TYPE) {
// Sanity assertion that a null isn't being assigned to an unboxed type
if (VM.VerifyAssertions && parentType.isReferenceType()) VM._assert(!parentType.isWordType());
return parentType.isReferenceType() ? YES : NO;
} else if (parentType == VM_TypeReference.NULL_TYPE) {
return NO;
} else if (parentType == childType) {
return YES;
} else if (parentType == VM_TypeReference.Word && childType.isWordType()) {
return YES;
} else if (parentType.isPrimitiveType() || childType.isPrimitiveType()) {
return NO;
} else if (parentType == VM_TypeReference.JavaLangObject) {
return YES;
} else {
// Unboxed types are handled in the word and primitive type case
if (VM.VerifyAssertions) {
VM._assert(!parentType.isWordType() && !childType.isWordType());
}
// Oh well, we're going to have to try to actually look
// at the type hierarchy.
// IMPORTANT: We aren't allowed to cause dynamic class loading,
// so we have to roll some of this ourselves
// instead of simply calling VM_Runtime.instanceOf
// (which is allowed/required to load classes to answer the question).
try {
if (parentType.isArrayType()) {
if (childType == VM_TypeReference.JavaLangObject) {
return MAYBE; // arrays are subtypes of Object.
} else if (!childType.isArrayType()) {
return NO;
} else {
VM_TypeReference parentET = parentType.getInnermostElementType();
if (parentET == VM_TypeReference.JavaLangObject) {
int LHSDimension = parentType.getDimensionality();
int RHSDimension = childType.getDimensionality();
if ((RHSDimension > LHSDimension) ||
(RHSDimension == LHSDimension && childType.getInnermostElementType().isClassType())) {
return YES;
} else {
return NO;
}
} else {
// parentType is [^k of something other than Object
// If dimensionalities are equal, then we can reduce
// to isAssignableWith(parentET, childET).
// If the dimensionalities are not equal then the answer is NO
if (parentType.getDimensionality() == childType.getDimensionality()) {
return includesType(parentET, childType.getInnermostElementType());
} else {
return NO;
}
}
}
} else { // parentType.isClassType()
if (!childType.isClassType()) {
// parentType is known to not be java.lang.Object.
return NO;
} else {
VM_Class childClass = (VM_Class) childType.peekType();
VM_Class parentClass = (VM_Class) parentType.peekType();
if (childClass != null && parentClass != null) {
if (parentClass.isResolved(false) && childClass.isResolved(false) ||
(VM.writingBootImage && parentClass.isInBootImage() && childClass.isInBootImage())) {
if (parentClass.isInterface()) {
if (VM_Runtime.isAssignableWith(parentClass, childClass)) {
return YES;
} else {
// If child is not a final class, it is
// possible that a subclass will implement parent.
return childClass.isFinal() ? NO : MAYBE;
}
} else if (childClass.isInterface()) {
// parent is a proper class, child is an interface
return MAYBE;
} else {
// parent & child are both proper classes.
if (VM_Runtime.isAssignableWith(parentClass, childClass)) {
return YES;
}
// If child is a final class, then
// !instanceOfClass(parent, child) lets us return NO.
// However, if child is not final, then it might have
// subclasses so we can't return NO out of hand.
// But, if the reverse instanceOf is also false, then we know
// that parent and child are completely
// unrelated and we can return NO.
if (childClass.isFinal()) {
return NO;
} else {
if (VM_Runtime.isAssignableWith(childClass, parentClass)) {
return MAYBE;
} else {
return NO;
}
}
}
}
}
return MAYBE;
}
}
} catch (Throwable e) {
e.printStackTrace();
OPT_OptimizingCompilerException.UNREACHABLE();
return MAYBE; // placate jikes.
}
}
}
// --------------------------------------------------------------------------
// Querry classloader data structures
// --------------------------------------------------------------------------
/**
* Find the method of the given class that matches the given descriptor.
*/
public static VM_Method lookupMethod(VM_Class cls, VM_MethodReference ref) {
VM_Method newmeth = null;
if (cls.isResolved(false) && !cls.isInterface()) {
VM_Atom mn = ref.getName();
VM_Atom md = ref.getDescriptor();
for (; (newmeth == null) && (cls != null); cls = cls.getSuperClass()) {
newmeth = cls.findDeclaredMethod(mn, md);
}
}
return newmeth;
}
// --------------------------------------------------------------------------
// Constant pool access
// --------------------------------------------------------------------------
/**
* Get the integer stored at a particular index of a class's constant
* pool.
*/
public static OPT_IntConstantOperand getIntFromConstantPool(VM_Class klass, int index) {
Offset offset = klass.getLiteralOffset(index);
int val = VM_Statics.getSlotContentsAsInt(offset);
return new OPT_IntConstantOperand(val);
}
/**
* Get the double stored at a particular index of a class's constant
* pool.
*/
public static OPT_DoubleConstantOperand getDoubleFromConstantPool(VM_Class klass, int index) {
Offset offset = klass.getLiteralOffset(index);
long val_raw = VM_Statics.getSlotContentsAsLong(offset);
double val = Double.longBitsToDouble(val_raw);
return new OPT_DoubleConstantOperand(val, offset);
}
/**
* Get the float stored at a particular index of a class's constant
* pool.
*/
public static OPT_FloatConstantOperand getFloatFromConstantPool(VM_Class klass, int index) {
Offset offset = klass.getLiteralOffset(index);
int val_raw = VM_Statics.getSlotContentsAsInt(offset);
float val = Float.intBitsToFloat(val_raw);
return new OPT_FloatConstantOperand(val, offset);
}
/**
* Get the long stored at a particular index of a class's constant
* pool.
*/
public static OPT_LongConstantOperand getLongFromConstantPool(VM_Class klass, int index) {
Offset offset = klass.getLiteralOffset(index);
long val = VM_Statics.getSlotContentsAsLong(offset);
return new OPT_LongConstantOperand(val, offset);
}
/**
* Get the String stored at a particular index of a class's constant
* pool.
*/
public static OPT_StringConstantOperand getStringFromConstantPool(VM_Class klass, int index) {
Offset offset = klass.getLiteralOffset(index);
try {
String val;
val = (String) VM_Statics.getSlotContentsAsObject(offset);
return new OPT_StringConstantOperand(val, offset);
} catch (ClassCastException e) {
throw new Error("Corrupt JTOC at offset " + offset.toInt(), e);
}
}
/**
* Get the Class stored at a particular index of a class's constant
* pool.
*/
public static OPT_ClassConstantOperand getClassFromConstantPool(VM_Class klass, int index) {
Offset offset = klass.getLiteralOffset(index);
try {
Class<?> val = klass.getClassForType();
return new OPT_ClassConstantOperand(val, offset);
} catch (ClassCastException e) {
throw new Error("Corrupt JTOC at offset " + offset.toInt(), e);
}
}
}