This commit is contained in:
Andrei Alexandrescu 2009-04-11 20:37:06 +00:00
parent 8e67f7a22f
commit f5f14dd469

View file

@ -7,18 +7,18 @@
/**** /****
* Demangle D mangled names. * Demangle D mangled names.
* Macros: * Macros:
* WIKI = Phobos/StdDemangle * WIKI = Phobos/StdDemangle
*/ */
/* Authors: /* Authors:
* Walter Bright, Digital Mars, www.digitalmars.com * Walter Bright, Digital Mars, www.digitalmars.com
* Thomas Kuehne * Thomas Kuehne
* Frits van Bommel * Frits van Bommel
*/ */
module std.demangle; module std.demangle;
//debug=demangle; // uncomment to turn on debugging printf's //debug=demangle; // uncomment to turn on debugging printf's
private import std.ctype; private import std.ctype;
private import std.string; private import std.string;
@ -32,7 +32,7 @@ private class MangleException : Exception
{ {
this() this()
{ {
super("MangleException"); super("MangleException");
} }
} }
@ -41,8 +41,8 @@ private class MangleException : Exception
* *
* If it is not a D mangled name, it returns its argument name. * If it is not a D mangled name, it returns its argument name.
* Example: * Example:
* This program reads standard in and writes it to standard out, * This program reads standard in and writes it to standard out,
* pretty-printing any found D mangled names. * pretty-printing any found D mangled names.
------------------- -------------------
import std.stdio; import std.stdio;
import std.ctype; import std.ctype;
@ -55,28 +55,28 @@ int main()
while ((c = fgetc(stdin)) != EOF) while ((c = fgetc(stdin)) != EOF)
{ {
if (inword) if (inword)
{ {
if (c == '_' || isalnum(c)) if (c == '_' || isalnum(c))
buffer ~= cast(char) c; buffer ~= cast(char) c;
else else
{ {
inword = false; inword = false;
writef(demangle(buffer), cast(char) c); writef(demangle(buffer), cast(char) c);
} }
} }
else else
{ if (c == '_' || isalpha(c)) { if (c == '_' || isalpha(c))
{ inword = true; { inword = true;
buffer.length = 0; buffer.length = 0;
buffer ~= cast(char) c; buffer ~= cast(char) c;
} }
else else
writef(cast(char) c); writef(cast(char) c);
} }
} }
if (inword) if (inword)
writef(demangle(buffer)); writef(demangle(buffer));
return 0; return 0;
} }
------------------- -------------------
@ -89,401 +89,401 @@ string demangle(string name)
static void error() static void error()
{ {
//writefln("error()"); //writefln("error()");
throw new MangleException(); throw new MangleException();
} }
static ubyte ascii2hex(char c) static ubyte ascii2hex(char c)
{ {
if (!isxdigit(c)) if (!isxdigit(c))
error(); error();
return cast(ubyte) return cast(ubyte)
( (c >= 'a') ? c - 'a' + 10 : ( (c >= 'a') ? c - 'a' + 10 :
(c >= 'A') ? c - 'A' + 10 : (c >= 'A') ? c - 'A' + 10 :
c - '0' c - '0'
); );
} }
size_t parseNumber() size_t parseNumber()
{ {
//writefln("parseNumber() %d", ni); //writefln("parseNumber() %d", ni);
size_t result; size_t result;
while (ni < name.length && isdigit(name[ni])) while (ni < name.length && isdigit(name[ni]))
{ int i = name[ni] - '0'; { int i = name[ni] - '0';
if (result > (size_t.max - i) / 10) if (result > (size_t.max - i) / 10)
error(); error();
result = result * 10 + i; result = result * 10 + i;
ni++; ni++;
} }
return result; return result;
} }
string parseSymbolName() string parseSymbolName()
{ {
//writefln("parseSymbolName() %d", ni); //writefln("parseSymbolName() %d", ni);
size_t i = parseNumber(); size_t i = parseNumber();
if (ni + i > name.length) if (ni + i > name.length)
error(); error();
string result; string result;
if (i >= 5 && if (i >= 5 &&
name[ni] == '_' && name[ni] == '_' &&
name[ni + 1] == '_' && name[ni + 1] == '_' &&
name[ni + 2] == 'T') name[ni + 2] == 'T')
{ {
size_t nisave = ni; size_t nisave = ni;
bool err; bool err;
ni += 3; ni += 3;
try try
{ {
result = fparseTemplateInstanceName(); result = fparseTemplateInstanceName();
if (ni != nisave + i) if (ni != nisave + i)
err = true; err = true;
} }
catch (MangleException me) catch (MangleException me)
{ {
err = true; err = true;
} }
ni = nisave; ni = nisave;
if (err) if (err)
goto L1; goto L1;
goto L2; goto L2;
} }
L1: L1:
result = name[ni .. ni + i]; result = name[ni .. ni + i];
L2: L2:
ni += i; ni += i;
return result; return result;
} }
string parseQualifiedName() string parseQualifiedName()
{ {
//writefln("parseQualifiedName() %d", ni); //writefln("parseQualifiedName() %d", ni);
string result; string result;
while (ni < name.length && isdigit(name[ni])) while (ni < name.length && isdigit(name[ni]))
{ {
if (result.length) if (result.length)
result ~= "."; result ~= ".";
result ~= parseSymbolName(); result ~= parseSymbolName();
} }
return result; return result;
} }
string parseType(string identifier = null) string parseType(string identifier = null)
{ {
//writefln("parseType() %d", ni); //writefln("parseType() %d", ni);
int isdelegate = 0; int isdelegate = 0;
bool hasthisptr = false; /// For function/delegate types: expects a 'this' pointer as last argument bool hasthisptr = false; /// For function/delegate types: expects a 'this' pointer as last argument
Lagain: Lagain:
if (ni >= name.length) if (ni >= name.length)
error(); error();
string p; string p;
switch (name[ni++]) switch (name[ni++])
{ {
case 'v': p = "void"; goto L1; case 'v': p = "void"; goto L1;
case 'b': p = "bool"; goto L1; case 'b': p = "bool"; goto L1;
case 'g': p = "byte"; goto L1; case 'g': p = "byte"; goto L1;
case 'h': p = "ubyte"; goto L1; case 'h': p = "ubyte"; goto L1;
case 's': p = "short"; goto L1; case 's': p = "short"; goto L1;
case 't': p = "ushort"; goto L1; case 't': p = "ushort"; goto L1;
case 'i': p = "int"; goto L1; case 'i': p = "int"; goto L1;
case 'k': p = "uint"; goto L1; case 'k': p = "uint"; goto L1;
case 'l': p = "long"; goto L1; case 'l': p = "long"; goto L1;
case 'm': p = "ulong"; goto L1; case 'm': p = "ulong"; goto L1;
case 'f': p = "float"; goto L1; case 'f': p = "float"; goto L1;
case 'd': p = "double"; goto L1; case 'd': p = "double"; goto L1;
case 'e': p = "real"; goto L1; case 'e': p = "real"; goto L1;
case 'o': p = "ifloat"; goto L1; case 'o': p = "ifloat"; goto L1;
case 'p': p = "idouble"; goto L1; case 'p': p = "idouble"; goto L1;
case 'j': p = "ireal"; goto L1; case 'j': p = "ireal"; goto L1;
case 'q': p = "cfloat"; goto L1; case 'q': p = "cfloat"; goto L1;
case 'r': p = "cdouble"; goto L1; case 'r': p = "cdouble"; goto L1;
case 'c': p = "creal"; goto L1; case 'c': p = "creal"; goto L1;
case 'a': p = "char"; goto L1; case 'a': p = "char"; goto L1;
case 'u': p = "wchar"; goto L1; case 'u': p = "wchar"; goto L1;
case 'w': p = "dchar"; goto L1; case 'w': p = "dchar"; goto L1;
case 'A': // dynamic array case 'A': // dynamic array
p = parseType() ~ "[]"; p = parseType() ~ "[]";
goto L1; goto L1;
case 'P': // pointer case 'P': // pointer
p = parseType() ~ "*"; p = parseType() ~ "*";
goto L1; goto L1;
case 'G': // static array case 'G': // static array
{ size_t ns = ni; { size_t ns = ni;
parseNumber(); parseNumber();
size_t ne = ni; size_t ne = ni;
p = parseType() ~ "[" ~ name[ns .. ne] ~ "]"; p = parseType() ~ "[" ~ name[ns .. ne] ~ "]";
goto L1; goto L1;
} }
case 'H': // associative array case 'H': // associative array
p = parseType(); p = parseType();
p = parseType() ~ "[" ~ p ~ "]"; p = parseType() ~ "[" ~ p ~ "]";
goto L1; goto L1;
case 'D': // delegate case 'D': // delegate
isdelegate = 1; isdelegate = 1;
goto Lagain; goto Lagain;
case 'M': case 'M':
hasthisptr = true; hasthisptr = true;
goto Lagain; goto Lagain;
case 'y': case 'y':
p = "immutable(" ~ parseType() ~ ")"; p = "immutable(" ~ parseType() ~ ")";
goto L1; goto L1;
case 'x': case 'x':
p = "const(" ~ parseType() ~ ")"; p = "const(" ~ parseType() ~ ")";
goto L1; goto L1;
case 'O': case 'O':
p = "shared(" ~ parseType() ~ ")"; p = "shared(" ~ parseType() ~ ")";
goto L1; goto L1;
case 'F': // D function case 'F': // D function
case 'U': // C function case 'U': // C function
case 'W': // Windows function case 'W': // Windows function
case 'V': // Pascal function case 'V': // Pascal function
case 'R': // C++ function case 'R': // C++ function
{ char mc = name[ni - 1]; { char mc = name[ni - 1];
string args; string args;
while (1) while (1)
{ {
if (ni >= name.length) if (ni >= name.length)
error(); error();
char c = name[ni]; char c = name[ni];
if (c == 'Z') if (c == 'Z')
break; break;
if (c == 'X') if (c == 'X')
{ {
if (!args.length) error(); if (!args.length) error();
args ~= " ..."; args ~= " ...";
break; break;
} }
if (args.length) if (args.length)
args ~= ", "; args ~= ", ";
switch (c) switch (c)
{ {
case 'J': case 'J':
args ~= "out "; args ~= "out ";
ni++; ni++;
goto default; goto default;
case 'K': case 'K':
args ~= "inout "; args ~= "inout ";
ni++; ni++;
goto default; goto default;
case 'L': case 'L':
args ~= "lazy "; args ~= "lazy ";
ni++; ni++;
goto default; goto default;
default: default:
args ~= parseType(); args ~= parseType();
continue; continue;
case 'Y': case 'Y':
args ~= "..."; args ~= "...";
break; break;
} }
break; break;
} }
ni++; ni++;
if (!isdelegate && identifier.length) if (!isdelegate && identifier.length)
{ {
switch (mc) switch (mc)
{ {
case 'F': p = null; break; // D function case 'F': p = null; break; // D function
case 'U': p = "extern (C) "; break; // C function case 'U': p = "extern (C) "; break; // C function
case 'W': p = "extern (Windows) "; break; // Windows function case 'W': p = "extern (Windows) "; break; // Windows function
case 'V': p = "extern (Pascal) "; break; // Pascal function case 'V': p = "extern (Pascal) "; break; // Pascal function
default: assert(0); default: assert(0);
} }
p ~= parseType() ~ " " ~ identifier ~ "(" ~ args ~ ")"; p ~= parseType() ~ " " ~ identifier ~ "(" ~ args ~ ")";
return p; return p;
} }
p = parseType() ~ p = parseType() ~
(isdelegate ? " delegate(" : " function(") ~ (isdelegate ? " delegate(" : " function(") ~
args ~ ")"; args ~ ")";
isdelegate = 0; isdelegate = 0;
goto L1; goto L1;
} }
case 'C': p = "class "; goto L2; case 'C': p = "class "; goto L2;
case 'S': p = "struct "; goto L2; case 'S': p = "struct "; goto L2;
case 'E': p = "enum "; goto L2; case 'E': p = "enum "; goto L2;
case 'T': p = "typedef "; goto L2; case 'T': p = "typedef "; goto L2;
L2: p ~= parseQualifiedName(); L2: p ~= parseQualifiedName();
goto L1; goto L1;
L1: L1:
if (isdelegate) if (isdelegate)
error(); // 'D' must be followed by function error(); // 'D' must be followed by function
if (identifier.length) if (identifier.length)
p ~= " " ~ identifier; p ~= " " ~ identifier;
return p; return p;
default: default:
size_t i = ni - 1; size_t i = ni - 1;
ni = name.length; ni = name.length;
p = name[i .. length]; p = name[i .. length];
goto L1; goto L1;
} }
} }
string parseTemplateInstanceName() string parseTemplateInstanceName()
{ {
auto result = parseSymbolName() ~ "!("; auto result = parseSymbolName() ~ "!(";
int nargs; int nargs;
while (1) while (1)
{ size_t i; { size_t i;
if (ni >= name.length) if (ni >= name.length)
error(); error();
if (nargs && name[ni] != 'Z') if (nargs && name[ni] != 'Z')
result ~= ", "; result ~= ", ";
nargs++; nargs++;
switch (name[ni++]) switch (name[ni++])
{ {
case 'T': case 'T':
result ~= parseType(); result ~= parseType();
continue; continue;
case 'V': case 'V':
void getReal() void getReal()
{ real r; { real r;
ubyte *p = cast(ubyte *)&r; ubyte *p = cast(ubyte *)&r;
if (ni + 10 * 2 > name.length) if (ni + 10 * 2 > name.length)
error(); error();
for (i = 0; i < 10; i++) for (i = 0; i < 10; i++)
{ ubyte b; { ubyte b;
b = cast(ubyte) b = cast(ubyte)
( (
(ascii2hex(name[ni + i * 2]) << 4) + (ascii2hex(name[ni + i * 2]) << 4) +
ascii2hex(name[ni + i * 2 + 1]) ascii2hex(name[ni + i * 2 + 1])
); );
p[i] = b; p[i] = b;
} }
result ~= format(r); result ~= format(r);
ni += 10 * 2; ni += 10 * 2;
} }
result ~= parseType() ~ " "; result ~= parseType() ~ " ";
if (ni >= name.length) if (ni >= name.length)
error(); error();
switch (name[ni++]) switch (name[ni++])
{ {
case '0': case '1': case '2': case '3': case '4': case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9': case '5': case '6': case '7': case '8': case '9':
i = ni - 1; i = ni - 1;
while (ni < name.length && isdigit(name[ni])) while (ni < name.length && isdigit(name[ni]))
ni++; ni++;
result ~= name[i .. ni]; result ~= name[i .. ni];
break; break;
case 'N': case 'N':
i = ni; i = ni;
while (ni < name.length && isdigit(name[ni])) while (ni < name.length && isdigit(name[ni]))
ni++; ni++;
if (i == ni) if (i == ni)
error(); error();
result ~= "-" ~ name[i .. ni]; result ~= "-" ~ name[i .. ni];
break; break;
case 'n': case 'n':
result ~= "null"; result ~= "null";
break; break;
case 'e': case 'e':
getReal(); getReal();
break; break;
case 'c': case 'c':
getReal(); getReal();
result ~= '+'; result ~= '+';
getReal(); getReal();
result ~= 'i'; result ~= 'i';
break; break;
case 'a': case 'a':
case 'w': case 'w':
case 'd': case 'd':
{ char m = name[ni - 1]; { char m = name[ni - 1];
if (m == 'a') if (m == 'a')
m = 'c'; m = 'c';
size_t n = parseNumber(); size_t n = parseNumber();
if (ni >= name.length || name[ni++] != '_' || if (ni >= name.length || name[ni++] != '_' ||
ni + n * 2 > name.length) ni + n * 2 > name.length)
error(); error();
result ~= '"'; result ~= '"';
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
{ char c; { char c;
c = (ascii2hex(name[ni + i * 2]) << 4) + c = (ascii2hex(name[ni + i * 2]) << 4) +
ascii2hex(name[ni + i * 2 + 1]); ascii2hex(name[ni + i * 2 + 1]);
result ~= c; result ~= c;
} }
ni += n * 2; ni += n * 2;
result ~= '"'; result ~= '"';
result ~= m; result ~= m;
break; break;
} }
default: default:
error(); error();
break; break;
} }
continue; continue;
case 'S': case 'S':
result ~= parseSymbolName(); result ~= parseSymbolName();
continue; continue;
case 'Z': case 'Z':
break; break;
default: default:
error(); error();
} }
break; break;
} }
result ~= ")"; result ~= ")";
return assumeUnique(result); return assumeUnique(result);
} }
if (name.length < 3 || if (name.length < 3 ||
name[0] != '_' || name[0] != '_' ||
name[1] != 'D' || name[1] != 'D' ||
!isdigit(name[2])) !isdigit(name[2]))
{ {
goto Lnot; goto Lnot;
} }
fparseTemplateInstanceName = &parseTemplateInstanceName; fparseTemplateInstanceName = &parseTemplateInstanceName;
try try
{ {
auto result = parseQualifiedName(); auto result = parseQualifiedName();
result = parseType(result); result = parseType(result);
while(ni < name.length){ while(ni < name.length){
result ~= " . " ~ parseType(parseQualifiedName()); result ~= " . " ~ parseType(parseQualifiedName());
} }
if (ni != name.length) if (ni != name.length)
goto Lnot; goto Lnot;
return result; return result;
} }
catch (MangleException e) catch (MangleException e)
{ {
@ -501,27 +501,27 @@ unittest
static string[2][] table = static string[2][] table =
[ [
[ "printf", "printf" ], [ "printf", "printf" ],
[ "_foo", "_foo" ], [ "_foo", "_foo" ],
[ "_D88", "_D88" ], [ "_D88", "_D88" ],
[ "_D4test3fooAa", "char[] test.foo"], [ "_D4test3fooAa", "char[] test.foo"],
[ "_D8demangle8demangleFAaZAa", "char[] demangle.demangle(char[])" ], [ "_D8demangle8demangleFAaZAa", "char[] demangle.demangle(char[])" ],
[ "_D6object6Object8opEqualsFC6ObjectZi", "int object.Object.opEquals(class Object)" ], [ "_D6object6Object8opEqualsFC6ObjectZi", "int object.Object.opEquals(class Object)" ],
[ "_D4test2dgDFiYd", "double delegate(int, ...) test.dg" ], [ "_D4test2dgDFiYd", "double delegate(int, ...) test.dg" ],
[ "_D4test58__T9factorialVde67666666666666860140VG5aa5_68656c6c6fVPvnZ9factorialf", "float test.factorial!(double 4.2, char[5] \"hello\"c, void* null).factorial" ], [ "_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" ], [ "_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" ], [ "_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))"], [ "_D8demangle4testFLC6ObjectLDFLiZiZi", "int demangle.test(lazy class Object, lazy int delegate(lazy int))"],
[ "_D8demangle4testFAiXi", "int demangle.test(int[] ...)"], [ "_D8demangle4testFAiXi", "int demangle.test(int[] ...)"],
[ "_D8demangle4testFLAiXi", "int demangle.test(lazy int[] ...)"], [ "_D8demangle4testFLAiXi", "int demangle.test(lazy int[] ...)"],
[ "_D6plugin8generateFiiZAya", "immutable(char)[] plugin.generate(int, int)"], [ "_D6plugin8generateFiiZAya", "immutable(char)[] plugin.generate(int, int)"],
[ "_D6plugin8generateFiiZAxa", "const(char)[] plugin.generate(int, int)"], [ "_D6plugin8generateFiiZAxa", "const(char)[] plugin.generate(int, int)"],
[ "_D6plugin8generateFiiZAOa", "shared(char)[] plugin.generate(int, int)"] [ "_D6plugin8generateFiiZAOa", "shared(char)[] plugin.generate(int, int)"]
]; ];
foreach (i, name; table) foreach (i, name; table)
{ {
string r = demangle(name[0]); string r = demangle(name[0]);
assert(r == name[1], assert(r == name[1],
"table entry #" ~ to!string(i) ~ ": '" ~ name[0] "table entry #" ~ to!string(i) ~ ": '" ~ name[0]
~ "' demangles as '" ~ r ~ "' but is expected to be '" ~ "' demangles as '" ~ r ~ "' but is expected to be '"