From 9ddca3b1327ccae185000a9cabaf6c5d61466907 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze <r.sagitario@gmx.de> Date: Sat, 27 May 2017 14:18:26 +0200 Subject: [PATCH 01/13] core.demangle: support template encoding without length prefix --- src/core/demangle.d | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/core/demangle.d b/src/core/demangle.d index ae68d22291..b4d12dc599 100644 --- a/src/core/demangle.d +++ b/src/core/demangle.d @@ -1483,21 +1483,21 @@ private struct Demangle TemplateInstanceName: Number __T LName TemplateArgs Z */ - void parseTemplateInstanceName() + void parseTemplateInstanceName(bool hasNumber) { debug(trace) printf( "parseTemplateInstanceName+\n" ); debug(trace) scope(success) printf( "parseTemplateInstanceName-\n" ); auto sav = pos; scope(failure) pos = sav; - auto n = decodeNumber(); + auto n = hasNumber ? decodeNumber() : 0; auto beg = pos; match( "__T" ); parseLName(); put( "!(" ); parseTemplateArgs(); match( 'Z' ); - if( pos - beg != n ) + if( hasNumber && pos - beg != n ) error( "Template name length mismatch" ); put( ')' ); } @@ -1532,6 +1532,11 @@ private struct Demangle // TemplateInstanceName -> Number "__T" switch( front ) { + case '_': + // no length encoding for templates for new mangling + parseTemplateInstanceName(false); + return; + case '0': .. case '9': if( mayBeTemplateInstanceName() ) { @@ -1540,7 +1545,7 @@ private struct Demangle try { debug(trace) printf( "may be template instance name\n" ); - parseTemplateInstanceName(); + parseTemplateInstanceName(true); return; } catch( ParseException e ) @@ -1589,12 +1594,12 @@ private struct Demangle put( '(' ); parseFuncArguments(); put( ')' ); - if( !isDigit( front ) ) // voldemort types don't have a return type on the function + if( !isDigit( front ) && front != '_' ) // voldemort types don't have a return type on the function { auto funclen = len; parseType(); - if( !isDigit( front ) ) + if( !isDigit( front ) && front != '_' ) { // not part of a qualified name, so back up pos = prevpos; @@ -1604,7 +1609,7 @@ private struct Demangle len = funclen; // remove return type from qualified name } } - } while( isDigit( front ) ); + } while( isDigit( front ) || front == '_' ); return dst[beg .. len]; } From ac13f8fd0d695031ae115b96464e3ef0fc758377 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze <r.sagitario@gmx.de> Date: Sat, 27 May 2017 17:18:08 +0200 Subject: [PATCH 02/13] core.demangle: add demangling of back references --- src/core/demangle.d | 202 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 163 insertions(+), 39 deletions(-) diff --git a/src/core/demangle.d b/src/core/demangle.d index b4d12dc599..1be88bf067 100644 --- a/src/core/demangle.d +++ b/src/core/demangle.d @@ -68,8 +68,9 @@ private struct Demangle char[] dst = null; size_t pos = 0; size_t len = 0; + size_t brp = 0; // current back reference pos AddType addType = AddType.yes; - + bool mute = false; static class ParseException : Exception { @@ -173,7 +174,7 @@ private struct Demangle dst[b] = t; } - if( val.length ) + if( val.length && !mute ) { assert( contains( dst[0 .. len], val ) ); debug(info) printf( "shifting (%.*s)\n", cast(int) val.length, val.ptr ); @@ -193,7 +194,7 @@ private struct Demangle char[] append( const(char)[] val ) { - if( val.length ) + if( val.length && !mute ) { if( !dst.length ) dst.length = minBufSize; @@ -296,6 +297,13 @@ private struct Demangle return char.init; } + char peek( size_t n ) + { + if( pos + n < buf.length ) + return buf[pos + n]; + return char.init; + } + void test( char val ) { @@ -334,6 +342,56 @@ private struct Demangle popFront(); } + bool isSymbolNameFront() + { + char val = front; + if( isDigit( val ) || val == '_' ) + return true; + if( val != 'Q' ) + return false; + + // check the back reference encoding after 'Q' + val = peekBackref(); + return isDigit( val ); // identifier ref + } + + // return the first character at the back reference + char peekBackref() + { + assert( front == 'Q' ); + auto n = decodeBackref!1(); + if (!n || n > pos) + error("invalid back reference"); + + return buf[pos - n]; + } + + size_t decodeBackref(size_t peekAt = 0)() + { + enum base = 26; + size_t n = 0; + for (size_t p; ; p++) + { + char t; + static if (peekAt > 0) + { + t = peek(peekAt + p); + } + else + { + t = front; + popFront(); + } + if (t < 'A' || t > 'Z') + { + if (t < 'a' || t > 'z') + error("invalid back reference"); + n = base * n + t - 'a'; + return n; + } + n = base * n + t - 'A'; + } + } ////////////////////////////////////////////////////////////////////////// // Parsing Implementation @@ -492,8 +550,24 @@ private struct Demangle debug(trace) printf( "parseLName+\n" ); debug(trace) scope(success) printf( "parseLName-\n" ); + if( front == 'Q' ) + { + // back reference to LName + auto refPos = pos; + popFront(); + size_t n = decodeBackref(); + if( !n || n > refPos ) + error( "Invalid LName back reference" ); + if ( !mute ) + { + auto savePos = pos; + scope(exit) pos = savePos; + pos = refPos - n; + parseLName(); + } + return; + } auto n = decodeNumber(); - if( !n || n > buf.length || n > buf.length - pos ) error( "LName must be at least 1 character" ); if( '_' != front && !isAlpha( front ) ) @@ -713,8 +787,30 @@ private struct Demangle auto beg = len; auto t = front; + char[] parseBackrefType(scope char[] delegate() parseDg) + { + if (pos == brp) + error("recursive back reference"); + auto refPos = pos; + popFront(); + auto n = decodeBackref(); + if (n == 0 || n > pos) + error("invalid back reference"); + if ( mute ) + return null; + auto savePos = pos; + auto saveBrp = brp; + scope(success) { pos = savePos; brp = saveBrp; } + pos = refPos - n; + brp = refPos; + auto ret = parseDg(); + return ret; + } + switch( t ) { + case 'Q': // Type back reference + return parseBackrefType( () => parseType( name ) ); case 'O': // Shared (O Type) popFront(); put( "shared(" ); @@ -801,7 +897,10 @@ private struct Demangle return dst[beg .. len]; case 'D': // TypeDelegate (D TypeFunction) popFront(); - parseTypeFunction( name, IsDelegate.yes ); + if( front == 'Q' ) + parseBackrefType( () => parseTypeFunction( name, IsDelegate.yes ) ); + else + parseTypeFunction( name, IsDelegate.yes ); return dst[beg .. len]; case 'n': // TypeNone (n) popFront(); @@ -1419,6 +1518,8 @@ private struct Demangle // generated by parseValue, so it is safe to simply // decrement len and let put/append do its thing. char t = front; // peek at type for parseValue + if( t == 'Q' ) + t = peekBackref(); char[] name; silent( name = parseType() ); parseValue( name, t ); continue; @@ -1430,7 +1531,7 @@ private struct Demangle { auto l = len; auto p = pos; - + auto b = brp; try { debug(trace) printf( "may be mangled name arg\n" ); @@ -1441,6 +1542,7 @@ private struct Demangle { len = l; pos = p; + brp = b; debug(trace) printf( "not a mangled name arg\n" ); } } @@ -1489,7 +1591,12 @@ private struct Demangle debug(trace) scope(success) printf( "parseTemplateInstanceName-\n" ); auto sav = pos; - scope(failure) pos = sav; + auto saveBrp = brp; + scope(failure) + { + pos = sav; + brp = saveBrp; + } auto n = hasNumber ? decodeNumber() : 0; auto beg = pos; match( "__T" ); @@ -1554,6 +1661,8 @@ private struct Demangle len = t; } } + goto case; + case 'Q': parseLName(); return; default: @@ -1561,6 +1670,47 @@ private struct Demangle } } + void parseFunctionType( bool muteReturnType ) + { + if( 'M' == front ) + { + // do not emit "needs this" + popFront(); + } + if( isCallConvention( front ) ) + { + // try to demangle a function, in case we are pointing to some function local + auto prevpos = pos; + auto prevlen = len; + + // we don't want calling convention and attributes in the qualified name + parseCallConvention(); + parseFuncAttr(); + len = prevlen; + + put( '(' ); + parseFuncArguments(); + put( ')' ); + if( !isSymbolNameFront() ) // voldemort types don't have a return type on the function + { + auto funclen = len; + + bool prevMute = mute; + scope(exit) mute = prevMute; + mute = muteReturnType; + parseType(); + + if( !muteReturnType && !isSymbolNameFront() ) + { + // not part of a qualified name, so back up + pos = prevpos; + len = prevlen; + } + else + len = funclen; // remove return type from qualified name + } + } + } /* QualifiedName: @@ -1579,37 +1729,8 @@ private struct Demangle if( n++ ) put( '.' ); parseSymbolName(); - - if( isCallConvention( front ) ) - { - // try to demangle a function, in case we are pointing to some function local - auto prevpos = pos; - auto prevlen = len; - - // we don't want calling convention and attributes in the qualified name - parseCallConvention(); - parseFuncAttr(); - len = prevlen; - - put( '(' ); - parseFuncArguments(); - put( ')' ); - if( !isDigit( front ) && front != '_' ) // voldemort types don't have a return type on the function - { - auto funclen = len; - parseType(); - - if( !isDigit( front ) && front != '_' ) - { - // not part of a qualified name, so back up - pos = prevpos; - len = prevlen; - } - else - len = funclen; // remove return type from qualified name - } - } - } while( isDigit( front ) || front == '_' ); + parseFunctionType( false ); + } while( isSymbolNameFront() ); return dst[beg .. len]; } @@ -1662,7 +1783,7 @@ private struct Demangle auto newsz = a < b ? b : a; debug(info) printf( "growing dst to %lu bytes\n", newsz ); dst.length = newsz; - pos = len = 0; + pos = len = brp = 0; continue; } catch( ParseException e ) @@ -1981,6 +2102,9 @@ version(unittest) ["_D5bug145Class3barMFNjZPv", "return void* bug14.Class.bar()"], ["_D5bug143fooFMPvZPv", "void* bug14.foo(scope void*)"], ["_D5bug143barFMNkPvZPv", "void* bug14.bar(scope return void*)"], + // back references + ["_D4core4stdc5errnoQgFZi", "int core.stdc.errno.errno()"], // identifier back reference + ["_D4testFS10structnameQnZb", "bool test(structname, structname)"], // type back reference ]; template staticIota(int x) From ea596c53922f4684f1bc47a5f059023ad8a603d9 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze <r.sagitario@gmx.de> Date: Sun, 28 May 2017 22:37:02 +0200 Subject: [PATCH 03/13] core.demangle: add test cases for back references --- src/core/demangle.d | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/core/demangle.d b/src/core/demangle.d index 1be88bf067..ac0b62b8de 100644 --- a/src/core/demangle.d +++ b/src/core/demangle.d @@ -2105,8 +2105,16 @@ version(unittest) // back references ["_D4core4stdc5errnoQgFZi", "int core.stdc.errno.errno()"], // identifier back reference ["_D4testFS10structnameQnZb", "bool test(structname, structname)"], // type back reference + ["_D3std11parallelism__T4TaskS8unittest3cmpTAyaTQeZQBb6__dtorMFNfZv", + "@safe void std.parallelism.Task!(unittest.cmp, immutable(char)[], immutable(char)[]).Task.__dtor()"], + // 1.s.s.foo from https://issues.dlang.org/show_bug.cgi?id=15831 + ["_D13testexpansion44__T1sTS13testexpansion8__T1sTiZ1sFiZ6ResultZ1sFS13testexpansion8__T1sTiZ1sFiZ6ResultZ6Result3fooMFNaNfZv", + "pure @safe void testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result.foo()"], + ["_D13testexpansion__T1sTSQw__TQjTiZQoFiZ6ResultZQBbFQBcZQq3fooMFNaNfZv", + "pure @safe void testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result.foo()"], ]; + template staticIota(int x) { template Seq(T...){ alias Seq = T; } From b8dcb052fa5779ef8f997f7890c90fa472f115bf Mon Sep 17 00:00:00 2001 From: Rainer Schuetze <r.sagitario@gmx.de> Date: Fri, 2 Jun 2017 22:41:26 +0200 Subject: [PATCH 04/13] demangle: improve performance for longer symbols --- src/core/demangle.d | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/core/demangle.d b/src/core/demangle.d index ac0b62b8de..255f53bf27 100644 --- a/src/core/demangle.d +++ b/src/core/demangle.d @@ -165,27 +165,21 @@ private struct Demangle } + // move val to the end of the dst buffer char[] shift( const(char)[] val ) { - void exch( size_t a, size_t b ) - { - char t = dst[a]; - dst[a] = dst[b]; - dst[b] = t; - } - if( val.length && !mute ) { assert( contains( dst[0 .. len], val ) ); debug(info) printf( "shifting (%.*s)\n", cast(int) val.length, val.ptr ); - for( size_t n = 0; n < val.length; n++ ) - { - for( size_t v = val.ptr - dst.ptr; v + 1 < len; v++ ) - { - exch( v, v + 1 ); - } - } + if (len + val.length > dst.length) + overflow(); + size_t v = val.ptr - dst.ptr; + dst[len .. len + val.length] = val[]; + for (size_t p = v; p < len; p++) + dst[p] = dst[p + val.length]; + return dst[len - val.length .. len]; } return null; From f7f87ac45b995e5c5f5a7af3c7ee0629c24804a1 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze <r.sagitario@gmx.de> Date: Fri, 2 Jun 2017 22:51:56 +0200 Subject: [PATCH 05/13] fix issue 14576: backtrack on ambiguous template alias parameter encoding, support no full length specified --- src/core/demangle.d | 52 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/src/core/demangle.d b/src/core/demangle.d index 255f53bf27..aa8725a1d9 100644 --- a/src/core/demangle.d +++ b/src/core/demangle.d @@ -1485,6 +1485,7 @@ private struct Demangle TemplateArgX: T Type V Type Value + S Number_opt QualifiedName S LName */ void parseTemplateArgs() @@ -1492,6 +1493,7 @@ private struct Demangle debug(trace) printf( "parseTemplateArgs+\n" ); debug(trace) scope(success) printf( "parseTemplateArgs-\n" ); + L_nextArg: for( size_t n = 0; true; n++ ) { if( front == 'H' ) @@ -1540,7 +1542,32 @@ private struct Demangle debug(trace) printf( "not a mangled name arg\n" ); } } - + if( isDigit( front ) && isDigit( peek( 1 ) ) ) + { + // ambiguity: length followed by qualified name (starting with number) + // try all possible pairs of numbers + auto qlen = decodeNumber() / 10; // last digit needed for QualifiedName + pos--; + auto l = len; + auto p = pos; + auto b = brp; + while( qlen > 0 ) + { + try + { + parseQualifiedName(); + if( pos == p + qlen ) + continue L_nextArg; + } + catch( ParseException e ) + { + } + qlen /= 10; // retry with one digit less + pos = --p; + len = l; + brp = b; + } + } parseQualifiedName(); continue; default: @@ -1557,11 +1584,20 @@ private struct Demangle auto p = pos; scope(exit) pos = p; - auto n = decodeNumber(); - return n >= 4 && - pos < buf.length && '_' == buf[pos++] && - pos < buf.length && 'D' == buf[pos++] && - isDigit(buf[pos]); + if ( isDigit( buf[pos] ) ) + { + auto n = decodeNumber(); + return n >= 4 && + pos < buf.length && '_' == buf[pos++] && + pos < buf.length && 'D' == buf[pos++] && + isDigit( buf[pos] ); + } + else + { + return pos < buf.length && '_' == buf[pos++] && + pos < buf.length && 'D' == buf[pos++] && + isSymbolNameFront(); + } } @@ -1570,7 +1606,9 @@ private struct Demangle debug(trace) printf( "parseMangledNameArg+\n" ); debug(trace) scope(success) printf( "parseMangledNameArg-\n" ); - auto n = decodeNumber(); + size_t n = 0; + if ( isDigit( front ) ) + n = decodeNumber(); parseMangledName( n ); } From 0f564d62e8e982f7e73ac662592198a7313348fb Mon Sep 17 00:00:00 2001 From: Rainer Schuetze <r.sagitario@gmx.de> Date: Fri, 2 Jun 2017 22:45:12 +0200 Subject: [PATCH 06/13] fix issues 14319, 14563, 17609, 17610, 17611: demangle: better detect function signatures in QualifiedNames, only print return types for the outmost symbol --- src/core/demangle.d | 247 +++++++++++++++++++++++++++++++++----------- 1 file changed, 186 insertions(+), 61 deletions(-) diff --git a/src/core/demangle.d b/src/core/demangle.d index aa8725a1d9..c50abb3f57 100644 --- a/src/core/demangle.d +++ b/src/core/demangle.d @@ -185,6 +185,20 @@ private struct Demangle return null; } + // remove val from dst buffer + void remove( const(char)[] val ) + { + if( val.length ) + { + assert( contains( dst[0 .. len], val ) ); + debug(info) printf( "removing (%.*s)\n", cast(int) val.length, val.ptr ); + + size_t v = val.ptr - dst.ptr; + for (size_t p = v; p < len; p++) + dst[p] = dst[p + val.length]; + len -= val.length; + } + } char[] append( const(char)[] val ) { @@ -891,10 +905,19 @@ private struct Demangle return dst[beg .. len]; case 'D': // TypeDelegate (D TypeFunction) popFront(); + auto modbeg = len; + parseModifier(); + auto modend = len; if( front == 'Q' ) parseBackrefType( () => parseTypeFunction( name, IsDelegate.yes ) ); else parseTypeFunction( name, IsDelegate.yes ); + if (modend > modbeg) + { + // move modifiers behind the function arguments + shift(dst[modend-1 .. modend]); // trailing space + shift(dst[modbeg .. modend-1]); + } return dst[beg .. len]; case 'n': // TypeNone (n) popFront(); @@ -1049,6 +1072,39 @@ private struct Demangle } } + void parseModifier() + { + switch( front ) + { + case 'y': + popFront(); + put( "immutable " ); + break; + case 'O': + popFront(); + put( "shared " ); + if( front == 'x' ) + goto case 'x'; + if( front == 'N' ) + goto case 'N'; + break; + case 'N': + if( peek( 1 ) != 'g' ) + break; + popFront(); + popFront(); + put( "inout " ); + if( front == 'x' ) + goto case 'x'; + break; + case 'x': + popFront(); + put( "const " ); + break; + default: break; + } + } + void parseFuncAttr() { // FuncAttrs @@ -1174,7 +1230,11 @@ private struct Demangle } enum IsDelegate { no, yes } - // returns the argument list with the left parenthesis, but not the right + + /* + TypeFunction: + CallConvention FuncAttrs Arguments ArgClose Type + */ char[] parseTypeFunction( char[] name = null, IsDelegate isdg = IsDelegate.no ) { debug(trace) printf( "parseTypeFunction+\n" ); @@ -1182,33 +1242,40 @@ private struct Demangle auto beg = len; parseCallConvention(); + auto attrbeg = len; parseFuncAttr(); - beg = len; + auto argbeg = len; put( '(' ); - scope(success) + parseFuncArguments(); + put( ')' ); + if (attrbeg < argbeg) { - put( ')' ); - auto t = len; - parseType(); - put( ' ' ); - if( name.length ) + // move function attributes behind arguments + shift( dst[argbeg - 1 .. argbeg] ); // trailing space + shift( dst[attrbeg .. argbeg - 1] ); // attributes + argbeg = attrbeg; + } + auto retbeg = len; + parseType(); + put( ' ' ); + // append name/delegate/function + if( name.length ) + { + if( !contains( dst[0 .. len], name ) ) + put( name ); + else if( shift( name ).ptr != name.ptr ) { - if( !contains( dst[0 .. len], name ) ) - put( name ); - else if( shift( name ).ptr != name.ptr ) - { - beg -= name.length; - t -= name.length; - } + argbeg -= name.length; + retbeg -= name.length; } - else if( IsDelegate.yes == isdg ) - put( "delegate" ); - else - put( "function" ); - shift( dst[beg .. t] ); } - parseFuncArguments(); + else if( IsDelegate.yes == isdg ) + put( "delegate" ); + else + put( "function" ); + // move arguments and attributes behind name + shift( dst[argbeg .. retbeg] ); return dst[beg..len]; } @@ -1609,7 +1676,7 @@ private struct Demangle size_t n = 0; if ( isDigit( front ) ) n = decodeNumber(); - parseMangledName( n ); + parseMangledName( false, n ); } @@ -1702,46 +1769,52 @@ private struct Demangle } } - void parseFunctionType( bool muteReturnType ) + // parse optional function arguments as part of a symbol name, i.e without return type + // if keepAttr, the calling convention and function attributes are not discarded, but returned + char[] parseFunctionTypeNoReturn( bool keepAttr = false ) { - if( 'M' == front ) - { - // do not emit "needs this" - popFront(); - } - if( isCallConvention( front ) ) - { - // try to demangle a function, in case we are pointing to some function local - auto prevpos = pos; - auto prevlen = len; - - // we don't want calling convention and attributes in the qualified name - parseCallConvention(); - parseFuncAttr(); - len = prevlen; + // try to demangle a function, in case we are pointing to some function local + auto prevpos = pos; + auto prevlen = len; + auto prevbrp = brp; - put( '(' ); - parseFuncArguments(); - put( ')' ); - if( !isSymbolNameFront() ) // voldemort types don't have a return type on the function + char[] attr; + try + { + if( 'M' == front ) { - auto funclen = len; - - bool prevMute = mute; - scope(exit) mute = prevMute; - mute = muteReturnType; - parseType(); - - if( !muteReturnType && !isSymbolNameFront() ) + // do not emit "needs this" + popFront(); + parseModifier(); + } + if( isCallConvention( front ) ) + { + // we don't want calling convention and attributes in the qualified name + parseCallConvention(); + parseFuncAttr(); + if( keepAttr ) { - // not part of a qualified name, so back up - pos = prevpos; - len = prevlen; + attr = dst[prevlen .. len]; } else - len = funclen; // remove return type from qualified name + { + len = prevlen; + } + + put( '(' ); + parseFuncArguments(); + put( ')' ); } } + catch( ParseException ) + { + // not part of a qualified name, so back up + pos = prevpos; + len = prevlen; + brp = prevbrp; + attr = null; + } + return attr; } /* @@ -1761,7 +1834,8 @@ private struct Demangle if( n++ ) put( '.' ); parseSymbolName(); - parseFunctionType( false ); + parseFunctionTypeNoReturn(); + } while( isSymbolNameFront() ); return dst[beg .. len]; } @@ -1772,7 +1846,7 @@ private struct Demangle _D QualifiedName Type _D QualifiedName M Type */ - void parseMangledName(size_t n = 0) + void parseMangledName( bool displayType, size_t n = 0 ) { debug(trace) printf( "parseMangledName+\n" ); debug(trace) scope(success) printf( "parseMangledName-\n" ); @@ -1784,18 +1858,68 @@ private struct Demangle match( 'D' ); do { - name = parseQualifiedName(); + size_t beg = len; + size_t nameEnd = len; + char[] attr; + do + { + if( attr ) + remove( attr ); // dump attributes of parent symbols + if( beg != len ) + put( '.' ); + parseSymbolName(); + nameEnd = len; + attr = parseFunctionTypeNoReturn( displayType ); + + } while( isSymbolNameFront() ); + + if( displayType ) + { + attr = shift( attr ); + nameEnd = len - attr.length; // name includes function arguments + } + name = dst[beg .. nameEnd]; + debug(info) printf( "name (%.*s)\n", cast(int) name.length, name.ptr ); if( 'M' == front ) popFront(); // has 'this' pointer - if( AddType.yes == addType ) - parseType( name ); + + auto lastlen = len; + auto type = parseType(); + if( displayType ) + { + if( type.length ) + put( ' ' ); + // sort (name,attr,type) -> (attr,type,name) + shift( name ); + } + else + { + // remove type + assert( attr.length == 0 ); + len = lastlen; + } if( pos >= buf.length || (n != 0 && pos >= end) ) return; + + switch( front ) + { + case 'T': // terminators when used as template alias parameter + case 'V': + case 'S': + case 'Z': + return; + default: + } put( '.' ); + } while( true ); } + void parseMangledName() + { + parseMangledName( AddType.yes == addType ); + } char[] doDemangle(alias FUNC)() { @@ -2081,7 +2205,8 @@ version(unittest) ["_D4test3fooAa", "char[] test.foo"], ["_D8demangle8demangleFAaZAa", "char[] demangle.demangle(char[])"], ["_D6object6Object8opEqualsFC6ObjectZi", "int object.Object.opEquals(Object)"], - ["_D4test2dgDFiYd", "double test.dg(int, ...)"], + ["_D4test2dgDFiYd", "double delegate(int, ...) test.dg"], + ["_D4test2dgDxFNfiYd", "double delegate(int, ...) @safe const test.dg"], //["_D4test58__T9factorialVde67666666666666860140VG5aa5_68656c6c6fVPvnZ9factorialf", ""], //["_D4test101__T9factorialVde67666666666666860140Vrc9a999999999999d9014000000000000000c00040VG5aa5_68656c6c6fVPvnZ9factorialf", ""], ["_D4test34__T3barVG3uw3_616263VG3wd3_646566Z1xi", "int test.bar!(\"abc\"w, \"def\"d).x"], @@ -2117,7 +2242,7 @@ version(unittest) ["_D8demangle4testFNhG4fZv", "void demangle.test(__vector(float[4]))"], ["_D8demangle4testFNhG2dZv", "void demangle.test(__vector(double[2]))"], ["_D8demangle4testFNhG4fNhG4fZv", "void demangle.test(__vector(float[4]), __vector(float[4]))"], - ["_D8bug1119234__T3fooS23_D8bug111924mainFZ3bariZ3fooMFZv","void bug11192.foo!(int bug11192.main().bar).foo()"], + ["_D8bug1119234__T3fooS23_D8bug111924mainFZ3bariZ3fooMFZv","void bug11192.foo!(bug11192.main().bar).foo()"], ["_D13libd_demangle12__ModuleInfoZ", "libd_demangle.__ModuleInfo"], ["_D15TypeInfo_Struct6__vtblZ", "TypeInfo_Struct.__vtbl"], ["_D3std5stdio12__ModuleInfoZ", "std.stdio.__ModuleInfo"], From 7a92a41d437c6cfe24f0fa46bf7f154401f037f8 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze <r.sagitario@gmx.de> Date: Fri, 2 Jun 2017 22:59:23 +0200 Subject: [PATCH 07/13] support template aliases to externally mangled symbols with 'X' instead of 'S' --- src/core/demangle.d | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/core/demangle.d b/src/core/demangle.d index c50abb3f57..6772fde6cb 100644 --- a/src/core/demangle.d +++ b/src/core/demangle.d @@ -1553,7 +1553,7 @@ private struct Demangle T Type V Type Value S Number_opt QualifiedName - S LName + X ExternallyMangledName */ void parseTemplateArgs() { @@ -1637,6 +1637,11 @@ private struct Demangle } parseQualifiedName(); continue; + case 'X': + popFront(); + putComma(n); + parseLName(); + continue; default: return; } @@ -2269,6 +2274,8 @@ version(unittest) "pure @safe void testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result.foo()"], ["_D13testexpansion__T1sTSQw__TQjTiZQoFiZ6ResultZQBbFQBcZQq3fooMFNaNfZv", "pure @safe void testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result.foo()"], + ["_D3std5stdio4File__T8lockImplX10LockFileExTykZQBaMFmmykZi", // C function as template alias parameter + "int std.stdio.File.lockImpl!(LockFileEx, immutable(uint)).lockImpl(ulong, ulong, immutable(uint))"], ]; From fb6ebcfffa40ebf59aae51e1fbe2f5e113793847 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze <r.sagitario@gmx.de> Date: Fri, 2 Jun 2017 23:00:05 +0200 Subject: [PATCH 08/13] demangle with back references: add test cases --- src/core/demangle.d | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/core/demangle.d b/src/core/demangle.d index 6772fde6cb..9f1281058b 100644 --- a/src/core/demangle.d +++ b/src/core/demangle.d @@ -2264,6 +2264,13 @@ version(unittest) ["_D5bug145Class3barMFNjZPv", "return void* bug14.Class.bar()"], ["_D5bug143fooFMPvZPv", "void* bug14.foo(scope void*)"], ["_D5bug143barFMNkPvZPv", "void* bug14.bar(scope return void*)"], + ["_D3std5range15__T4iotaTtTtTtZ4iotaFtttZ6Result7opIndexMNgFNaNbNiNfmZNgt", + "inout pure nothrow @nogc @safe inout(ushort) std.range.iota!(ushort, ushort, ushort).iota(ushort, ushort, ushort).Result.opIndex(ulong)"], + ["_D3std6format77__T6getNthVAyaa13_696e7465676572207769647468S233std6traits10isIntegralTiTkTkZ6getNthFNaNfkkkZi", + "pure @safe int std.format.getNth!(\"integer width\", std.traits.isIntegral, int, uint, uint).getNth(uint, uint, uint)"], + ["_D3std11parallelism42__T16RoundRobinBufferTDFKAaZvTDxFNaNdNeZbZ16RoundRobinBuffer5primeMFZv", + "void std.parallelism.RoundRobinBuffer!(void delegate(ref char[]), bool delegate() pure @property @trusted const).RoundRobinBuffer.prime()"], + // back references ["_D4core4stdc5errnoQgFZi", "int core.stdc.errno.errno()"], // identifier back reference ["_D4testFS10structnameQnZb", "bool test(structname, structname)"], // type back reference @@ -2274,8 +2281,37 @@ version(unittest) "pure @safe void testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result.foo()"], ["_D13testexpansion__T1sTSQw__TQjTiZQoFiZ6ResultZQBbFQBcZQq3fooMFNaNfZv", "pure @safe void testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result.foo()"], + // ambiguity on 'V', template value argument or pascal function + ["_D3std4conv__T7enumRepTyAaTEQBa12experimental9allocator15building_blocks15stats_collector7OptionsVQCti64ZQDnyQDh", + "immutable(char[]) std.conv.enumRep!(immutable(char[]), std.experimental.allocator.building_blocks.stats_collector.Options, 64).enumRep"], + // symbol back reference to location with symbol back reference + ["_D3std12experimental9allocator6common__T10reallocateTSQCaQBzQBo15building_blocks17kernighan_ritchie__T8KRRegionTSQEhQEgQDvQCh14null_allocator13NullAllocatorZQCdZQErFNaNbNiKQEpKAvmZb", + "pure nothrow @nogc bool std.experimental.allocator.common.reallocate!(std.experimental.allocator.building_blocks.kernighan_ritchie.KRRegion!(" + ~"std.experimental.allocator.building_blocks.null_allocator.NullAllocator).KRRegion).reallocate(ref " + ~"std.experimental.allocator.building_blocks.kernighan_ritchie.KRRegion!(std.experimental.allocator.building_blocks.null_allocator.NullAllocator).KRRegion, ref void[], ulong)"], + ["_D3std9exception__T11doesPointToTASQBh5regex8internal2ir10NamedGroupTQBkTvZQCeFNaNbNiNeKxASQDlQCeQCbQBvQBvKxQtZb", + "pure nothrow @nogc @trusted bool std.exception.doesPointTo!(std.regex.internal.ir.NamedGroup[], " + ~"std.regex.internal.ir.NamedGroup[], void).doesPointTo(ref const(std.regex.internal.ir.NamedGroup[]), ref const(std.regex.internal.ir.NamedGroup[]))"], + ["_D3std9algorithm9iteration__T14SplitterResultS_DQBu3uni7isWhiteFNaNbNiNfwZbTAyaZQBz9__xtoHashFNbNeKxSQDvQDuQDn__TQDgS_DQEnQCtQCsQCnTQCeZQEdZm", + "nothrow @trusted ulong std.algorithm.iteration.SplitterResult!(std.uni.isWhite(dchar), immutable(char)[]).SplitterResult." + ~"__xtoHash(ref const(std.algorithm.iteration.SplitterResult!(std.uni.isWhite, immutable(char)[]).SplitterResult))"], + ["_D3std8typecons__T7TypedefTCQBaQz19__unittestL6513_208FNfZ7MyClassVQBonVAyanZQCh6__ctorMFNaNbNcNiNfQCuZSQDyQDx__TQDrTQDmVQDqnVQCcnZQEj", + "pure nothrow ref @nogc @safe std.typecons.Typedef!(std.typecons.__unittestL6513_208().MyClass, null, null).Typedef " + ~"std.typecons.Typedef!(std.typecons.__unittestL6513_208().MyClass, null, null).Typedef.__ctor(std.typecons.__unittestL6513_208().MyClass)"], + ["_D3std6getopt__TQkTAyaTDFNaNbNiNfQoZvTQtTDQsZQBnFNfKAQBiQBlQBkQBrQyZSQCpQCo12GetoptResult", + "@safe std.getopt.GetoptResult std.getopt.getopt!(immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe, " + ~"immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe)." + ~"getopt(ref immutable(char)[][], immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe, " + ~"immutable(char)[], void delegate(immutable(char)[]) pure nothrow @nogc @safe)"], + ["_D3std5regex8internal9kickstart__T7ShiftOrTaZQl11ShiftThread__T3setS_DQCqQCpQCmQCg__TQBzTaZQCfQBv10setInvMaskMFNaNbNiNfkkZvZQCjMFNaNfwZv", + "pure @safe void std.regex.internal.kickstart.ShiftOr!(char).ShiftOr.ShiftThread.set!(std.regex.internal.kickstart.ShiftOr!(char).ShiftOr.ShiftThread.setInvMask(uint, uint)).set(dchar)"], ["_D3std5stdio4File__T8lockImplX10LockFileExTykZQBaMFmmykZi", // C function as template alias parameter "int std.stdio.File.lockImpl!(LockFileEx, immutable(uint)).lockImpl(ulong, ulong, immutable(uint))"], + // back reference for type in template AA parameter value + ["_D3std9algorithm9iteration__T12FilterResultSQBq8typecons__T5TupleTiVAyaa1_61TiVQla1_62TiVQva1_63ZQBm__T6renameVHiQBtA2i0a1_63i2a1_61ZQBeMFNcZ9__lambda1TAiZQEw9__xtoHashFNbNeKxSQGsQGrQGk__TQGdSQHiQFs__TQFmTiVQFja1_61TiVQFua1_62TiVQGfa1_63ZQGx__TQFlVQFhA2i0a1_63i2a1_61ZQGjMFNcZQFfTQEyZQJvZm", + `nothrow @trusted ulong std.algorithm.iteration.FilterResult!(std.typecons.Tuple!(int, "a", int, "b", int, "c").` + ~`Tuple.rename!([0:"c", 2:"a"]).rename().__lambda1, int[]).FilterResult.__xtoHash(ref const(std.algorithm.iteration.` + ~`FilterResult!(std.typecons.Tuple!(int, "a", int, "b", int, "c").Tuple.rename!([0:"c", 2:"a"]).rename().__lambda1, int[]).FilterResult))`], ]; From f54956f7bd4dcffe5a09a73f788775f8120545e4 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze <r.sagitario@gmx.de> Date: Thu, 22 Jun 2017 08:46:55 +0200 Subject: [PATCH 09/13] purify Demangle --- src/core/demangle.d | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/core/demangle.d b/src/core/demangle.d index 9f1281058b..06c9b9f656 100644 --- a/src/core/demangle.d +++ b/src/core/demangle.d @@ -43,7 +43,7 @@ private struct Demangle // allocation during the course of a parsing run, this is still // faster than assembling the result piecemeal. - +pure: enum AddType { no, yes } @@ -524,10 +524,7 @@ private struct Demangle tbuf[tlen] = 0; debug(info) printf( "got (%s)\n", tbuf.ptr ); - import core.stdc.stdlib : strtold; - val = strtold( tbuf.ptr, null ); - import core.stdc.stdio : snprintf; - tlen = snprintf( tbuf.ptr, tbuf.length, "%#Lg", val ); + pureReprintReal( tbuf.ptr, tbuf.length ); debug(info) printf( "converted (%.*s)\n", cast(int) tlen, tbuf.ptr ); put( tbuf[0 .. tlen] ); } @@ -795,7 +792,7 @@ private struct Demangle auto beg = len; auto t = front; - char[] parseBackrefType(scope char[] delegate() parseDg) + char[] parseBackrefType(scope char[] delegate() pure parseDg) pure { if (pos == brp) error("recursive back reference"); @@ -2386,3 +2383,23 @@ string decodeDmdString( const(char)[] ln, ref size_t p ) } return s; } + +// locally purified for internal use here only +extern (C) private +{ + pure @trusted @nogc nothrow pragma(mangle, "fakePureReprintReal") real pureReprintReal(char* nptr, size_t len); + + char* fakePureReprintReal(char* nptr, size_t len) + { + import core.stdc.stdlib : strtold; + import core.stdc.stdio : snprintf; + import core.stdc.errno : errno; + + const err = errno; + real val = strtold(nptr, null); + len = snprintf(nptr, len, "%#Lg", val); + errno = err; + + return nptr; + } +} From 92cee12b8ac8c5bbc97b0ecdec0500df34e6cd26 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze <r.sagitario@gmx.de> Date: Thu, 22 Jun 2017 08:48:43 +0200 Subject: [PATCH 10/13] use DbI to reencode mangled string with identifier back references --- src/core/demangle.d | 213 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 207 insertions(+), 6 deletions(-) diff --git a/src/core/demangle.d b/src/core/demangle.d index 06c9b9f656..191dfae571 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 { @@ -555,6 +563,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 @@ -787,6 +799,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; @@ -1986,7 +2002,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(); } @@ -2004,10 +2020,178 @@ 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); + + void flushPosition(ref Remangle d) + { + if (lastpos < d.pos) + { + result ~= d.buf[lastpos .. d.pos]; + } + else if (lastpos > d.pos) + { + // roll back to earlier position + while (replacements.length > 0 && replacements[$-1].pos > d.pos) + replacements = replacements[0 .. $-1]; + + if (replacements.length > 0) + result.length = replacements[$-1].respos + d.pos - replacements[$-1].pos; + else + result.length = d.pos; + } + } + + bool parseLName(ref Remangle d) + { + flushPosition(d); + + 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.decodeBackref(); + 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); + 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); + 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; + + flushPosition(d); + + auto refPos = d.pos; + d.popFront(); + auto n = d.decodeBackref(); + if (n == 0 || n > refPos) + d.error("invalid back reference"); + + size_t npos = positionInResult(refPos - n); + size_t reslen = result.length; + encodeBackref(reslen - npos); + + lastpos = d.pos; + return result[reslen .. $]; // anything but null + } + + void encodeBackref(size_t relpos) + { + result ~= 'Q'; + enum base = 26; + size_t div = 1; + while (relpos >= div * base) + div *= base; + while (div >= base) + { + auto dig = (relpos / div); + result ~= cast(char)('A' + dig); + relpos -= dig * div; + div /= base; + } + result ~= cast(char)('a' + 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. @@ -2069,7 +2253,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]; } @@ -2129,12 +2317,25 @@ 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") == "_D6object6Object8opEqualsFCQsZi"); + assert(mangleFunc!(int function(Object, Object))("object.Object.opEquals") == "_D6object6Object8opEqualsFCQsQdZi"); + } + else + { + auto mngl = mangleFunc!(int function(Object))("object.Object.opEquals"); + assert(mngl == "_D6object6Object8opEqualsFC6ObjectZi"); + assert(reencodeMangled(mngl) == "_D6object6Object8opEqualsFCQtZi"); + } + // trigger back tracking with ambiguity on '__T', template or identifier + assert(reencodeMangled("_D3std4conv4conv7__T3std4convi") == "_D3std4convQf7__T3stdQpi"); } unittest @@ -2373,7 +2574,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 { From dc455719c50a433a3720bdeade0912eb386e5845 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze <r.sagitario@gmx.de> Date: Mon, 26 Jun 2017 08:30:52 +0200 Subject: [PATCH 11/13] support LName with length 0 as "__anonymous" --- src/core/demangle.d | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/core/demangle.d b/src/core/demangle.d index 191dfae571..ffd14fe69c 100644 --- a/src/core/demangle.d +++ b/src/core/demangle.d @@ -585,7 +585,12 @@ pure: return; } auto n = decodeNumber(); - if( !n || n > buf.length || n > buf.length - pos ) + if( n == 0 ) + { + put( "__anonymous" ); + return; + } + if( n > buf.length || n > buf.length - pos ) error( "LName must be at least 1 character" ); if( '_' != front && !isAlpha( front ) ) error( "Invalid character in LName" ); @@ -2332,7 +2337,8 @@ unittest { auto mngl = mangleFunc!(int function(Object))("object.Object.opEquals"); assert(mngl == "_D6object6Object8opEqualsFC6ObjectZi"); - assert(reencodeMangled(mngl) == "_D6object6Object8opEqualsFCQtZi"); + auto remngl = reencodeMangled(mngl); + assert(remngl == "_D6object6Object8opEqualsFCQsZi"); } // trigger back tracking with ambiguity on '__T', template or identifier assert(reencodeMangled("_D3std4conv4conv7__T3std4convi") == "_D3std4convQf7__T3stdQpi"); @@ -2468,6 +2474,10 @@ version(unittest) "pure @safe int std.format.getNth!(\"integer width\", std.traits.isIntegral, int, uint, uint).getNth(uint, uint, uint)"], ["_D3std11parallelism42__T16RoundRobinBufferTDFKAaZvTDxFNaNdNeZbZ16RoundRobinBuffer5primeMFZv", "void std.parallelism.RoundRobinBuffer!(void delegate(ref char[]), bool delegate() pure @property @trusted const).RoundRobinBuffer.prime()"], + // Lname '0' + ["_D3std9algorithm9iteration__T9MapResultSQBmQBlQBe005stripTAAyaZQBi7opSliceMFNaNbNiNfmmZSQDiQDhQDa__TQCtSQDyQDxQDq00QCmTQCjZQDq", + "pure nothrow @nogc @safe std.algorithm.iteration.MapResult!(std.algorithm.iteration.__anonymous.strip, " + ~"immutable(char)[][]).MapResult std.algorithm.iteration.MapResult!(std.algorithm.iteration.strip, immutable(char)[][]).MapResult.opSlice(ulong, ulong)"], // back references ["_D4core4stdc5errnoQgFZi", "int core.stdc.errno.errno()"], // identifier back reference From 2e37769507517fb5f6600172e4ed8548c713a3df Mon Sep 17 00:00:00 2001 From: Rainer Schuetze <r.sagitario@gmx.de> Date: Mon, 3 Jul 2017 09:00:30 +0200 Subject: [PATCH 12/13] fix issue 16664 - restrict trusted code to a few small functions and make demangler @safe, pure and nothrow --- src/core/demangle.d | 58 +++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/src/core/demangle.d b/src/core/demangle.d index ffd14fe69c..d205b7d541 100644 --- a/src/core/demangle.d +++ b/src/core/demangle.d @@ -50,7 +50,7 @@ private struct Demangle(Hooks = NoHooks) // allocation during the course of a parsing run, this is still // faster than assembling the result piecemeal. -pure: +pure @safe: enum AddType { no, yes } @@ -98,7 +98,7 @@ pure: } - static void error( string msg = "Invalid symbol" ) + static void error( string msg = "Invalid symbol" ) @trusted /* exception only used in module */ { //throw new ParseException( msg ); debug(info) printf( "error: %.*s\n", cast(int) msg.length, msg.ptr ); @@ -108,7 +108,7 @@ pure: } - static void overflow( string msg = "Buffer overflow" ) + static void overflow( string msg = "Buffer overflow" ) @trusted /* exception only used in module */ { //throw new OverflowException( msg ); debug(info) printf( "overflow: %.*s\n", cast(int) msg.length, msg.ptr ); @@ -161,7 +161,7 @@ pure: ////////////////////////////////////////////////////////////////////////// - static bool contains( const(char)[] a, const(char)[] b ) + static bool contains( const(char)[] a, const(char)[] b ) @trusted { if (a.length && b.length) { @@ -183,7 +183,7 @@ pure: if (len + val.length > dst.length) overflow(); - size_t v = val.ptr - dst.ptr; + size_t v = &val[0] - &dst[0]; dst[len .. len + val.length] = val[]; for (size_t p = v; p < len; p++) dst[p] = dst[p + val.length]; @@ -201,7 +201,7 @@ pure: assert( contains( dst[0 .. len], val ) ); debug(info) printf( "removing (%.*s)\n", cast(int) val.length, val.ptr ); - size_t v = val.ptr - dst.ptr; + size_t v = &val[0] - &dst[0]; for (size_t p = v; p < len; p++) dst[p] = dst[p + val.length]; len -= val.length; @@ -217,7 +217,7 @@ pure: assert( !contains( dst[0 .. len], val ) ); debug(info) printf( "appending (%.*s)\n", cast(int) val.length, val.ptr ); - if( dst.ptr + len == val.ptr && + if( &dst[len] == &val[0] && dst.length - len >= val.length ) { // data is already in place @@ -532,7 +532,7 @@ pure: tbuf[tlen] = 0; debug(info) printf( "got (%s)\n", tbuf.ptr ); - pureReprintReal( tbuf.ptr, tbuf.length ); + pureReprintReal( tbuf[] ); debug(info) printf( "converted (%.*s)\n", cast(int) tlen, tbuf.ptr ); put( tbuf[0 .. tlen] ); } @@ -813,7 +813,7 @@ pure: auto beg = len; auto t = front; - char[] parseBackrefType(scope char[] delegate() pure parseDg) pure + char[] parseBackrefType(scope char[] delegate() pure @safe parseDg) pure @safe { if (pos == brp) error("recursive back reference"); @@ -1977,15 +1977,19 @@ pure: dst[0 .. buf.length] = buf[]; return dst[0 .. buf.length]; } + catch( Exception e ) + { + assert( false ); // no other exceptions thrown + } } } - char[] demangleName() + char[] demangleName() nothrow { return doDemangle!parseMangledName(); } - char[] demangleType() + char[] demangleType() nothrow { return doDemangle!parseType(); } @@ -2004,7 +2008,7 @@ pure: * The demangled name or the original string if the name is not a mangled D * name. */ -char[] demangle( const(char)[] buf, char[] dst = null ) +char[] demangle( const(char)[] buf, char[] dst = null ) nothrow pure @safe { //return Demangle(buf, dst)(); auto d = Demangle!()(buf, dst); @@ -2023,7 +2027,7 @@ char[] demangle( const(char)[] buf, char[] dst = null ) * The demangled type name or the original string if the name is not a * mangled D type. */ -char[] demangleType( const(char)[] buf, char[] dst = null ) +char[] demangleType( const(char)[] buf, char[] dst = null ) nothrow pure @safe { auto d = Demangle!()(buf, dst); return d.demangleType(); @@ -2055,7 +2059,7 @@ char[] reencodeMangled(const(char)[] mangled) nothrow pure @safe } Replacement [] replacements; - pure: + pure @safe: size_t positionInResult(size_t pos) { foreach_reverse (r; replacements) @@ -2186,7 +2190,7 @@ char[] reencodeMangled(const(char)[] mangled) nothrow pure @safe d.mute = true; // no demangled output try { - () @trusted { d.parseMangledName(); }(); + d.parseMangledName(); if (d.hooks.lastpos < d.pos) d.hooks.result ~= d.buf[d.hooks.lastpos .. d.pos]; return d.hooks.result; @@ -2267,14 +2271,14 @@ char[] mangle(T)(const(char)[] fqn, char[] dst = null) @safe pure nothrow /// -unittest +@safe pure nothrow unittest { assert(mangle!int("a.b") == "_D1a1bi"); assert(mangle!(char[])("test.foo") == "_D4test3fooAa"); assert(mangle!(int function(int))("a.b") == "_D1a1bPFiZi"); } -unittest +@safe pure nothrow unittest { static assert(mangle!int("a.b") == "_D1a1bi"); @@ -2325,7 +2329,7 @@ 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 +@safe pure nothrow unittest { assert(mangleFunc!(int function(int))("a.b") == "_D1a1bFiZi"); static if(hasTypeBackRef) @@ -2344,7 +2348,7 @@ unittest assert(reencodeMangled("_D3std4conv4conv7__T3std4convi") == "_D3std4convQf7__T3stdQpi"); } -unittest +@safe pure nothrow unittest { int function(lazy int[], ...) fp; assert(mangle!(typeof(fp))("demangle.test") == "_D8demangle4testPFLAiYi"); @@ -2368,7 +2372,7 @@ private template hasPlainMangling(FT) if (is(FT == function)) enum hasPlainMangling = c == 'U' || c == 'V' || c == 'W'; } -unittest +@safe pure nothrow unittest { static extern(D) void fooD(); static extern(C) void fooC(); @@ -2533,7 +2537,7 @@ version(unittest) alias staticIota = Seq!(staticIota!(x - 1), x - 1); } } -unittest +@safe pure nothrow unittest { foreach( i, name; table ) { @@ -2553,7 +2557,7 @@ unittest /* * */ -string decodeDmdString( const(char)[] ln, ref size_t p ) +string decodeDmdString( const(char)[] ln, ref size_t p ) nothrow pure @safe { string s; uint zlen, zpos; @@ -2598,19 +2602,17 @@ string decodeDmdString( const(char)[] ln, ref size_t p ) // locally purified for internal use here only extern (C) private { - pure @trusted @nogc nothrow pragma(mangle, "fakePureReprintReal") real pureReprintReal(char* nptr, size_t len); + pure @trusted @nogc nothrow pragma(mangle, "fakePureReprintReal") void pureReprintReal(char[] nptr); - char* fakePureReprintReal(char* nptr, size_t len) + void fakePureReprintReal(char[] nptr) { import core.stdc.stdlib : strtold; import core.stdc.stdio : snprintf; import core.stdc.errno : errno; const err = errno; - real val = strtold(nptr, null); - len = snprintf(nptr, len, "%#Lg", val); + real val = strtold(nptr.ptr, null); + snprintf(nptr.ptr, nptr.length, "%#Lg", val); errno = err; - - return nptr; } } From 81a06e731780a420e76a5b037fb34aa9e625eaf6 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze <r.sagitario@gmx.de> Date: Sun, 16 Jul 2017 08:31:40 +0200 Subject: [PATCH 13/13] disable some inlining to tame dmd inliner, it causes huge compilation times when optimizing --- src/core/demangle.d | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/core/demangle.d b/src/core/demangle.d index d205b7d541..e80f9197bd 100644 --- a/src/core/demangle.d +++ b/src/core/demangle.d @@ -100,6 +100,8 @@ pure @safe: static void error( string msg = "Invalid symbol" ) @trusted /* exception only used in module */ { + pragma(inline, false); // tame dmd inliner + //throw new ParseException( msg ); debug(info) printf( "error: %.*s\n", cast(int) msg.length, msg.ptr ); throw __ctfe ? new ParseException(msg) @@ -110,6 +112,8 @@ pure @safe: static void overflow( string msg = "Buffer overflow" ) @trusted /* exception only used in module */ { + pragma(inline, false); // tame dmd inliner + //throw new OverflowException( msg ); debug(info) printf( "overflow: %.*s\n", cast(int) msg.length, msg.ptr ); throw cast(OverflowException) cast(void*) typeid(OverflowException).initializer; @@ -176,6 +180,8 @@ pure @safe: // move val to the end of the dst buffer char[] shift( const(char)[] val ) { + pragma(inline, false); // tame dmd inliner + if( val.length && !mute ) { assert( contains( dst[0 .. len], val ) ); @@ -196,6 +202,8 @@ pure @safe: // remove val from dst buffer void remove( const(char)[] val ) { + pragma(inline, false); // tame dmd inliner + if( val.length ) { assert( contains( dst[0 .. len], val ) ); @@ -210,6 +218,8 @@ pure @safe: char[] append( const(char)[] val ) { + pragma(inline, false); // tame dmd inliner + if( val.length && !mute ) { if( !dst.length ) @@ -252,6 +262,8 @@ pure @safe: char[] put( const(char)[] val ) { + pragma(inline, false); // tame dmd inliner + if( val.length ) { if( !contains( dst[0 .. len], val ) )