432 changes: 397 additions & 35 deletions src/core/demangle.d
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ private struct Demangle
static bool isAlpha( char val )
{
return ('a' <= val && 'z' >= val) ||
('A' <= val && 'Z' >= val);
('A' <= val && 'Z' >= val) ||
(val >= '\x80'); // Assume all other Unicode characters are valid
}


Expand Down Expand Up @@ -339,8 +340,23 @@ private struct Demangle
return val;
}

void putHexNumber( size_t number, int minLength )
{
char buf[number.sizeof * 3];
size_t index = buf.length - 1;
while( number > 0 || minLength > 0 )
{
auto digit = number % 16;
buf[index] = cast(char)(digit < 10 ? (digit + '0') : (digit-10 + 'a'));
number /= 16;
-- index;
-- minLength;
}
put( buf[index+1 .. $] );
}


void parseReal()
void parseReal( char typeCode = '\0' )
{
debug(trace) printf( "parseReal+\n" );
debug(trace) scope(success) printf( "parseReal-\n" );
Expand All @@ -349,11 +365,39 @@ private struct Demangle
size_t tlen = 0;
real val = void;

string actualType = void, suffix = void;
switch( typeCode )
{
case 'f': actualType = "float"; suffix = "f"; break;
case 'd': actualType = "double"; suffix = ""; break;
case 'e': actualType = "real"; suffix = "L"; break;
case 'o': actualType = "ifloat"; suffix = "fi"; break;
case 'p': actualType = "idouble"; suffix = "i"; break;
case 'j': actualType = "ireal"; suffix = "Li"; break;
default: actualType = "real"; suffix = ""; break;
}

if( 'N' == tok() )
{
tbuf[tlen++] = '-';
next();
}
if( 'I' == tok() ) // INF
{
match( "INF" );
if( tlen == 1 )
{
debug(info) printf( "got (NINF)\n" );
put( "-" );
}
else
{
debug(info) printf( "got (INF)\n" );
}
put( actualType );
put( ".infinity" );
return;
}
tbuf[tlen++] = '0';
tbuf[tlen++] = 'X';
if( !isHexDigit( tok() ) )
Expand All @@ -362,12 +406,27 @@ private struct Demangle
tbuf[tlen++] = '.';
next();

if( 'N' == tok() ) // NAN
{
if( tbuf[0] == '-' && tbuf[3] == 'A' )
{
next();
debug(info) printf( "got (NAN)\n" );
put( actualType );
put( ".nan" );
return;
}
else
error( "Unexpected 'N'" );
}

while( isHexDigit( tok() ) )
{
tbuf[tlen++] = tok();
next();
}
match( 'P' );
tbuf[tlen++] = 'p';
if( 'N' == tok() )
{
tbuf[tlen++] = '-';
Expand All @@ -386,9 +445,10 @@ private struct Demangle
tbuf[tlen] = 0;
debug(info) printf( "got (%s)\n", tbuf.ptr );
val = strtold( tbuf.ptr, null );
tlen = snprintf( tbuf.ptr, tbuf.length, "%Lf", val );
tlen = snprintf( tbuf.ptr, tbuf.length, "%#Lg", val );
debug(info) printf( "converted (%.*s)\n", cast(int) tlen, tbuf.ptr );
put( tbuf[0 .. tlen] );
put( suffix );
}


Expand Down Expand Up @@ -699,6 +759,7 @@ private struct Demangle
}

// FuncAttrs
funcAttrs:
while( 'N' == tok() )
{
next();
Expand Down Expand Up @@ -729,7 +790,8 @@ private struct Demangle
put( "@safe " );
continue;
default:
error();
-- pos;
break funcAttrs;
}
}

