/* Takes the output of gprof and demangles the method signatures generated by the Titanium compiler. See usage instructions below. Written by Meling Ngo (meling@cs.berkeley.edu) and Omair Kamil (oakamil@cs.berkeley.edu). Modified by Dan Bonachea */ import java.util.*; import java.io.*; public class tcdemangle { int current = 0; int len = 0; public static void Usage() { String prefix = ""; System.err.print("tcdemangle: Titanium demangler filter"); if (System.getProperty("java.vendor").equals("Titanium Project")) { System.err.println(", version " + System.getProperty("java.vm.version")); } else { prefix = "java "; System.err.println(""); } System.err.println("Usage: " + prefix + "tcdemangle [--help|--version] [infilename [outfilename] ] "); System.err.println(" Operate on files/pipes."); System.err.println(" If infilename is omitted, input is taken from stdin."); System.err.println(" If outfilename is omitted, output is sent to stdout."); System.err.println(" " + prefix + "tcdemangle -c [text...] "); System.err.println(" Treat the rest of the command line as input and demangle to stdout."); System.exit(1); } public static void main(String[] args) throws IOException { InputStream in = null; PrintStream out = null; if (args.length >= 1 && args[0].charAt(0) == '-') { if (args[0].equals("-c")) { String buf = new String(); for (int i = 1; i < args.length-1; i++) buf += args[i] + " "; if (args.length > 1) buf += args[args.length-1]; buf += '\n'; in = new StringBufferInputStream(buf); } else Usage(); } else { try { if (args.length > 2) Usage(); if (args.length >= 1) { in = new BufferedInputStream(new FileInputStream(args[0])); } else { in = System.in; } if (args.length >= 2) { out = new PrintStream(new BufferedOutputStream(new FileOutputStream(args[1]))); } } catch (IOException e) { System.err.println(e); Usage(); } } if (out == null) out = System.out; StreamTokenizer st = new StreamTokenizer(in); st.ordinaryChar('/'); tcdemangle d = new tcdemangle(); init(st); createOpTable(); Loop: while (true) { st.nextToken(); switch(st.ttype) { case StreamTokenizer.TT_EOF: break Loop; case StreamTokenizer.TT_NUMBER: out.print(st.nval); break; case StreamTokenizer.TT_WORD: out.print(d.demangle(st.sval)); break; default: out.print((char)st.ttype); break; } } out.flush(); } private static void init(StreamTokenizer st) { st.resetSyntax(); st.ordinaryChar(' '); st.ordinaryChar('\t'); st.ordinaryChar('\n'); st.ordinaryChar('\r'); st.ordinaryChar('('); st.ordinaryChar(')'); st.ordinaryChar(','); st.eolIsSignificant(true); st.wordChars('0', '9'); st.wordChars('a', 'z'); st.wordChars('A', 'Z'); st.wordChars('_', '_'); } /* Demangle the method signature. If the string is not * of the expected format, then return the string. */ private String demangle(String s) { switch(s.charAt(0)) { case 'm': return demangleMethod(s, false); case 'o': return demangleMethod(s, true); case 'j': return demanglePrimitive(s); case 'P': return demangleType(s); case 'L': return demangleType(s); case 'T': if (s.substring(1).startsWith("I_")) return demangleArray(s); else return demangleType(s); default: return s; } } /* Handle Titanium array methods */ private String demangleArray(String s) { String type, method, clss; int length = s.length(); int dim; current = s.indexOf("_ARRAY_"); if (current < 0) return s; current += 7; if (s.startsWith("TI_GLOBAL")) type = ""; else type = "(local)"; if (current >= length) return s; int index = s.substring(current).indexOf("_"); if (index < 1) return s; index += current; method = s.substring(current, index); current = index+1; if (current >= length) return s; index = s.substring(current).indexOf("_"); if (index < 1) return s; index += current; String c = s.substring(current, index); if (c.charAt(0) == 'j') { current++; clss = getBuiltIn(c.substring(1)); if (clss == null) return s; current = index+1; } else { clss = getArgs(s.substring(0, index), false); if (clss == null) return s; current = index+1; } int len = getLenNum(s.substring(current)); if ((len == 0) || ((current+len) != length)) return s; dim = Integer.parseInt(s.substring(current)); int pos = clss.indexOf("["); if (pos < 0) return type + clss + "[" + dim + "d]." + method + "()"; return type + clss.substring(0, pos) + "[" + dim + "d]" + clss.substring(pos) + "." + method + "()"; } private String demangleMethod(String s, boolean isOp) { int length = s.length(); int mlen; int methodlen; String methodname; String args; current = 1; // Ignore all characters that occur between the first 'm' // and the first digit while (current < length) { if (Character.isDigit(s.charAt(current))) break; current++; } mlen = getLenNum(s.substring(current)); if (mlen == 0) return s; methodlen = Integer.parseInt(s.substring(current, current+mlen)); current += mlen; if ((methodlen+current) > length) return s; // overloaded operators if (isOp) { methodname = (String) optable.get(s.substring(current, current+methodlen)); if (methodname == null) return s; } else methodname = s.substring(current, current+methodlen); current += methodlen; // methods from nested classes are handled differently if (methodname.startsWith("nc_")) { methodname = getNested(methodname.substring(3), true); if (methodname == null) return s; } args = getArgs(s, false); if (args == null) return s; current += 2; if (current >= length) return s; // class to which this method belongs String res = getClass(s.substring(current)); if (res == null) return s; if (current != length) return s; return res + "." + methodname + "(" + args + ")"; } /* Handle primitive types and reference types. * Array types are special cases. */ private String getArgs(String s, boolean isStandAlone) { int length = s.length(); String args = ""; String dim = ""; while ((current < length) && (!s.substring(current).startsWith("mT"))) { if (isBuiltIn(s.charAt(current))) { args += ", " + getBuiltIn(s.charAt(current)) + dim; dim = ""; current++; continue; } else if (isJavaArray(s.substring(current))) { dim += " []"; if (isLocal(s.substring(current))) dim += " local"; current += 3; String type = ""; String arrstr = s.substring(current); if (isJavaArray(arrstr) || isTiArray(arrstr)) { continue; } if (current >= length) return null; if (s.charAt(current) == 'j') { current++; type = getBuiltIn(s.substring(current)); if (type == null) return null; args += ", " + type + dim; dim = ""; current += type.length(); continue; } } if (isTiArray(s.substring(current))) { String qual = ""; if (isLocal(s.substring(current))) qual = " local"; current += 3; int nlen = getLenNum(s.substring(current)); if (nlen == 0) return null; int tidim = Integer.parseInt(s.substring(current, current+nlen)); dim = dim + " [" + tidim + "d]" + qual; current += nlen; String arrstr = s.substring(current); if (isJavaArray(arrstr) || isTiArray(arrstr)) { continue; } String type; if (current >= length) return null; if (s.charAt(current) == 'j') { current++; type = getBuiltIn(s.substring(current)); if (type == null) return null; args += ", " + type + dim; current += type.length(); dim = ""; continue; } } if (isReference(s.substring(current))) { String qual = ""; if (isLocal(s.substring(current))) qual = " local"; if (s.substring(current).startsWith("T")) current += 1; else current += 2; String res = getClass(s.substring(current)); if (res == null) return null; String type = res; args += ", " + type + qual + dim; dim = ""; } else { // handle other markers char marker = s.charAt(current); current++; // stand alone types do not have these markers if (isStandAlone) return null; switch (marker) { case 's': // MANGLE_SHARING_INFERENCE_FORMAL_MARKER case 'p': // MANGLE_POLYSHARED_NAMESPACE_MARKER case 'n': // MANGLE_NONSHARED_NAMESPACE_MARKER case 'M': // MANGLE_LQI_FORMAL__MARKER case 'm': // MANGLE_LQI_NAMESPACE_MARKER case 'l': // MANGLE_LOCAL_NAMESPACE_MARKER //case 'p': // MANGLE_TYPE_POLYSHARED_MARKER //case 'n': // MANGLE_TYPE_NONSHARED_MARKER break; default: return null; } } } if (isStandAlone && (current >= length) && (args == "")) return null; if (args.length() > 2) return args.substring(2); return args; } private boolean isJavaArray(String s) { return (s.startsWith("PTA") || s.startsWith("LTA")); } private boolean isTiArray(String s) { return (s.startsWith("TBP") || s.startsWith("TBL")); } private boolean isReference(String s) { return (s.startsWith("PT") || s.startsWith("LT") || s.startsWith("T")); } private boolean isLocal(String s) { if (isTiArray(s)) return s.startsWith("TBL"); else if (isJavaArray(s) || isReference(s)) return s.startsWith("L"); else return false; } /* Handle class names */ private String getClass(String s) { String clss = ""; int current = 0; if ((current < s.length()) && (s.charAt(current) == 'G')) return getTemplate(s, false); while ((current < s.length()) && (Character.isDigit(s.charAt(current)))) { int nlen = getLenNum(s.substring(current)); int length = Integer.parseInt(s.substring(current, current+nlen)); current += nlen; if ((current+length) > s.length()) return null; String type = s.substring(current, current+length); // nested classes if (type.startsWith("nc_")) { type = getNested(type.substring(3), false); } clss = "." + type + clss; current += length; } this.current += current; if (clss.length() == 0) return null; clss = clss.substring(1); if (clss.startsWith("ti.domains.ti") && Character.isDigit(clss.charAt(clss.length()-1))) { clss = clss.substring(13); int i; for (i = clss.length()-1; i > 0; i--) { if (! Character.isDigit(clss.charAt(i))) break; } clss = clss.substring(0, i+1) + "<" + clss.substring(i+1) + ">"; } return clss; } private String getClass2(String s) { if ((s.length() == 0) || (!Character.isDigit(s.charAt(0)))) return null; int nlen = getLenNum(s); int length = Integer.parseInt(s.substring(0, nlen)); if ((nlen + length) > s.length()) return null; this.len += nlen + length; return s.substring(nlen, nlen + length); } /* Nested Classes */ private String getNested(String s, boolean last) { String type = ""; int current = 0; while(current < s.length()) { String nested; int length; if (s.charAt(current) == 'G') { nested = getTemplate(s, true); if (nested == null) return null; length = this.len; } else if (Character.isDigit(s.charAt(current))) { int nlen = getLenNum(s.substring(current)); length = Integer.parseInt(s.substring(current, current+nlen)); current += nlen; if ((current+length) > s.length()) return null; nested = s.substring(current, current+length); if (nested.startsWith("ac_")) // anonymous class nested = nested.substring(3); else if (nested.startsWith("lc_")) // local class nested = getLocal(nested.substring(3)); } else return null; if (last) type = "." + nested; else type += "." + nested; current += length; } if (current != s.length()) return null; return type.substring(1); } /* Local classes */ private String getLocal(String nested) { // NESTED begins with an integer and is followed the declared name int nlen = getLenNum(nested); return nested.substring(nlen) + "-" + nested.substring(0, nlen); } /* demangle template classes */ private String getTemplate (String s, boolean isNested) { String args = ""; int current = 1; int len = getLenNum(s.substring(current)); if (len == 0) return null; int arity = Integer.parseInt(s.substring(current, current+len)); current += len; if (current >= s.length()) return null; if (s.charAt(current) != 'G') return null; current++; int oldCurrent = this.current; for (int i = 0; (i < arity) && (current < s.length()); i++) { len = getLenNum(s.substring(current)); if (len == 0) return null; int childlen = Integer.parseInt(s.substring(current, current+len)); current += len; if (current + childlen >= s.length()) return null; // need to add a special case here for primitive types if (s.charAt(current) == 'v') { String res = getTemplatePrimitive(s.substring(current, current+childlen)); if (res == null) return null; args += ", " + res; current += childlen; } else { this.current = 0; String arg1 = getArgs(s.substring(current, current+childlen), true); if (arg1 == null) return null; args += ", " + arg1; current += childlen; } } if (isNested) this.current = oldCurrent; else this.current = current+oldCurrent; this.len = current; if (current >= s.length()) return null; // get template class name String clss = isNested ? getClass2(s.substring(current)) : getClass(s.substring(current)); if (clss == null) return null; if (args.length() > 2) args = args.substring(2); return "template "+ clss + " <" + args + ">"; } /* get primitive template argument */ private String getTemplatePrimitive(String s) { int current = 1; int lenNum = getLenNum(s.substring(current)); if (lenNum == 0) return null; int len = Integer.parseInt(s.substring(current, current+lenNum)); current += lenNum; if ((current+len) >= s.length()) return null; if (s.charAt(current) != 'v') return null; current++; String type = s.substring(current+len); String val = s.substring(current, current+len); // for booleans, convert "1" to "true" and "0" to "false" if (type.equals("jboolean")) return val.equals("0") ? "false" : "true"; else if (type.equals("jdouble") || type.equals("jfloat")) return parseNumDouble(val); else if (type.equals("jint") || type.equals("jbyte") || type.equals("jshort") || type.equals("jlong")) return parseNum(val, false); else if (type.equals("jchar")) return parseNum(val, true); else return null; } private String demanglePrimitive(String s) { String prim = getBuiltIn(s.substring(1)); if (prim == null) return s; else if (prim.length()+1 != s.length()) return s; else return prim; } private String demangleType(String s) { current = 0; String typ = getArgs(s, true); if (typ == null) return s; else return typ; } private String getBuiltIn(String s) { String[] builtin = {"boolean", "byte", "char", "double", "float", "int", "long", "short"}; for (int i = 0; i < builtin.length; i++) { if (s.startsWith(builtin[i])) return builtin[i]; } return null; } private String getBuiltIn(char c) { switch(c) { case 'B': return "byte"; case 'C': return "char"; case 'D': return "double"; case 'F': return "float"; case 'I': return "int"; case 'J': return "long"; case 'S': return "short"; case 'Z': return "boolean"; default: return null; } } private boolean isBuiltIn(char c) { return getBuiltIn(c) != null; } /* get the length of the largest prefix of s that is composed entirely of numbers */ private int getLenNum(String s) { int current = 0; int length = s.length(); while (current < length) { if (Character.isDigit(s.charAt(current))) current++; else break; } return current; } private static String parseNum(String s, boolean isChar) { if (s.startsWith("0x") && s.endsWith("LL")) { s = s.substring(2, s.length()-2); return "" + Long.parseLong(s, 16); // radix = 16 } else if (s.startsWith("0x")) { s = s.substring(2); if (isChar) return "'" + String.valueOf((char)Integer.parseInt(s, 16)) + "'"; // radix = 16 return "" + Integer.parseInt(s, 16); } else return null; } private static String parseNumDouble(String s) { int first = s.indexOf('_'); int last = s.lastIndexOf('_'); if (first < 0 || (first != last)) return null; // either '_' does not occur or there is more than one occurence return s.replace('_', '.'); } /* initializes table of overloaded operators */ private static void createOpTable() { optable = new Hashtable(32); optable.put("EX", "op!"); /* logicalComplement */ optable.put("TI", "op~"); /* bitwiseComplement */ optable.put("LE", "op<"); /* less */ optable.put("GT", "op>"); /* greater */ optable.put("LEEQ", "op<="); /* lessEqual */ optable.put("GTEQ", "op>="); /* greaterEqual */ optable.put("EQEQ", "op=="); /* equality */ optable.put("EXEQ", "op!="); /* inequality */ optable.put("PL", "op+"); /* addition */ optable.put("MI", "op-"); /* subtraction */ optable.put("ST", "op*"); /* multiplication */ optable.put("SL", "op/"); /* division */ optable.put("AN", "op&"); /* bitwiseAnd */ optable.put("BA", "op|"); /* bitwiseOr */ optable.put("CA", "op^"); /* bitwiseXor */ optable.put("PE", "op%"); /* remainder */ optable.put("LELE", "op<<"); /* leftShift */ optable.put("GTGT", "op>>"); /* signedRightShift */ optable.put("GTGTGT", "op>>>"); /* unsignedRightShift */ optable.put("PLEQ", "op+="); /* additionAssign */ optable.put("MIEQ", "op-="); /* subtractionAssign */ optable.put("STEQ", "op*="); /* multiplicationAssign */ optable.put("SLEQ", "op/="); /* divisionAssign */ optable.put("PEEQ", "op%="); /* remainderAssign */ optable.put("LELEEQ", "op<<="); /* leftShiftAssign */ optable.put("GTGTEQ", "op>>="); /* signedRightShiftAssign */ optable.put("GTGTGTEQ", "op>>>="); /* unsignedRightShiftAssign */ optable.put("ANEQ", "op&="); /* bitwiseAndAssign */ optable.put("CAEQ", "op^="); /* bitwiseXorAssign */ optable.put("BAEQ", "op|="); /* bitwiseOrAssign */ optable.put("OBCB", "op[]"); /* arrayAccess */ optable.put("OBCBEQ", "op[]="); /* arrayAccessAssign */ } private static Hashtable optable; }