// Written in the D programming language. /* * Placed into the Public Domain. */ /**** * Demangle D mangled names. * Macros: * WIKI = Phobos/StdDemangle */ /* Authors: * Walter Bright, Digital Mars, www.digitalmars.com * Thomas Kuehne * Frits van Bommel */ module demangler; //debug=demangle; // uncomment to turn on debugging printf's private import std.ctype; private import std.string; private import std.utf; private import std.stdio; private class MangleException : Exception { this() { super("MangleException"); } } /***************************** * Demangle D mangled names. * * If it is not a D mangled name, it returns its argument name. * Example: * This program reads standard in and writes it to standard out, * pretty-printing any found D mangled names. ------------------- import std.stdio; import std.ctype; import std.demangle; int main() { char[] buffer; bool inword; int c; while ((c = fgetc(stdin)) != EOF) { if (inword) { if (c == '_' || isalnum(c)) buffer ~= cast(char) c; else { inword = false; writef(demangle(buffer), cast(char) c); } } else { if (c == '_' || isalpha(c)) { inword = true; buffer.length = 0; buffer ~= cast(char) c; } else writef(cast(char) c); } } if (inword) writef(demangle(buffer)); return 0; } ------------------- */ char[] demangle(char[] name) { size_t ni = 2; char[] delegate() fparseTemplateInstanceName; char[] delegate(char[] = null) fparseType; static void error() { //writefln("error()"); throw new MangleException(); } static ubyte ascii2hex(char c) { if (!isxdigit(c)) error(); return cast(ubyte) ( (c >= 'a') ? c - 'a' + 10 : (c >= 'A') ? c - 'A' + 10 : c - '0' ); } size_t parseNumber() { //writefln("parseNumber() %d", ni); size_t result; while (ni < name.length && isdigit(name[ni])) { int i = name[ni] - '0'; if (result > (size_t.max - i) / 10) error(); result = result * 10 + i; ni++; } return result; } char[] parseSymbolName() { //writefln("parseSymbolName() %d", ni); size_t i = parseNumber(); if (ni + i > name.length) error(); char[] result; if (i >= 5 && name[ni] == '_' && name[ni + 1] == '_' && name[ni + 2] == 'T') { size_t nisave = ni; bool err; ni += 3; try { result = fparseTemplateInstanceName(); if (ni != nisave + i) err = true; } catch (MangleException me) { err = true; } ni = nisave; if (err) goto L1; goto L2; } L1: result = name[ni .. ni + i]; L2: ni += i; return result; } char[] parseQualifiedName(bool isRootName = false) { //writefln("parseQualifiedName() %d", ni); char[] result; while (ni < name.length && isdigit(name[ni])) { if (result.length) { result ~= "."; result ~= parseSymbolName(); } else {//if (result.length == 0) { result = parseSymbolName(); if (result.length > 9 && result[0 .. 9] == "TypeInfo_" && !(result[9 .. $] in typeinfo_exceptions)) { auto old_ni = ni; auto old_name = name; scope(exit) { ni = old_ni; name = old_name; } name = result[9 .. $]; ni = 0; try { result = "typeid(" ~ fparseType() ~ ")"; } catch (MangleException) { // don't fix result } } } } return result; } char[] parseType(char[] identifier = null) { //writefln("parseType() %d", ni); int isdelegate = 0; bool hasthisptr = false; /// For function/delegate types: expects a 'this' pointer as last argument Lagain: if (ni >= name.length) error(); char[] p; switch (name[ni++]) { case 'v': p = "void"; goto L1; case 'b': p = "bool"; goto L1; case 'g': p = "byte"; goto L1; case 'h': p = "ubyte"; goto L1; case 's': p = "short"; goto L1; case 't': p = "ushort"; goto L1; case 'i': p = "int"; goto L1; case 'k': p = "uint"; goto L1; case 'l': p = "long"; goto L1; case 'm': p = "ulong"; goto L1; case 'f': p = "float"; goto L1; case 'd': p = "double"; goto L1; case 'e': p = "real"; goto L1; case 'o': p = "ifloat"; goto L1; case 'p': p = "idouble"; goto L1; case 'j': p = "ireal"; goto L1; case 'q': p = "cfloat"; goto L1; case 'r': p = "cdouble"; goto L1; case 'c': p = "creal"; goto L1; case 'a': p = "char"; goto L1; case 'u': p = "wchar"; goto L1; case 'w': p = "dchar"; goto L1; case 'A': // dynamic array p = parseType() ~ "[]"; goto L1; case 'B': { // type tuple p = "("; auto old_ni = ni; auto old_name = name; scope(exit) { ni = old_ni; name = old_name; } char[] newname = parseSymbolName(); name = newname; ni = 0; while (ni < name.length) { p ~= parseType(); p ~= ", "; } if (p.length > 1) { p.length = p.length - 2; // remove last ", " } p ~= ")"; } goto L1; case 'Z': // terminator on __init, __ModuleInfo, etc. return identifier; case 'P': // pointer p = parseType() ~ "*"; goto L1; case 'G': // static array { size_t ns = ni; parseNumber(); size_t ne = ni; p = parseType() ~ "[" ~ name[ns .. ne] ~ "]"; goto L1; } case 'H': // associative array p = parseType(); p = parseType() ~ "[" ~ p ~ "]"; goto L1; case 'D': // delegate isdelegate = 1; goto Lagain; case 'M': hasthisptr = true; goto Lagain; case 'F': // D function case 'U': // C function case 'W': // Windows function case 'V': // Pascal function case 'R': // C++ function { char mc = name[ni - 1]; char[] args; while (1) { if (ni >= name.length) error(); char c = name[ni]; if (c == 'Z') break; if (c == 'X') { if (!args.length) error(); args ~= " ..."; break; } if (args.length) args ~= ", "; switch (c) { case 'J': args ~= "out "; ni++; goto default; case 'K': args ~= "inout "; ni++; goto default; case 'L': args ~= "lazy "; ni++; goto default; default: args ~= parseType(); continue; case 'Y': args ~= "..."; break; } break; } if (hasthisptr || isdelegate) { // add implicit 'this'/context pointer if (args.length) args ~= ", "; args ~= "this"; } ni++; if (!isdelegate && identifier.length) { switch (mc) { case 'F': p = null; break; // D function case 'U': p = "extern (C) "; break; // C function case 'W': p = "extern (Windows) "; break; // Windows function case 'V': p = "extern (Pascal) "; break; // Pascal function default: assert(0); } p ~= parseType() ~ " " ~ identifier ~ "(" ~ args ~ ")"; return p; } p = parseType() ~ (isdelegate ? " delegate(" : " function(") ~ args ~ ")"; isdelegate = 0; goto L1; } case 'C': p = "class "; goto L2; case 'S': p = "struct "; goto L2; case 'E': p = "enum "; goto L2; case 'T': p = "typedef "; goto L2; L2: p ~= parseQualifiedName(); goto L1; L1: if (isdelegate) error(); // 'D' must be followed by function if (identifier.length) p ~= " " ~ identifier; return p; default: size_t i = ni - 1; ni = name.length; p = name[i .. length]; goto L1; } assert(0); } char[] parseTemplateInstanceName() { char[] result = parseSymbolName() ~ "!("; int nargs; while (1) { size_t i; if (ni >= name.length) error(); if (nargs && name[ni] != 'Z') result ~= ", "; nargs++; switch (name[ni++]) { case 'T': result ~= parseType(); continue; case 'V': void getReal() { real r; ubyte *p = cast(ubyte *)&r; if (ni + 10 * 2 > name.length) error(); for (i = 0; i < 10; i++) { ubyte b; b = cast(ubyte) ( (ascii2hex(name[ni + i * 2]) << 4) + ascii2hex(name[ni + i * 2 + 1]) ); p[i] = b; } result ~= format(r); ni += 10 * 2; } result ~= parseType() ~ " "; if (ni >= name.length) error(); switch (name[ni++]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': i = ni - 1; while (ni < name.length && isdigit(name[ni])) ni++; result ~= name[i .. ni]; break; case 'N': i = ni; while (ni < name.length && isdigit(name[ni])) ni++; if (i == ni) error(); result ~= "-" ~ name[i .. ni]; break; case 'n': result ~= "null"; break; case 'e': getReal(); break; case 'c': getReal(); result ~= '+'; getReal(); result ~= 'i'; break; case 'a': case 'w': case 'd': { char m = name[ni - 1]; if (m == 'a') m = 'c'; size_t n = parseNumber(); if (ni >= name.length || name[ni++] != '_' || ni + n * 2 > name.length) error(); result ~= '"'; for (i = 0; i < n; i++) { char c; c = (ascii2hex(name[ni + i * 2]) << 4) + ascii2hex(name[ni + i * 2 + 1]); result ~= c; } ni += n * 2; result ~= '"'; result ~= m; break; } default: error(); break; } continue; case 'S': result ~= parseSymbolName(); continue; case 'Z': break; default: error(); } break; } result ~= ")"; return result; } if (name.length < 3 || name[0] != '_' || name[1] != 'D' || !isdigit(name[2])) { goto Lnot; } fparseTemplateInstanceName = &parseTemplateInstanceName; fparseType = &parseType; try { char[] result = parseQualifiedName(true); result = parseType(result); while(ni < name.length){ result ~= " . " ~ parseType(parseQualifiedName()); } if (ni != name.length) goto Lnot; return result; } catch (MangleException e) { } Lnot: // Not a recognized D mangled name; so return original return name; } bool[char[]] typeinfo_exceptions; static this() { typeinfo_exceptions["Typedef"] = true; typeinfo_exceptions["Enum"] = true; typeinfo_exceptions["Pointer"] = true; typeinfo_exceptions["Array"] = true; typeinfo_exceptions["StaticArray"] = true; typeinfo_exceptions["AssociativeArray"] = true; typeinfo_exceptions["Function"] = true; typeinfo_exceptions["Delegate"] = true; typeinfo_exceptions["Class"] = true; typeinfo_exceptions["Interface"] = true; typeinfo_exceptions["Struct"] = true; typeinfo_exceptions["Tuple"] = true; } unittest { debug(demangle) printf("demangle.demangle.unittest\n"); static char[][2][] table = [ [ "printf", "printf" ], [ "_foo", "_foo" ], [ "_D88", "_D88" ], [ "_D4test3fooAa", "char[] test.foo"], [ "_D8demangle8demangleFAaZAa", "char[] demangle.demangle(char[])" ], [ "_D6object6Object8opEqualsFC6ObjectZi", "int object.Object.opEquals(class Object)" ], [ "_D4test2dgDFiYd", "double delegate(int, ...) test.dg" ], [ "_D4test58__T9factorialVde67666666666666860140VG5aa5_68656c6c6fVPvnZ9factorialf", "float test.factorial!(double 4.2, char[5] \"hello\"c, void* null).factorial" ], [ "_D4test101__T9factorialVde67666666666666860140Vrc9a999999999999d9014000000000000000c00040VG5aa5_68656c6c6fVPvnZ9factorialf", "float test.factorial!(double 4.2, cdouble 6.8+3i, char[5] \"hello\"c, void* null).factorial" ], [ "_D4test34__T3barVG3uw3_616263VG3wd3_646566Z1xi", "int test.bar!(wchar[3] \"abc\"w, dchar[3] \"def\"d).x" ], [ "_D8demangle4testFLC6ObjectLDFLiZiZi", "int demangle.test(lazy class Object, lazy int delegate(lazy int))"], [ "_D8demangle4testFAiXi", "int demangle.test(int[] ...)"], [ "_D8demangle4testFLAiXi", "int demangle.test(lazy int[] ...)"] ]; foreach (char[][2] name; table) { char[] r = demangle(name[0]); //writefln("[ \"%s\", \"%s\" ],", name[0], r); assert(r == name[1]); } }