Expand All @@ -741,7 +803,9 @@ private struct Demangle
auto t = len;
parseType();
put( " " );
if( name.length )
if( IsDelegate.yes == isdg )
put( "delegate" );
else if( name.length )
{
if( !contains( dst[0 .. len], name ) )
put( name );
Expand All @@ -751,8 +815,6 @@ private struct Demangle
t -= name.length;
}
}
else if( IsDelegate.yes == isdg )
put( "delegate" );
else
put( "function" );
shift( dst[beg .. t] );
Expand All @@ -764,13 +826,13 @@ private struct Demangle
debug(info) printf( "tok (%c)\n", tok() );
switch( tok() )
{
case 'X': // ArgClose (variadic T t,...) style)
case 'X': // ArgClose (variadic T t...) style)
next();
put( ", ..." );
put( " ..." );
return;
case 'Y': // ArgClose (variadic T t...) style)
case 'Y': // ArgClose (variadic T t,...) style)
next();
put( "..." );
put( ", ..." );
return;
case 'Z': // ArgClose (not variadic)
next();
Expand Down Expand Up @@ -839,8 +901,10 @@ private struct Demangle
{
case 'g': // Wild (Ng Type)
next();
// TODO: Anything needed here?
put( "inout(" );
parseType();
put( ")" );
pad( name );
return dst[beg .. len];
case 'e': // TypeNewArray (Ne Type)
next();
Expand Down Expand Up @@ -890,11 +954,12 @@ private struct Demangle
case 'E': // TypeEnum (E LName)
case 'T': // TypeTypedef (T LName)
next();
parseQualifiedName();
parseQualifiedName( /*mayContainInnerFunction*/true );
return dst[beg .. len];
case 'D': // TypeDelegate (D TypeFunction)
next();
parseTypeFunction( IsDelegate.yes );
pad( name );
return dst[beg .. len];
case 'n': // TypeNone (n)
next();
Expand Down Expand Up @@ -1018,6 +1083,120 @@ private struct Demangle
}
}

/**
A strip-down version of parseType() which the only purpose is to distiguish
various floating point and array types. This is used for making parseValue()
provide the correct results.
*/
char parseTypeForValue( out size_t keyTypePos, out size_t valueTypePos )
{
debug(trace) printf( "parseTypeForValue+\n" );
debug(trace) scope(success) printf( "parseTypeForValue-\n" );

size_t keyPosIgnored, valuePosIgnored;

char c = tok();
next();
switch( c )
{
// Shared, Const, Immutable: we Unqual them
case 'O', 'x', 'y':
c = parseTypeForValue( keyTypePos, valueTypePos );
break;

// TypeStaticArray: strip the number and parse the type.
case 'G':
while( isDigit( tok() ) )
next();
goto case;

// TypeArray, TypePointer: the keyTypePos is correct, just munch
// the Type after.
case 'A', 'P', 'H':
keyTypePos = pos;
parseTypeForValue( keyPosIgnored, valuePosIgnored );

// TypeAssocArray: have both key & value type.
if (c == 'H') {
valueTypePos = pos;
parseTypeForValue( keyPosIgnored, valuePosIgnored );
}
break;

// Wild and TypeNewArray: What are these?
case 'N':
switch( tok() )
{
case 'g', 'e':
// TODO: Anything needed here?
next();
break;
default:
error( "Unexpected type encoding 'N" ~ tok() ~ "'" );
}
goto case 'A';

// TypeDelegate
case 'D':
next();
goto case;

// TypeFunction: Just skip through the entire type.
case 'F', 'U', 'W', 'V', 'R':
// strip the function attributes
while( tok() == 'N' )
{
next();
auto attrib = tok();
if( !('a' <= attrib && attrib <= 'f') )
{
-- pos;
break;
}
next();
}

parseArguments:
while( true )
{
auto attrib = tok();
next();
// out, ref, lazy, scope
if ('J' <= attrib && attrib <= 'M')
continue;
// ArgClose
if ('X' <= attrib && attrib <= 'Z')
break;
parseTypeForValue( keyPosIgnored, valuePosIgnored );
}
parseTypeForValue( keyPosIgnored, valuePosIgnored );
break;

// TypeIdent, TypeClass, TypeStruct, TypeEnum, TypeTypedef
case 'I', 'C', 'S', 'E', 'T':
keyTypePos = pos;
silent( parseQualifiedName( /*mayContainInnerFunction*/true ) );
break;

// The rest of 1-letter type codes
case 'n', 'v', 'g', 'h', 's', 't', 'i', 'k', 'l', 'm', 'f', 'd',
'e', 'o', 'p', 'j', 'q', 'r', 'c', 'b', 'a', 'u', 'w':
break;

// TypeTuple
case 'B':
while( isDigit( tok() ) )
next();
goto parseArguments;

default:
-- pos;
error( "Unexpected type code '" ~ c ~ "'" );
break;
}

return c;
}

