/* Copyright (c) 2006, Sriram Srinivasan
*
* You may distribute this software under the terms of the license
* specified in the file "License"
*/
package kilim.analysis;
import static kilim.Constants.D_BOOLEAN;
import static kilim.Constants.D_BYTE;
import static kilim.Constants.D_CHAR;
import static kilim.Constants.D_DOUBLE;
import static kilim.Constants.D_FLOAT;
import static kilim.Constants.D_INT;
import static kilim.Constants.D_LONG;
import static kilim.Constants.D_NULL;
import static kilim.Constants.D_OBJECT;
import static kilim.Constants.D_SHORT;
import static kilim.Constants.D_STRING;
import static kilim.Constants.D_UNDEFINED;
import java.lang.reflect.Field;
import java.util.HashMap;
import kilim.Constants;
import kilim.mirrors.ClassMirrorNotFoundException;
import kilim.mirrors.Detector;
import org.objectweb.asm.Type;
/**
* A utility class that provides static methods for interning type strings and merging type
* descriptors.
*
*/
public class TypeDesc {
static final HashMap<String, String> knownTypes = new HashMap<String, String>(30);
static {
Field[] fields = Constants.class.getFields();
try {
for (int i = 0; i < fields.length; i++) {
Field f = fields[i];
if (f.getName().startsWith("D_")) {
String val = (String) f.get(null);
knownTypes.put(val, val);
}
}
} catch (IllegalAccessException iae) {
iae.printStackTrace();
}
knownTypes.put("java/lang/Object", D_OBJECT);
knownTypes.put("java/lang/String", D_STRING);
}
static boolean isDoubleWord(String desc) {
return (desc == D_DOUBLE || desc == D_LONG);
}
public static String getInterned(String desc) {
String ret = knownTypes.get(desc);
if (ret == null) {
switch (desc.charAt(0)) {
case 'L':
case '[':
return desc;
default:
return "L" + desc + ';';
}
} else {
return ret;
}
}
public static String getReturnTypeDesc(String desc) {
return getInterned(desc.substring(desc.indexOf(")") + 1));
}
static boolean isSingleWord(String desc) {
return !isDoubleWord(desc);
}
public static String getComponentType(String t) {
if (t.charAt(0) != '[') {
throw new InternalError("Can't get component type of " + t);
}
return getInterned(t.substring(1));
}
public static String getTypeDesc(Object object) {
if (object instanceof Integer)
return D_INT;
if (object instanceof Long)
return D_LONG;
if (object instanceof Float)
return D_FLOAT;
if (object instanceof Double)
return D_DOUBLE;
if (object instanceof String)
return D_STRING;
if (object instanceof Boolean)
return D_BOOLEAN;
if (object instanceof Type)
return TypeDesc.getInterned(((Type) object).getDescriptor());
throw new InternalError("Unrecognized ldc constant: " + object);
}
private static int typelen(char[] buf, int off) {
int start = off;
switch (buf[off]) {
case 'L':
while (buf[off++] != ';') {}
return off - start;
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
case 'Z':
case 'V':
return 1;
case '[':
return typelen(buf, off + 1) + 1;
default:
throw new InternalError("Unknown descriptor type");
}
}
public static String[] getArgumentTypes(String methodDescriptor) {
char[] buf = methodDescriptor.toCharArray();
int size = getNumArgumentTypes(buf);
String[] args = new String[size];
size = 0;
int off = 1;
while (buf[off] != ')') {
int len = typelen(buf, off);
args[size] = getInterned(new String(buf, off, len));
off += len;
size += 1;
}
return args;
}
public static int getNumArgumentTypes(String desc) {
return getNumArgumentTypes(desc.toCharArray());
}
public static int getNumArgumentTypes(char[] buf) {
int off = 1;
int size = 0;
while (true) {
if (buf[off] == ')') {
break;
}
off += typelen(buf, off);
size++;
}
return size;
}
/**
* Given two type descriptors, it returns an appropriate merge: 1) If they are Array types, the
* result is a an array of the merged component types 2) If they are ref types, it returns the
* least common super type. If one of them is an interface, the result is D_OBJECT 3) All other
* types must match exactly in order to not raise an error.
*/
public static String mergeType(String a, String b) throws IncompatibleTypesException {
// given: a and b are different.
if (a == D_UNDEFINED)
return b;
if (b == D_UNDEFINED)
return a;
char ac = a.charAt(0);
char bc = b.charAt(0);
if (a == D_NULL) {
assert b == D_NULL || bc == 'L' || bc == '[' : "merging NULL type with non ref type: "
+ b;
return b;
}
if (b == D_NULL) {
assert b == D_NULL || bc == 'L' || bc == '[' : "merging NULL type with non ref type: "
+ a;
return a;
}
if (a == b || a.equals(b))
return a;
switch (ac) {
case 'N': // D_NULL
if (bc == 'L')
return b;
break;
case 'L':
if (bc == 'L') {
return commonSuperType(a, b);
} else if (bc == 'N') {
return a;
} else if (bc == '[') {
return D_OBJECT; // common supertype of Ref and ArrayRef
}
break;
case '[':
if (bc == '[') {
try {
return "["
+ mergeType(TypeDesc.getComponentType(a), TypeDesc.getComponentType(b));
} catch (IncompatibleTypesException ite) {
// The component types are incompatible, but two disparate arrays still
// inherit from Object
return D_OBJECT;
}
} else if (bc == 'L') {
return D_OBJECT; // common supertype of Ref and ArrayRef
}
break;
case 'I':
case 'Z':
case 'B':
case 'C':
case 'S':
// all int types are interchangeable
switch (bc) {
case 'I':
case 'Z':
case 'B':
case 'C':
case 'S':
return D_INT;
}
break;
}
throw new IncompatibleTypesException("" + a + "," + b);
}
static String JAVA_LANG_OBJECT = "java.lang.Object";
// public for testing purposes
public static String commonSuperType(String oa, String ob) {
try {
if (oa == D_OBJECT || ob == D_OBJECT)
return D_OBJECT;
if (oa.equals(ob))
return oa;
String lub = Detector.getDetector()
.commonSuperType(getInternalName(oa),
getInternalName(ob));
if (lub.equals("java/lang/Object"))
return D_OBJECT;
return "L" + lub + ";";
} catch (ClassMirrorNotFoundException cnfe) {
throw new InternalError(cnfe.getMessage());
}
}
public static boolean isIntType(String typeDesc) {
return (typeDesc == D_INT || typeDesc == D_CHAR || typeDesc == D_SHORT
|| typeDesc == D_BYTE || typeDesc == D_BOOLEAN);
}
public static boolean isRefType(String typeDesc) {
char c = typeDesc.charAt(0);
return typeDesc == D_NULL || c == '[' || c == 'L';
}
public static String getInternalName(String desc) {
if (desc.charAt(0) == 'L') {
return desc.substring(1, desc.length() - 1);
} else {
assert desc.charAt(0) == '[' : "Unexpected internal name " + desc;
return desc;
}
}
// public static void main(String[] args) throws Exception {
// System.out.println(mergeType("Lkilim/test/ex/ExC;", "Lkilim/test/ex/ExD;"));
// }
}