/* 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;")); // } }