/*
Value:
Expand Down Expand Up @@ -1053,7 +1232,7 @@ private struct Demangle
E
F
*/
void parseValue()
void parseValue( char typeCode = '\0', size_t keyTypePos = 0, size_t valueTypePos = 0 )
{
debug(trace) printf( "parseValue+\n" );
debug(trace) scope(success) printf( "parseValue-\n" );
Expand All @@ -1070,23 +1249,67 @@ private struct Demangle
error( "Number expected" );
// fall-through intentional
case '0': .. case '9':
put( sliceNumber() );
bool isChar = false, isBool = false;
string suffix = "";
size_t minLen = void;
switch( typeCode )
{
case 'h', 't', 'k': suffix = "u"; break; // ubyte, ushort, uint
case 'l': suffix = "L"; break; // long
case 'm': suffix = "uL"; break; // ulong
case 'a': isChar = true; suffix = "\\x"; minLen = 2; break; // char
case 'u': isChar = true; suffix = "\\u"; minLen = 4; break; // wchar
case 'w': isChar = true; suffix = "\\U"; minLen = 8; break; // dchar
case 'b': isBool = true; break; // bool
default: break;
}

if( isBool )
put( decodeNumber() ? "true" : "false" );
else if( isChar )
{
auto charCode = decodeNumber();
put( "'" );
if( typeCode == 'a' && charCode >= ' ' && charCode != 0x7f )
{
if( charCode == '\'' || charCode == '\\' )
put( "\\" );
put( (cast(char*) &charCode)[0 .. 1] );
}
else
{
put( suffix );
putHexNumber( charCode, minLen );
}
put( "'" );
}
else
{
put( sliceNumber() );
put( suffix );
}
return;
case 'N':
next();
put( "-" );
put( sliceNumber() );
return;
goto case '0';
case 'e':
next();
parseReal();
parseReal( typeCode );
return;
case 'c':
char reTypeCode = void, imTypeCode = void;
switch( typeCode )
{
case 'q': reTypeCode = 'f'; imTypeCode = 'o'; break; // cfloat
case 'c': reTypeCode = 'e'; imTypeCode = 'j'; break; // creal
default: reTypeCode = 'd'; imTypeCode = 'p'; break; // cdouble
}
next();
parseReal();
parseReal( reTypeCode );
put( "+" );
parseReal();
put( "i" );
match( "c" );
parseReal( imTypeCode );
return;
case 'a': case 'w': case 'd':
char t = tok();
Expand All @@ -1099,16 +1322,70 @@ private struct Demangle
auto a = ascii2hex( tok() ); next();
auto b = ascii2hex( tok() ); next();
auto v = cast(char)((a << 4) | b);
put( (cast(char*) &v)[0 .. 1] );
if( v >= ' ' && v != '\x7f' )
{
if( v == '"' || v == '\\' )
put( "\\" );
put( (cast(char*) &v)[0 .. 1] );
}
else
{
put( "\\x" );
putHexNumber( v, 2 );
}
}
put( "\"" );
if( 'a' != t )
put( (cast(char*) &t)[0 .. 1] );
return;
case 'A':
case 'A', 'S':
// A Number Value...
// An array literal. Value is repeated Number times.
error(); // TODO: Not implemented.
//
// S Number Value...
// A struct literal. See bug 5956.
bool isStruct = (tok() == 'S');
bool isAssociativeArray = (typeCode == 'H');

next();
auto numberOfEntries = decodeNumber();

size_t keyKeyTypePos = 0, keyValueTypePos = void;
size_t valueKeyTypePos = void, valueValueTypePos = void;
char keyTypeCode = '\0', valueTypeCode = void;
if( keyTypePos != 0 )
{
size_t curPos = pos;
pos = keyTypePos;
if( isStruct )
parseQualifiedName( /*mayContainInnerFunction*/true );
else
{
keyTypeCode = parseTypeForValue( keyKeyTypePos, keyValueTypePos );
if( isAssociativeArray )
{
pos = valueTypePos;
valueTypeCode = parseTypeForValue( valueKeyTypePos, valueValueTypePos );
}
}
pos = curPos;
}

