diff --git a/src/core/demangle.d b/src/core/demangle.d index 6b653a39b7..8877e3de15 100644 --- a/src/core/demangle.d +++ b/src/core/demangle.d @@ -24,7 +24,14 @@ else version (WatchOS) debug(trace) import core.stdc.stdio : printf; debug(info) import core.stdc.stdio : printf; -private struct Demangle +private struct NoHooks +{ + // supported hooks + // static bool parseLName(ref Demangle); + // static char[] parseType(ref Demangle, char[]) +} + +private struct Demangle(Hooks = NoHooks) { // NOTE: This implementation currently only works with mangled function // names as they exist in an object file. Type names mangled via @@ -71,6 +78,7 @@ pure: size_t brp = 0; // current back reference pos AddType addType = AddType.yes; bool mute = false; + Hooks hooks; static class ParseException : Exception { @@ -538,6 +546,10 @@ pure: debug(trace) printf( "parseLName+\n" ); debug(trace) scope(success) printf( "parseLName-\n" ); + static if(__traits(hasMember, Hooks, "parseLName")) + if (hooks.parseLName(this)) + return; + if( front == 'Q' ) { // back reference to LName @@ -771,6 +783,10 @@ pure: "dchar", // w ]; + static if (__traits(hasMember, Hooks, "parseType")) + if (auto n = hooks.parseType(this, name)) + return n; + debug(trace) printf( "parseType+\n" ); debug(trace) scope(success) printf( "parseType-\n" ); auto beg = len; @@ -1962,7 +1978,7 @@ pure: char[] demangle( const(char)[] buf, char[] dst = null ) { //return Demangle(buf, dst)(); - auto d = Demangle(buf, dst); + auto d = Demangle!()(buf, dst); return d.demangleName(); } @@ -1980,10 +1996,168 @@ char[] demangle( const(char)[] buf, char[] dst = null ) */ char[] demangleType( const(char)[] buf, char[] dst = null ) { - auto d = Demangle(buf, dst); + auto d = Demangle!()(buf, dst); return d.demangleType(); } +/** +* reencode a mangled symbol name that might include duplicate occurrences +* of the same identifier by replacing all but the first occurence with +* a back reference. +* +* Params: +* mangled = The mangled string representing the type +* +* Returns: +* The mangled name with deduplicated identifiers +*/ +char[] reencodeMangled(const(char)[] mangled) nothrow pure @safe +{ + static struct PrependHooks + { + size_t lastpos; + char[] result; + size_t[const(char)[]] idpos; // identifier positions + + static struct Replacement + { + size_t pos; // postion in original mangled string + size_t respos; // postion in result string + } + Replacement [] replacements; + + pure: + size_t positionInResult(size_t pos) + { + foreach_reverse (r; replacements) + if (pos >= r.pos) + return r.respos + pos - r.pos; + return pos; + } + + alias Remangle = Demangle!(PrependHooks); + + bool parseLName(ref Remangle d) + { + if (lastpos < d.pos) + result ~= d.buf[lastpos .. d.pos]; + else if (lastpos > d.pos) + { + // todo: roll back to earlier position + } + + auto reslen = result.length; + auto refpos = d.pos; + if (d.front == 'Q') + { + size_t npos; + { + scope(exit) result.length = reslen; // remove all intermediate additions + // only support identifier back references + d.popFront(); + size_t n = d.decodeNumber!'A'(); + if (!n || n > refpos) + d.error("invalid back reference"); + + auto savepos = d.pos; + scope(exit) d.pos = savepos; + size_t srcpos = refpos - n; + + auto idlen = d.decodeNumber(); + if (d.pos + idlen > d.buf.length) + d.error("invalid back reference"); + auto id = d.buf[d.pos .. d.pos + idlen]; + auto pid = id in idpos; + if (!pid) + d.error("invalid back reference"); + npos = positionInResult(*pid); + } + encodeBackref(reslen - npos, 'A'); + replacements ~= Replacement(d.pos, result.length); + } + else + { + auto n = d.decodeNumber(); + if(!n || n > d.buf.length || n > d.buf.length - d.pos) + d.error("LName too shot or too long"); + auto id = d.buf[d.pos .. d.pos + n]; + d.pos += n; + if (auto pid = id in idpos) + { + size_t npos = positionInResult(*pid); + result.length = reslen; + encodeBackref(reslen - npos, 'A'); + replacements ~= Replacement(d.pos, result.length); + } + else + { + idpos[id] = refpos; + result ~= d.buf[refpos .. d.pos]; + } + } + lastpos = d.pos; + return true; + } + + char[] parseType( ref Remangle d, char[] name = null ) + { + if (d.front != 'Q') + return null; + + if (lastpos < d.pos) + result ~= d.buf[lastpos .. d.pos]; + else if (lastpos > d.pos) + { + // todo: roll back to earlier position + } + + auto refPos = d.pos; + d.popFront(); + auto n = d.decodeNumber!'a'(); + if (n == 0 || n > refPos) + d.error("invalid back reference"); + + size_t npos = positionInResult(refPos - n); + size_t reslen = result.length; + encodeBackref(reslen - npos, 'a'); + + lastpos = d.pos; + return result[reslen .. $]; // anything but null + } + + void encodeBackref(size_t relpos, char lastDigitBase) + { + result ~= 'Q'; + size_t div = 1; + while (relpos >= div * 10) + div *= 10; + while (div >= 10) + { + auto dig = (relpos / div); + result ~= cast(char)('0' + dig); + relpos -= dig * div; + div /= 10; + } + result ~= cast(char)(lastDigitBase + relpos); + } + } + + auto d = Demangle!(PrependHooks)(mangled, null); + d.hooks = PrependHooks(); + d.mute = true; // no demangled output + try + { + () @trusted { d.parseMangledName(); }(); + if (d.hooks.lastpos < d.pos) + d.hooks.result ~= d.buf[d.hooks.lastpos .. d.pos]; + return d.hooks.result; + } + catch(Exception) + { + // overflow exception cannot occur + return mangled.dup; + } +} /** * Mangles a D symbol. @@ -2045,7 +2219,11 @@ char[] mangle(T)(const(char)[] fqn, char[] dst = null) @safe pure nothrow } dst[i .. i + T.mangleof.length] = T.mangleof[]; i += T.mangleof.length; - return dst[0 .. i]; + + static if(hasTypeBackRef) + return reencodeMangled(dst[0 .. i]); + else + return dst[0 .. i]; } @@ -2105,12 +2283,19 @@ char[] mangleFunc(T:FT*, FT)(const(char)[] fqn, char[] dst = null) @safe pure no } } +private enum hasTypeBackRef = (int function(void**,void**)).mangleof[$-4 .. $] == "QdZi"; /// unittest { assert(mangleFunc!(int function(int))("a.b") == "_D1a1bFiZi"); - assert(mangleFunc!(int function(Object))("object.Object.opEquals") == "_D6object6Object8opEqualsFC6ObjectZi"); + static if(hasTypeBackRef) + { + assert(mangleFunc!(int function(Object))("object.Object.opEquals") == "_D6object6Object8opEqualsFCQ1IZi"); + assert(mangleFunc!(int function(Object, Object))("object.Object.opEquals") == "_D6object6Object8opEqualsFCQ1IQeZi"); + } + else + assert(mangleFunc!(int function(Object))("object.Object.opEquals") == "_D6object6Object8opEqualsFC6ObjectZi"); } unittest @@ -2343,7 +2528,7 @@ string decodeDmdString( const(char)[] ln, ref size_t p ) break; s ~= s[$ - zpos .. $ - zpos + zlen]; } - else if( Demangle.isAlpha(cast(char)ch) || Demangle.isDigit(cast(char)ch) || ch == '_' ) + else if( Demangle!().isAlpha(cast(char)ch) || Demangle!().isDigit(cast(char)ch) || ch == '_' ) s ~= cast(char) ch; else {