put( isStruct ? "(" : "[" );

foreach( i; 0 .. numberOfEntries )
{
if( i != 0 )
put( ", " );
parseValue( keyTypeCode, keyKeyTypePos, keyValueTypePos );
if( isAssociativeArray )
{
put( ":" );
parseValue( valueTypeCode, valueKeyTypePos, valueValueTypePos );
}
}
put( isStruct ? ")" : "]" );
return;
default:
error();
}
Expand Down Expand Up @@ -1142,8 +1419,9 @@ private struct Demangle
case 'V':
next();
if( n ) put( ", " );
silent( parseType() );
parseValue();
size_t keyTypePos = void, valueTypePos = void;
char typeCode = parseTypeForValue( keyTypePos, valueTypePos );
parseValue( typeCode, keyTypePos, valueTypePos );
continue;
case 'S':
next();
Expand Down Expand Up @@ -1240,18 +1518,53 @@ private struct Demangle
SymbolName
SymbolName QualifiedName
*/
char[] parseQualifiedName()
char[] parseQualifiedName( bool mayContainInnerFunction = false )
{
debug(trace) printf( "parseQualifiedName+\n" );
debug(trace) scope(success) printf( "parseQualifiedName-\n" );
size_t beg = len;
size_t n = 0;
size_t lastTypedOffset = len;

do
{
if( n++ )
put( "." );
parseSymbolName();

if( mayContainInnerFunction )
{
size_t functionPosition = 0;
size_t storedPosition = pos;
if( tok() == 'M' )
next();
auto t = tok();
if( t == 'F' || t == 'U' || t == 'W' || t == 'V' || t == 'R' )
{
debug(trace) printf( "maybe inner function\n" );
// try to skip this function and see if it is followed by a digit
try
{
functionPosition = pos;
size_t keyPosIgnored, valuePosIgnored;
parseTypeForValue( keyPosIgnored, valuePosIgnored );
}
catch( ParseException e )
{
functionPosition = 0;
pos = storedPosition;
}
}
if( !isDigit( tok() ) )
pos = storedPosition;
else if( functionPosition != 0 )
{
debug(trace) printf( "yes it is inner function\n" );
pos = functionPosition;
parseType( dst[lastTypedOffset .. len] );
lastTypedOffset = len+1;
}
}
} while( isDigit( tok() ) );
return dst[beg .. len];
}
Expand Down Expand Up @@ -1352,12 +1665,14 @@ unittest
["_D88", "_D88"],
["_D4test3fooAa", "char[] test.foo"],
["_D8demangle8demangleFAaZAa", "char[] demangle.demangle(char[])"],
["_D6object6Object8opEqualsFC6ObjectZi", "int object.Object.opEquals(class Object)"],
["_D6object6Object8opEqualsFC6ObjectZi", "int object.Object.opEquals(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))"],
+/
["_D4test34__T3barVG3uw3_616263VG3wd3_646566Z1xi", "int test.bar!(\"abc\"w, \"def\"d).x"],
["_D8demangle4testFLC6ObjectLDFLiZiZi", "int demangle.test(lazy Object, lazy int delegate(lazy int))"],
["_D8demangle4testFAiXi", "int demangle.test(int[] ...)"],
["_D8demangle4testFLAiXi", "int demangle.test(lazy int[] ...)"],
["_D6plugin8generateFiiZAya", "immutable(char)[] plugin.generate(int, int)"],
Expand All @@ -1371,15 +1686,62 @@ unittest
foreach( i, name; table )
{
auto r = demangle( name[0] );
/*
assert(r == name[1],
"table entry #" ~ to!string(i) ~ ": '" ~ name[0]
~ "' demangles as '" ~ r ~ "' but is expected to be '"
"'" ~ name[0] ~ "' demangles as '" ~ r ~ "' but is expected to be '"
~ name[1] ~ "'");
*/
}
}

unittest
{
// NOTE: These asserts depend on %g showing 6 significant figures by default.
static string[2][] checks =
[
["_D1y133__T1TVxfe8PN3VeeF38DB1F9DD3DAC05P3318Vee868A9188A89E1466PN3325VpeN932C05A4P27VeeNANVdeINFVfeNINFVeeFFFFFFFFFFFFFFFFP16380Vee8PN16385Z1TFZv",
"void y.T!(1.00000f, 1.00000e+1000L, 1.00000e-1000L, -1.23457e+09i, real.nan, double.infinity, -float.infinity, 1.18973e+4932L, 3.36210e-4932L).T()"],
["_D1y22__T1fVDFDFDFNaZiZiZinZ1fFZv",
"void y.f!(null).f()"],
["_D1y16__T1fVPPPPPPPinZ1fFZv",
"void y.f!(null).f()"],
["_D1y23__T1fVHHHiiHiiHHiiHiinZ1fFZv",
"void y.f!(null).f()"],
["_D1y17__T1fVC1x1y3z1wnZ1fFZv",
"void y.f!(null).f()"],
["_D1y57__T1TVAAiA2A2i1i2A2i3i4VAdA2eAPN1eCPN1VAAxaA2a1_37A2i8i9Z1TFZv",
"void y.T!([[1, 2], [3, 4]], [5.00000, 6.00000], [\"7\", ['\\x08', '\\x09']]).T()"],
["_D1y28__T1TVrc8PN1cAPN1VqcINFcINFZ1TFZv",
"void y.T!(4.00000+5.00000i, float.infinity+ifloat.infinity).T()"],
["_D1y22__T1TVHaiA2i49i2i51i4Z1TFZv",
"void y.T!(['1':2, '3':4]).T()"],
["_D1y23__T1fVS1y1US3i1e8PN2i3Z1fFZv",
"void y.f!(y.U(1, 2.00000, 3)).f()"],
["_D4test27__T1fVS4test1SS1A4i1i2i3i4Z1fFZv",
"void test.f!(test.S([1, 2, 3, 4])).f()"],
["_D1x1hFNgiZNgi",
"inout(int) x.h(inout(int))"],
["_D1x17__T1fTS1x1gFZv1KZ1fFZv",
"void x.f!(void x.g().K).f()"],
["_D1x35__T1fTS1x4mainFZv12__dgliteral1M1KZ1fFZv",
"void x.f!(void x.main().__dgliteral1.K).f()"],
["_D3std4conv51__T7emplaceTS1x4mainFZv1T3fffMFZv12__dgliteral1M1KZ7emplaceFPS1x4mainFZv1T3fffMFZv12__dgliteral1M1KZPS1x4mainFZv1T3fffMFZv12__dgliteral1M1K",
"void x.main().void T.fff().__dgliteral1.K* std.conv.emplace!(void x.main().void T.fff().__dgliteral1.K).emplace(void x.main().void T.fff().__dgliteral1.K*)"],
["_D3std4conv56__T7emplaceTS1x4mainFZv1T3fffMFZv3gggMFNeZv1K3hhhMUZv1LZ7emplaceFPS1x4mainFZv1T3fffMFZv3gggMFNeZv1K3hhhMUZv1LZPS1x4mainFZv1T3fffMFZv3gggMFNeZv1K3hhhMUZv1L",
"void x.main().void T.fff().@trusted void ggg().extern (C) void K.hhh().L* std.conv.emplace!(void x.main().void T.fff().@trusted void ggg().extern (C) void K.hhh().L).emplace(void x.main().void T.fff().@trusted void ggg().extern (C) void K.hhh().L*)"],
["_D1x10\&aring;\&eacute;\&icirc;\&oslash;\&uuml;FZv",
"void x.\&aring;\&eacute;\&icirc;\&oslash;\&uuml;()"],
["_D1x20__T2\&fnof;VAyaa3_e28891Z2\&fnof;FZv",
"void x.\&fnof;!(\"\&sum;\").\&fnof;()"],
["_D1x21__T1fVAyaa4_01020304Z1fFZv",
`void x.f!("\x01\x02\x03\x04").f()`],
["_D1x34__T1fVi1Vk1Vl1Vm1Vb1Va1Vu1Vw1Vya1Z1fFZv",
`void x.f!(1, 1u, 1L, 1uL, true, '\x01', '\u0001', '\U00000001', '\x01').f()`],
];

foreach( i, name; checks )
{
auto r = demangle( name[0] );
assert(r == name[1], "Unexpected: \"" ~ r ~ "\" != \"" ~ name[1] ~ "\"");
}
}

/*
*
Expand Down