| /** | |
| * The demangle module converts mangled D symbols to a representation similar | |
| * to what would have existed in code. | |
| * | |
| * Copyright: Copyright Sean Kelly 2010 - 2014. | |
| * License: Distributed under the | |
| * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). | |
| * (See accompanying file LICENSE) | |
| * Authors: Sean Kelly | |
| * Source: $(DRUNTIMESRC core/_demangle.d) | |
| */ | |
| module core.demangle; | |
| version (OSX) | |
| version = Darwin; | |
| else version (iOS) | |
| version = Darwin; | |
| else version (TVOS) | |
| version = Darwin; | |
| else version (WatchOS) | |
| version = Darwin; | |
| debug (trace) debug = needPrintf; | |
| debug (info) debug = needPrintf; | |
| debug (needPrintf) | |
| private int printf(Args...)(scope const char* fmt, scope const Args args) | |
| => __ctfe ? 0 : imported!"core.stdc.stdio".printf(fmt, args); | |
| extern (C) alias CXX_DEMANGLER = char* function (const char* mangled_name, | |
| char* output_buffer, | |
| size_t* length, | |
| int* status) nothrow pure @trusted; | |
| 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 | |
| // the .mangleof property are effectively incomplete as far as the | |
| // ABI is concerned and so are not considered to be mangled symbol | |
| // names. | |
| // NOTE: This implementation builds the demangled buffer in place by | |
| // writing data as it is decoded and then rearranging it later as | |
| // needed. In practice this results in very little data movement, | |
| // and the performance cost is more than offset by the gain from | |
| // not allocating dynamic memory to assemble the name piecemeal. | |
| // | |
| // If the destination buffer is too small, parsing will restart | |
| // with a larger buffer. Since this generally means only one | |
| // allocation during the course of a parsing run, this is still | |
| // faster than assembling the result piecemeal. | |
| pure @safe: | |
| enum AddType { no, yes } | |
| this( return scope const(char)[] buf_, return scope char[] dst_ = null ) | |
| { | |
| this( buf_, AddType.yes, dst_ ); | |
| } | |
| this( return scope const(char)[] buf_, AddType addType_, return scope char[] dst_ = null ) | |
| { | |
| buf = buf_; | |
| addType = addType_; | |
| dst.dst = dst_; | |
| } | |
| const(char)[] buf = null; | |
| Buffer dst; | |
| size_t pos = 0; | |
| size_t brp = 0; // current back reference pos | |
| AddType addType = AddType.yes; | |
| bool mute = false; | |
| Hooks hooks; | |
| ////////////////////////////////////////////////////////////////////////// | |
| // Type Testing and Conversion | |
| ////////////////////////////////////////////////////////////////////////// | |
| static bool isAlpha( char val ) | |
| { | |
| return ('a' <= val && 'z' >= val) || | |
| ('A' <= val && 'Z' >= val) || | |
| (0x80 & val); // treat all unicode as alphabetic | |
| } | |
| static bool isDigit( char val ) nothrow | |
| { | |
| return '0' <= val && '9' >= val; | |
| } | |
| static bool isHexDigit( char val ) | |
| { | |
| return ('0' <= val && '9' >= val) || | |
| ('a' <= val && 'f' >= val) || | |
| ('A' <= val && 'F' >= val); | |
| } | |
| static ubyte ascii2hex( out bool errStatus, char val ) nothrow | |
| { | |
| if (val >= 'a' && val <= 'f') | |
| return cast(ubyte)(val - 'a' + 10); | |
| if (val >= 'A' && val <= 'F') | |
| return cast(ubyte)(val - 'A' + 10); | |
| if (val >= '0' && val <= '9') | |
| return cast(ubyte)(val - '0'); | |
| errStatus = true; | |
| return 0; | |
| } | |
| BufSlice shift(scope const BufSlice val) return scope | |
| { | |
| if (mute) | |
| return dst.bslice_empty; | |
| return dst.shift(val); | |
| } | |
| void putComma(size_t n) | |
| { | |
| version (DigitalMars) pragma(inline, false); | |
| if (n) | |
| put(", "); | |
| } | |
| void put(char c) return scope | |
| { | |
| char[1] val = c; | |
| put(val[]); | |
| } | |
| void put(scope BufSlice val) return scope | |
| { | |
| put(val.getSlice); | |
| } | |
| void put(scope const(char)[] val) return scope nothrow | |
| { | |
| if (mute) | |
| return; | |
| dst.append(val); | |
| } | |
| void putAsHex( size_t val, int width = 0 ) | |
| { | |
| import core.internal.string; | |
| UnsignedStringBuf buf = void; | |
| auto s = unsignedToTempString!16(val, buf); | |
| int slen = cast(int)s.length; | |
| if (slen < width) | |
| { | |
| foreach (i; slen .. width) | |
| put('0'); | |
| } | |
| put(s); | |
| } | |
| void pad( const(char)[] val ) | |
| { | |
| if ( val.length ) | |
| { | |
| put(" "); | |
| put( val ); | |
| } | |
| } | |
| void silent( out bool err_status, void delegate(out bool err_status) pure @safe nothrow dg ) nothrow | |
| { | |
| debug(trace) printf( "silent+\n" ); | |
| debug(trace) scope(success) printf( "silent-\n" ); | |
| auto n = dst.length; | |
| dg(err_status); | |
| if(!err_status) | |
| dst.len = n; | |
| } | |
| ////////////////////////////////////////////////////////////////////////// | |
| // Parsing Utility | |
| ////////////////////////////////////////////////////////////////////////// | |
| @property bool empty() | |
| { | |
| return pos >= buf.length; | |
| } | |
| @property char front() | |
| { | |
| if ( pos < buf.length ) | |
| return buf[pos]; | |
| return char.init; | |
| } | |
| char peek( size_t n ) | |
| { | |
| if ( pos + n < buf.length ) | |
| return buf[pos + n]; | |
| return char.init; | |
| } | |
| bool test( char val ) nothrow | |
| { | |
| return val == front; | |
| } | |
| void popFront() nothrow | |
| { | |
| if ( pos++ >= buf.length ) | |
| assert(false); | |
| } | |
| void popFront(int i) nothrow | |
| { | |
| while (i--) | |
| popFront(); | |
| } | |
| bool match( char val ) nothrow | |
| { | |
| if (!test(val)) | |
| return false; | |
| else | |
| { | |
| popFront(); | |
| return true; | |
| } | |
| } | |
| bool match( const(char)[] val ) nothrow | |
| { | |
| foreach (char e; val ) | |
| if (!match( e )) | |
| return false; | |
| return true; | |
| } | |
| void eat( char val ) | |
| { | |
| if ( val == front ) | |
| popFront(); | |
| } | |
| bool isSymbolNameFront(out bool errStatus) nothrow | |
| { | |
| char val = front; | |
| if ( isDigit( val ) || val == '_' ) | |
| return true; | |
| if ( val != 'Q' ) | |
| return false; | |
| // check the back reference encoding after 'Q' | |
| val = peekBackref(); | |
| if (val == 0) | |
| { | |
| // invalid back reference | |
| errStatus = true; | |
| return false; | |
| } | |
| return isDigit( val ); // identifier ref | |
| } | |
| // return the first character at the back reference | |
| char peekBackref() nothrow | |
| { | |
| assert( front == 'Q' ); | |
| auto n = decodeBackref!1(); | |
| if (!n || n > pos) | |
| return 0; // invalid back reference | |
| return buf[pos - n]; | |
| } | |
| size_t decodeBackref(size_t peekAt = 0)() nothrow | |
| { | |
| 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') | |
| return 0; // invalid back reference | |
| n = base * n + t - 'a'; | |
| return n; | |
| } | |
| n = base * n + t - 'A'; | |
| } | |
| } | |
| ////////////////////////////////////////////////////////////////////////// | |
| // Parsing Implementation | |
| ////////////////////////////////////////////////////////////////////////// | |
| /* | |
| Number: | |
| Digit | |
| Digit Number | |
| */ | |
| const(char)[] sliceNumber() return scope | |
| { | |
| debug(trace) printf( "sliceNumber+\n" ); | |
| debug(trace) scope(success) printf( "sliceNumber-\n" ); | |
| auto beg = pos; | |
| while ( true ) | |
| { | |
| auto t = front; | |
| if (t >= '0' && t <= '9') | |
| popFront(); | |
| else | |
| return buf[beg .. pos]; | |
| } | |
| } | |
| size_t decodeNumber(out bool errStatus) scope nothrow | |
| { | |
| debug(trace) printf( "decodeNumber+\n" ); | |
| debug(trace) scope(success) printf( "decodeNumber-\n" ); | |
| return decodeNumber( errStatus, sliceNumber() ); | |
| } | |
| size_t decodeNumber( out bool errStatus, scope const(char)[] num ) scope nothrow | |
| { | |
| debug(trace) printf( "decodeNumber+\n" ); | |
| debug(trace) scope(success) printf( "decodeNumber-\n" ); | |
| size_t val = 0; | |
| foreach ( c; num ) | |
| { | |
| import core.checkedint : mulu, addu; | |
| bool overflow = false; | |
| val = mulu(val, 10, overflow); | |
| val = addu(val, c - '0', overflow); | |
| if (overflow) | |
| { | |
| errStatus = true; | |
| return 0; | |
| } | |
| } | |
| return val; | |
| } | |
| void parseReal(out bool errStatus) scope nothrow | |
| { | |
| debug(trace) printf( "parseReal+\n" ); | |
| debug(trace) scope(success) printf( "parseReal-\n" ); | |
| char[64] tbuf = void; | |
| size_t tlen = 0; | |
| real val = void; | |
| void onError() | |
| { | |
| errStatus = true; | |
| } | |
| if ( 'I' == front ) | |
| { | |
| if (!match("INF")) | |
| return onError(); | |
| put( "real.infinity" ); | |
| return; | |
| } | |
| if ( 'N' == front ) | |
| { | |
| popFront(); | |
| if ( 'I' == front ) | |
| { | |
| if (!match("INF")) | |
| return onError(); | |
| put( "-real.infinity" ); | |
| return; | |
| } | |
| if ( 'A' == front ) | |
| { | |
| if (!match("AN")) | |
| return onError(); | |
| put( "real.nan" ); | |
| return; | |
| } | |
| tbuf[tlen++] = '-'; | |
| } | |
| tbuf[tlen++] = '0'; | |
| tbuf[tlen++] = 'X'; | |
| errStatus = !isHexDigit( front ); | |
| if (errStatus) | |
| return; // Expected hex digit | |
| tbuf[tlen++] = front; | |
| tbuf[tlen++] = '.'; | |
| popFront(); | |
| while ( isHexDigit( front ) ) | |
| { | |
| if (tlen >= tbuf.length) | |
| return onError(); // Too many hex float digits | |
| tbuf[tlen++] = front; | |
| popFront(); | |
| } | |
| if (!match('P')) | |
| return onError(); | |
| tbuf[tlen++] = 'p'; | |
| if ( 'N' == front ) | |
| { | |
| tbuf[tlen++] = '-'; | |
| popFront(); | |
| } | |
| else | |
| { | |
| tbuf[tlen++] = '+'; | |
| } | |
| while ( isDigit( front ) ) | |
| { | |
| tbuf[tlen++] = front; | |
| popFront(); | |
| } | |
| tbuf[tlen] = 0; | |
| debug(info) printf( "got (%s)\n", tbuf.ptr ); | |
| pureReprintReal( tbuf[] ); | |
| debug(info) printf( "converted (%.*s)\n", cast(int) tlen, tbuf.ptr ); | |
| put( tbuf[0 .. tlen] ); | |
| } | |
| /* | |
| LName: | |
| Number Name | |
| Name: | |
| Namestart | |
| Namestart Namechars | |
| Namestart: | |
| _ | |
| Alpha | |
| Namechar: | |
| Namestart | |
| Digit | |
| Namechars: | |
| Namechar | |
| Namechar Namechars | |
| */ | |
| void parseLName(out string errMsg) scope nothrow | |
| { | |
| debug(trace) printf( "parseLName+\n" ); | |
| debug(trace) scope(success) printf( "parseLName-\n" ); | |
| static if (__traits(hasMember, Hooks, "parseLName")) | |
| { | |
| auto r = hooks.parseLName(errMsg, this); | |
| if (errMsg !is null) | |
| return; | |
| if (r) return; | |
| } | |
| void error(string msg) | |
| { | |
| errMsg = msg; | |
| } | |
| if ( front == 'Q' ) | |
| { | |
| // back reference to LName | |
| auto refPos = pos; | |
| popFront(); | |
| size_t n = decodeBackref(); | |
| if (!n || n > refPos) | |
| return error("Invalid LName back reference"); | |
| if ( !mute ) | |
| { | |
| auto savePos = pos; | |
| scope(exit) pos = savePos; | |
| pos = refPos - n; | |
| parseLName(errMsg); | |
| } | |
| return; | |
| } | |
| bool err_flag; | |
| auto n = decodeNumber(err_flag); | |
| if (err_flag) | |
| return error("Number overflow"); | |
| if ( n == 0 ) | |
| { | |
| put( "__anonymous" ); | |
| return; | |
| } | |
| if ( n > buf.length || n > buf.length - pos ) | |
| return error("LName must be at least 1 character"); | |
| if ( '_' != front && !isAlpha( front ) ) | |
| return error("Invalid character in LName"); | |
| foreach (char e; buf[pos + 1 .. pos + n] ) | |
| { | |
| if ( '_' != e && !isAlpha( e ) && !isDigit( e ) ) | |
| return error("Invalid character in LName"); | |
| } | |
| put( buf[pos .. pos + n] ); | |
| pos += n; | |
| } | |
| /* | |
| Type: | |
| Shared | |
| Const | |
| Immutable | |
| Wild | |
| TypeArray | |
| TypeVector | |
| TypeStaticArray | |
| TypeAssocArray | |
| TypePointer | |
| TypeFunction | |
| TypeIdent | |
| TypeClass | |
| TypeStruct | |
| TypeEnum | |
| TypeTypedef | |
| TypeDelegate | |
| TypeNone | |
| TypeVoid | |
| TypeNoreturn | |
| TypeByte | |
| TypeUbyte | |
| TypeShort | |
| TypeUshort | |
| TypeInt | |
| TypeUint | |
| TypeLong | |
| TypeUlong | |
| TypeCent | |
| TypeUcent | |
| TypeFloat | |
| TypeDouble | |
| TypeReal | |
| TypeIfloat | |
| TypeIdouble | |
| TypeIreal | |
| TypeCfloat | |
| TypeCdouble | |
| TypeCreal | |
| TypeBool | |
| TypeChar | |
| TypeWchar | |
| TypeDchar | |
| TypeTuple | |
| Shared: | |
| O Type | |
| Const: | |
| x Type | |
| Immutable: | |
| y Type | |
| Wild: | |
| Ng Type | |
| TypeArray: | |
| A Type | |
| TypeVector: | |
| Nh Type | |
| TypeStaticArray: | |
| G Number Type | |
| TypeAssocArray: | |
| H Type Type | |
| TypePointer: | |
| P Type | |
| TypeFunction: | |
| CallConvention FuncAttrs Arguments ArgClose Type | |
| TypeIdent: | |
| I LName | |
| TypeClass: | |
| C LName | |
| TypeStruct: | |
| S LName | |
| TypeEnum: | |
| E LName | |
| TypeTypedef: | |
| T LName | |
| TypeDelegate: | |
| D TypeFunction | |
| TypeNone: | |
| n | |
| TypeVoid: | |
| v | |
| TypeNoreturn | |
| Nn | |
| TypeByte: | |
| g | |
| TypeUbyte: | |
| h | |
| TypeShort: | |
| s | |
| TypeUshort: | |
| t | |
| TypeInt: | |
| i | |
| TypeUint: | |
| k | |
| TypeLong: | |
| l | |
| TypeUlong: | |
| m | |
| TypeCent | |
| zi | |
| TypeUcent | |
| zk | |
| TypeFloat: | |
| f | |
| TypeDouble: | |
| d | |
| TypeReal: | |
| e | |
| TypeIfloat: | |
| o | |
| TypeIdouble: | |
| p | |
| TypeIreal: | |
| j | |
| TypeCfloat: | |
| q | |
| TypeCdouble: | |
| r | |
| TypeCreal: | |
| c | |
| TypeBool: | |
| b | |
| TypeChar: | |
| a | |
| TypeWchar: | |
| u | |
| TypeDchar: | |
| w | |
| TypeTuple: | |
| B Number Arguments | |
| */ | |
| BufSlice parseType(out bool errStatus) return scope nothrow | |
| { | |
| static immutable string[23] primitives = [ | |
| "char", // a | |
| "bool", // b | |
| "creal", // c | |
| "double", // d | |
| "real", // e | |
| "float", // f | |
| "byte", // g | |
| "ubyte", // h | |
| "int", // i | |
| "ireal", // j | |
| "uint", // k | |
| "long", // l | |
| "ulong", // m | |
| null, // n | |
| "ifloat", // o | |
| "idouble", // p | |
| "cfloat", // q | |
| "cdouble", // r | |
| "short", // s | |
| "ushort", // t | |
| "wchar", // u | |
| "void", // v | |
| "dchar", // w | |
| ]; | |
| static if (__traits(hasMember, Hooks, "parseType")) | |
| { | |
| auto n = hooks.parseType(errStatus, this, null); | |
| if (errStatus) | |
| return dst.bslice_empty; | |
| else | |
| if (n !is null) | |
| return BufSlice(n, 0, n.length); | |
| } | |
| debug(trace) printf( "parseType+\n" ); | |
| debug(trace) scope(success) printf( "parseType-\n" ); | |
| auto beg = dst.length; | |
| auto t = front; | |
| BufSlice parseBackrefType(out string errStatus, scope BufSlice delegate(bool err_flag) pure @safe nothrow parseDg) pure @safe nothrow | |
| { | |
| if (pos == brp) | |
| { | |
| errStatus = "recursive back reference"; | |
| return dst.bslice_empty; | |
| } | |
| auto refPos = pos; | |
| popFront(); | |
| auto n = decodeBackref(); | |
| if (n == 0 || n > pos) | |
| { | |
| errStatus = "invalid back reference"; | |
| return dst.bslice_empty; | |
| } | |
| if ( mute ) | |
| return dst.bslice_empty; | |
| auto savePos = pos; | |
| auto saveBrp = brp; | |
| scope(success) { pos = savePos; brp = saveBrp; } | |
| pos = refPos - n; | |
| brp = refPos; | |
| bool err_flag; | |
| auto ret = parseDg(err_flag); | |
| if (err_flag) | |
| { | |
| errStatus = "parseDg error"; | |
| return dst.bslice_empty; | |
| } | |
| return ret; | |
| } | |
| // call parseType() and return error if occured | |
| enum parseTypeOrF = "parseType(errStatus); if (errStatus) return dst.bslice_empty;"; | |
| switch ( t ) | |
| { | |
| case 'Q': // Type back reference | |
| string errMsg; | |
| auto r = parseBackrefType(errMsg, (e_flag) => parseType(e_flag)); | |
| if (errMsg !is null) | |
| return dst.bslice_empty; | |
| return r; | |
| case 'O': // Shared (O Type) | |
| popFront(); | |
| put( "shared(" ); | |
| mixin(parseTypeOrF); | |
| put( ')' ); | |
| return dst[beg .. $]; | |
| case 'x': // Const (x Type) | |
| popFront(); | |
| put( "const(" ); | |
| mixin(parseTypeOrF); | |
| put( ')' ); | |
| return dst[beg .. $]; | |
| case 'y': // Immutable (y Type) | |
| popFront(); | |
| put( "immutable(" ); | |
| mixin(parseTypeOrF); | |
| put( ')' ); | |
| return dst[beg .. $]; | |
| case 'N': | |
| popFront(); | |
| switch ( front ) | |
| { | |
| case 'n': // Noreturn | |
| popFront(); | |
| put("noreturn"); | |
| return dst[beg .. $]; | |
| case 'g': // Wild (Ng Type) | |
| popFront(); | |
| // TODO: Anything needed here? | |
| put( "inout(" ); | |
| mixin(parseTypeOrF); | |
| put( ')' ); | |
| return dst[beg .. $]; | |
| case 'h': // TypeVector (Nh Type) | |
| popFront(); | |
| put( "__vector(" ); | |
| mixin(parseTypeOrF); | |
| put( ')' ); | |
| return dst[beg .. $]; | |
| default: | |
| errStatus = true; | |
| return dst.bslice_empty; | |
| } | |
| case 'A': // TypeArray (A Type) | |
| popFront(); | |
| mixin(parseTypeOrF); | |
| put( "[]" ); | |
| return dst[beg .. $]; | |
| case 'G': // TypeStaticArray (G Number Type) | |
| popFront(); | |
| auto num = sliceNumber(); | |
| mixin(parseTypeOrF); | |
| put( '[' ); | |
| put( num ); | |
| put( ']' ); | |
| return dst[beg .. $]; | |
| case 'H': // TypeAssocArray (H Type Type) | |
| popFront(); | |
| // skip t1 | |
| auto tx = parseType(errStatus); | |
| if (errStatus) | |
| return dst.bslice_empty; | |
| mixin(parseTypeOrF); | |
| put( '[' ); | |
| shift(tx); | |
| put( ']' ); | |
| return dst[beg .. $]; | |
| case 'P': // TypePointer (P Type) | |
| popFront(); | |
| mixin(parseTypeOrF); | |
| put( '*' ); | |
| return dst[beg .. $]; | |
| case 'F': case 'U': case 'W': case 'V': case 'R': // TypeFunction | |
| auto r = parseTypeFunction(errStatus); | |
| if (errStatus) | |
| return dst.bslice_empty; | |
| return r; | |
| case 'C': // TypeClass (C LName) | |
| case 'S': // TypeStruct (S LName) | |
| case 'E': // TypeEnum (E LName) | |
| case 'T': // TypeTypedef (T LName) | |
| popFront(); | |
| parseQualifiedName(errStatus); | |
| if (errStatus) | |
| return dst.bslice_empty; | |
| return dst[beg .. $]; | |
| case 'D': // TypeDelegate (D TypeFunction) | |
| popFront(); | |
| auto modifiers = parseModifier(); | |
| if ( front == 'Q' ) | |
| { | |
| string errMsg; | |
| auto r = parseBackrefType(errMsg, (e_flag) => parseTypeFunction(e_flag, IsDelegate.yes)); | |
| if (errMsg !is null) | |
| return dst.bslice_empty; | |
| return r; | |
| } | |
| else | |
| { | |
| parseTypeFunction(errStatus, IsDelegate.yes); | |
| if (errStatus) | |
| return dst.bslice_empty; | |
| } | |
| if (modifiers) | |
| { | |
| // write modifiers behind the function arguments | |
| while (auto str = typeCtors.toStringConsume(modifiers)) | |
| { | |
| put(' '); | |
| put(str); | |
| } | |
| } | |
| return dst[beg .. $]; | |
| case 'n': // TypeNone (n) | |
| popFront(); | |
| // TODO: Anything needed here? | |
| return dst[beg .. $]; | |
| case 'B': // TypeTuple (B Number Arguments) | |
| popFront(); | |
| // TODO: Handle this. | |
| return dst[beg .. $]; | |
| case 'Z': // Internal symbol | |
| // This 'type' is used for untyped internal symbols, i.e.: | |
| // __array | |
| // __init | |
| // __vtbl | |
| // __Class | |
| // __Interface | |
| // __ModuleInfo | |
| popFront(); | |
| return dst[beg .. $]; | |
| default: | |
| if (t >= 'a' && t <= 'w') | |
| { | |
| popFront(); | |
| put( primitives[cast(size_t)(t - 'a')] ); | |
| return dst[beg .. $]; | |
| } | |
| else if (t == 'z') | |
| { | |
| popFront(); | |
| switch ( front ) | |
| { | |
| case 'i': | |
| popFront(); | |
| put( "cent" ); | |
| return dst[beg .. $]; | |
| case 'k': | |
| popFront(); | |
| put( "ucent" ); | |
| return dst[beg .. $]; | |
| default: | |
| errStatus = true; | |
| return dst.bslice_empty; | |
| } | |
| } | |
| errStatus = true; | |
| return dst.bslice_empty; | |
| } | |
| } | |
| /* | |
| TypeFunction: | |
| CallConvention FuncAttrs Arguments ArgClose Type | |
| CallConvention: | |
| F // D | |
| U // C | |
| W // Windows | |
| R // C++ | |
| FuncAttrs: | |
| FuncAttr | |
| FuncAttr FuncAttrs | |
| FuncAttr: | |
| empty | |
| FuncAttrPure | |
| FuncAttrNothrow | |
| FuncAttrProperty | |
| FuncAttrRef | |
| FuncAttrReturn | |
| FuncAttrScope | |
| FuncAttrTrusted | |
| FuncAttrSafe | |
| FuncAttrPure: | |
| Na | |
| FuncAttrNothrow: | |
| Nb | |
| FuncAttrRef: | |
| Nc | |
| FuncAttrProperty: | |
| Nd | |
| FuncAttrTrusted: | |
| Ne | |
| FuncAttrSafe: | |
| Nf | |
| FuncAttrNogc: | |
| Ni | |
| FuncAttrReturn: | |
| Nj | |
| FuncAttrScope: | |
| Nl | |
| Arguments: | |
| Argument | |
| Argument Arguments | |
| Argument: | |
| Argument2 | |
| M Argument2 // scope | |
| Argument2: | |
| Type | |
| J Type // out | |
| K Type // ref | |
| L Type // lazy | |
| ArgClose | |
| X // variadic T t,...) style | |
| Y // variadic T t...) style | |
| Z // not variadic | |
| */ | |
| void parseCallConvention(out bool errStatus) nothrow | |
| { | |
| // CallConvention | |
| switch ( front ) | |
| { | |
| case 'F': // D | |
| popFront(); | |
| break; | |
| case 'U': // C | |
| popFront(); | |
| put( "extern (C) " ); | |
| break; | |
| case 'W': // Windows | |
| popFront(); | |
| put( "extern (Windows) " ); | |
| break; | |
| case 'R': // C++ | |
| popFront(); | |
| put( "extern (C++) " ); | |
| break; | |
| default: | |
| errStatus = true; | |
| } | |
| } | |
| /// Returns: Flags of `TypeCtor` | |
| ushort parseModifier() | |
| { | |
| TypeCtor res = TypeCtor.None; | |
| switch ( front ) | |
| { | |
| case 'y': | |
| popFront(); | |
| return TypeCtor.Immutable; | |
| case 'O': | |
| popFront(); | |
| res |= TypeCtor.Shared; | |
| if (front == 'x') | |
| goto case 'x'; | |
| if (front == 'N') | |
| goto case 'N'; | |
| return TypeCtor.Shared; | |
| case 'N': | |
| if (peek( 1 ) != 'g') | |
| return res; | |
| popFront(); | |
| popFront(); | |
| res |= TypeCtor.InOut; | |
| if ( front == 'x' ) | |
| goto case 'x'; | |
| return res; | |
| case 'x': | |
| popFront(); | |
| res |= TypeCtor.Const; | |
| return res; | |
| default: return TypeCtor.None; | |
| } | |
| } | |
| ushort parseFuncAttr(out bool errStatus) nothrow | |
| { | |
| // FuncAttrs | |
| ushort result; | |
| while ('N' == front) | |
| { | |
| popFront(); | |
| switch ( front ) | |
| { | |
| case 'a': // FuncAttrPure | |
| popFront(); | |
| result |= FuncAttributes.Pure; | |
| continue; | |
| case 'b': // FuncAttrNoThrow | |
| popFront(); | |
| result |= FuncAttributes.Nothrow; | |
| continue; | |
| case 'c': // FuncAttrRef | |
| popFront(); | |
| result |= FuncAttributes.Ref; | |
| continue; | |
| case 'd': // FuncAttrProperty | |
| popFront(); | |
| result |= FuncAttributes.Property; | |
| continue; | |
| case 'e': // FuncAttrTrusted | |
| popFront(); | |
| result |= FuncAttributes.Trusted; | |
| continue; | |
| case 'f': // FuncAttrSafe | |
| popFront(); | |
| result |= FuncAttributes.Safe; | |
| continue; | |
| case 'g': | |
| case 'h': | |
| case 'k': | |
| case 'n': | |
| // NOTE: The inout parameter type is represented as "Ng". | |
| // The vector parameter type is represented as "Nh". | |
| // The return parameter type is represented as "Nk". | |
| // The noreturn parameter type is represented as "Nn". | |
| // These make it look like a FuncAttr, but infact | |
| // if we see these, then we know we're really in | |
| // the parameter list. Rewind and break. | |
| pos--; | |
| return result; | |
| case 'i': // FuncAttrNogc | |
| popFront(); | |
| result |= FuncAttributes.NoGC; | |
| continue; | |
| case 'j': // FuncAttrReturn | |
| popFront(); | |
| if (this.peek(0) == 'N' && this.peek(1) == 'l') | |
| { | |
| result |= FuncAttributes.ReturnScope; | |
| popFront(); | |
| popFront(); | |
| } else { | |
| result |= FuncAttributes.Return; | |
| } | |
| continue; | |
| case 'l': // FuncAttrScope | |
| popFront(); | |
| if (this.peek(0) == 'N' && this.peek(1) == 'j') | |
| { | |
| result |= FuncAttributes.ScopeReturn; | |
| popFront(); | |
| popFront(); | |
| } else { | |
| result |= FuncAttributes.Scope; | |
| } | |
| continue; | |
| case 'm': // FuncAttrLive | |
| popFront(); | |
| result |= FuncAttributes.Live; | |
| continue; | |
| default: | |
| errStatus = true; | |
| return 0; | |
| } | |
| } | |
| return result; | |
| } | |
| void parseFuncArguments(out bool errStatus) scope nothrow | |
| { | |
| // Arguments | |
| for ( size_t n = 0; true; n++ ) | |
| { | |
| debug(info) printf( "tok (%c)\n", front ); | |
| switch ( front ) | |
| { | |
| case 'X': // ArgClose (variadic T t...) style) | |
| popFront(); | |
| put( "..." ); | |
| return; | |
| case 'Y': // ArgClose (variadic T t,...) style) | |
| popFront(); | |
| put( ", ..." ); | |
| return; | |
| case 'Z': // ArgClose (not variadic) | |
| popFront(); | |
| return; | |
| default: | |
| break; | |
| } | |
| putComma(n); | |
| /* Do special return, scope, ref, out combinations | |
| */ | |
| int npops; | |
| if ( 'M' == front && peek(1) == 'N' && peek(2) == 'k') | |
| { | |
| const c3 = peek(3); | |
| if (c3 == 'J') | |
| { | |
| put("scope return out "); // MNkJ | |
| npops = 4; | |
| } | |
| else if (c3 == 'K') | |
| { | |
| put("scope return ref "); // MNkK | |
| npops = 4; | |
| } | |
| } | |
| else if ('N' == front && peek(1) == 'k') | |
| { | |
| const c2 = peek(2); | |
| if (c2 == 'J') | |
| { | |
| put("return out "); // NkJ | |
| npops = 3; | |
| } | |
| else if (c2 == 'K') | |
| { | |
| put("return ref "); // NkK | |
| npops = 3; | |
| } | |
| else if (c2 == 'M') | |
| { | |
| const c3 = peek(3); | |
| if (c3 == 'J') | |
| { | |
| put("return scope out "); // NkMJ | |
| npops = 4; | |
| } | |
| else if (c3 == 'K') | |
| { | |
| put("return scope ref "); // NkMK | |
| npops = 4; | |
| } | |
| else | |
| { | |
| put("return scope "); // NkM | |
| npops = 3; | |
| } | |
| } | |
| } | |
| popFront(npops); | |
| if ( 'M' == front ) | |
| { | |
| popFront(); | |
| put( "scope " ); | |
| } | |
| if ( 'N' == front ) | |
| { | |
| popFront(); | |
| if ( 'k' == front ) // Return (Nk Parameter2) | |
| { | |
| popFront(); | |
| put( "return " ); | |
| } | |
| else | |
| pos--; | |
| } | |
| // call parseType() and return error if occured | |
| enum parseTypeOrF = "parseType(errStatus); if (errStatus) return;"; | |
| switch ( front ) | |
| { | |
| case 'I': // in (I Type) | |
| popFront(); | |
| put("in "); | |
| if (front == 'K') | |
| goto case; | |
| mixin(parseTypeOrF); | |
| continue; | |
| case 'K': // ref (K Type) | |
| popFront(); | |
| put( "ref " ); | |
| mixin(parseTypeOrF); | |
| continue; | |
| case 'J': // out (J Type) | |
| popFront(); | |
| put( "out " ); | |
| mixin(parseTypeOrF); | |
| continue; | |
| case 'L': // lazy (L Type) | |
| popFront(); | |
| put( "lazy " ); | |
| mixin(parseTypeOrF); | |
| continue; | |
| default: | |
| mixin(parseTypeOrF); | |
| } | |
| } | |
| } | |
| enum IsDelegate { no, yes } | |
| /* | |
| TypeFunction: | |
| CallConvention FuncAttrs Arguments ArgClose Type | |
| */ | |
| BufSlice parseTypeFunction(out bool errStatus, IsDelegate isdg = IsDelegate.no) return scope nothrow | |
| { | |
| debug(trace) printf( "parseTypeFunction+\n" ); | |
| debug(trace) scope(success) printf( "parseTypeFunction-\n" ); | |
| auto beg = dst.length; | |
| parseCallConvention(errStatus); | |
| if (errStatus) | |
| return dst.bslice_empty; | |
| auto attributes = parseFuncAttr(errStatus); | |
| if (errStatus) | |
| return dst.bslice_empty; | |
| auto argbeg = dst.length; | |
| put(IsDelegate.yes == isdg ? "delegate" : "function"); | |
| put( '(' ); | |
| parseFuncArguments(errStatus); | |
| if (errStatus) | |
| return dst.bslice_empty; | |
| put( ')' ); | |
| if (attributes) | |
| { | |
| // write function attributes behind arguments | |
| while (auto str = funcAttrs.toStringConsume(attributes)) | |
| { | |
| put(' '); | |
| put(str); | |
| } | |
| } | |
| // A function / delegate return type is located at the end of its mangling | |
| // Write it in order, then shift it back to 'code order' | |
| // e.g. `delegate(int) @safedouble ' => 'double delegate(int) @safe' | |
| { | |
| auto retbeg = dst.length; | |
| parseType(errStatus); | |
| if (errStatus) | |
| return dst.bslice_empty; | |
| put(' '); | |
| shift(dst[argbeg .. retbeg]); | |
| } | |
| return dst[beg .. $]; | |
| } | |
| static bool isCallConvention( char ch ) | |
| { | |
| switch ( ch ) | |
| { | |
| case 'F', 'U', 'V', 'W', 'R': | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| /* | |
| Value: | |
| n | |
| Number | |
| i Number | |
| N Number | |
| e HexFloat | |
| c HexFloat c HexFloat | |
| A Number Value... | |
| HexFloat: | |
| NAN | |
| INF | |
| NINF | |
| N HexDigits P Exponent | |
| HexDigits P Exponent | |
| Exponent: | |
| N Number | |
| Number | |
| HexDigits: | |
| HexDigit | |
| HexDigit HexDigits | |
| HexDigit: | |
| Digit | |
| A | |
| B | |
| C | |
| D | |
| E | |
| F | |
| */ | |
| void parseValue(out bool errStatus) scope nothrow | |
| { | |
| parseValue(errStatus, dst.bslice_empty); | |
| } | |
| void parseValue(out bool errStatus, scope BufSlice name, char type = '\0' ) scope nothrow | |
| { | |
| debug(trace) printf( "parseValue+\n" ); | |
| debug(trace) scope(success) printf( "parseValue-\n" ); | |
| void onError() | |
| { | |
| errStatus = true; | |
| } | |
| // printf( "*** %c\n", front ); | |
| switch ( front ) | |
| { | |
| case 'n': | |
| popFront(); | |
| put( "null" ); | |
| return; | |
| case 'i': | |
| popFront(); | |
| if ('0' > front || '9' < front) | |
| return onError(); // Number expected | |
| goto case; | |
| case '0': .. case '9': | |
| parseIntegerValue( errStatus, name, type ); | |
| return; | |
| case 'N': | |
| popFront(); | |
| put( '-' ); | |
| parseIntegerValue( errStatus, name, type ); | |
| return; | |
| case 'e': | |
| popFront(); | |
| parseReal(errStatus); | |
| return; | |
| case 'c': | |
| popFront(); | |
| parseReal(errStatus); | |
| if (errStatus) | |
| return; | |
| put( '+' ); | |
| if (!match('c')) | |
| return onError(); | |
| parseReal(errStatus); | |
| if (errStatus) | |
| return; | |
| put( 'i' ); | |
| return; | |
| case 'a': case 'w': case 'd': | |
| char t = front; | |
| popFront(); | |
| auto n = decodeNumber(errStatus); | |
| if (errStatus) | |
| return; | |
| if (!match('_')) | |
| return onError(); | |
| put( '"' ); | |
| foreach (i; 0..n) | |
| { | |
| auto a = ascii2hex( errStatus, front ); | |
| if (errStatus) | |
| return; | |
| popFront(); | |
| auto b = ascii2hex( errStatus, front ); | |
| if (errStatus) | |
| return; | |
| popFront(); | |
| auto v = cast(char)((a << 4) | b); | |
| if (' ' <= v && v <= '~') // ASCII printable | |
| { | |
| put(v); | |
| } | |
| else | |
| { | |
| put("\\x"); | |
| putAsHex(v, 2); | |
| } | |
| } | |
| put( '"' ); | |
| if ( 'a' != t ) | |
| put(t); | |
| return; | |
| case 'A': | |
| // NOTE: This is kind of a hack. An associative array literal | |
| // [1:2, 3:4] is represented as HiiA2i1i2i3i4, so the type | |
| // is "Hii" and the value is "A2i1i2i3i4". Thus the only | |
| // way to determine that this is an AA value rather than an | |
| // array value is for the caller to supply the type char. | |
| // Hopefully, this will change so that the value is | |
| // "H2i1i2i3i4", rendering this unnecesary. | |
| if ( 'H' == type ) | |
| goto LassocArray; | |
| // A Number Value... | |
| // An array literal. Value is repeated Number times. | |
| popFront(); | |
| put( '[' ); | |
| auto n = decodeNumber(errStatus); | |
| if (errStatus) | |
| return; | |
| foreach ( i; 0 .. n ) | |
| { | |
| putComma(i); | |
| parseValue(errStatus); | |
| if (errStatus) | |
| return; | |
| } | |
| put( ']' ); | |
| return; | |
| case 'H': | |
| LassocArray: | |
| // H Number Value... | |
| // An associative array literal. Value is repeated 2*Number times. | |
| popFront(); | |
| put( '[' ); | |
| auto n = decodeNumber(errStatus); | |
| if (errStatus) | |
| return; | |
| foreach ( i; 0 .. n ) | |
| { | |
| putComma(i); | |
| parseValue(errStatus); | |
| if (errStatus) | |
| return; | |
| put(':'); | |
| parseValue(errStatus); | |
| if (errStatus) | |
| return; | |
| } | |
| put( ']' ); | |
| return; | |
| case 'S': | |
| // S Number Value... | |
| // A struct literal. Value is repeated Number times. | |
| popFront(); | |
| if ( name.length ) | |
| put( name ); | |
| put( '(' ); | |
| auto n = decodeNumber(errStatus); | |
| if (errStatus) | |
| return; | |
| foreach ( i; 0 .. n ) | |
| { | |
| putComma(i); | |
| parseValue(errStatus); | |
| if (errStatus) | |
| return; | |
| } | |
| put( ')' ); | |
| return; | |
| case 'f': | |
| // f MangledName | |
| // A function literal symbol | |
| popFront(); | |
| parseMangledName(errStatus, false, 1); | |
| return; | |
| default: | |
| errStatus = true; | |
| } | |
| } | |
| void parseIntegerValue( out bool errStatus, scope BufSlice name, char type = '\0' ) scope nothrow | |
| { | |
| debug(trace) printf( "parseIntegerValue+\n" ); | |
| debug(trace) scope(success) printf( "parseIntegerValue-\n" ); | |
| switch ( type ) | |
| { | |
| case 'a': // char | |
| case 'u': // wchar | |
| case 'w': // dchar | |
| { | |
| auto val = sliceNumber(); | |
| auto num = decodeNumber( errStatus, val ); | |
| if (errStatus) | |
| return; | |
| switch ( num ) | |
| { | |
| case '\'': | |
| put( "'\\''" ); | |
| return; | |
| // \", \? | |
| case '\\': | |
| put( "'\\\\'" ); | |
| return; | |
| case '\a': | |
| put( "'\\a'" ); | |
| return; | |
| case '\b': | |
| put( "'\\b'" ); | |
| return; | |
| case '\f': | |
| put( "'\\f'" ); | |
| return; | |
| case '\n': | |
| put( "'\\n'" ); | |
| return; | |
| case '\r': | |
| put( "'\\r'" ); | |
| return; | |
| case '\t': | |
| put( "'\\t'" ); | |
| return; | |
| case '\v': | |
| put( "'\\v'" ); | |
| return; | |
| default: | |
| switch ( type ) | |
| { | |
| case 'a': | |
| if ( num >= 0x20 && num < 0x7F ) | |
| { | |
| put( '\'' ); | |
| put( cast(char)num ); | |
| put( '\'' ); | |
| return; | |
| } | |
| put( "\\x" ); | |
| putAsHex( num, 2 ); | |
| return; | |
| case 'u': | |
| put( "'\\u" ); | |
| putAsHex( num, 4 ); | |
| put( '\'' ); | |
| return; | |
| case 'w': | |
| put( "'\\U" ); | |
| putAsHex( num, 8 ); | |
| put( '\'' ); | |
| return; | |
| default: | |
| assert( 0 ); | |
| } | |
| } | |
| } | |
| case 'b': // bool | |
| auto d = decodeNumber(errStatus); | |
| if (errStatus) | |
| return; | |
| put( d ? "true" : "false" ); | |
| return; | |
| case 'h', 't', 'k': // ubyte, ushort, uint | |
| put( sliceNumber() ); | |
| put( 'u' ); | |
| return; | |
| case 'l': // long | |
| put( sliceNumber() ); | |
| put( 'L' ); | |
| return; | |
| case 'm': // ulong | |
| put( sliceNumber() ); | |
| put( "uL" ); | |
| return; | |
| default: | |
| put( sliceNumber() ); | |
| return; | |
| } | |
| } | |
| /* | |
| TemplateArgs: | |
| TemplateArg | |
| TemplateArg TemplateArgs | |
| TemplateArg: | |
| TemplateArgX | |
| H TemplateArgX | |
| TemplateArgX: | |
| T Type | |
| V Type Value | |
| S Number_opt QualifiedName | |
| X ExternallyMangledName | |
| */ | |
| void parseTemplateArgs(out bool errStatus) scope nothrow | |
| { | |
| debug(trace) printf( "parseTemplateArgs+\n" ); | |
| debug(trace) scope(success) printf( "parseTemplateArgs-\n" ); | |
| L_nextArg: | |
| for ( size_t n = 0; true; n++ ) | |
| { | |
| if ( front == 'H' ) | |
| popFront(); | |
| switch ( front ) | |
| { | |
| case 'T': | |
| popFront(); | |
| putComma(n); | |
| parseType(errStatus); | |
| if (errStatus) | |
| return; | |
| continue; | |
| case 'V': | |
| popFront(); | |
| putComma(n); | |
| // NOTE: In the few instances where the type is actually | |
| // desired in the output it should precede the value | |
| // 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(); | |
| if (t == 0) | |
| { | |
| // invalid back reference | |
| errStatus = true; | |
| return; | |
| } | |
| } | |
| BufSlice name = dst.bslice_empty; | |
| silent( errStatus, delegate void(out bool e_flg) nothrow { name = parseType(e_flg); } ); | |
| if (errStatus) | |
| return; | |
| parseValue( errStatus, name, t ); | |
| if (errStatus) | |
| return; | |
| continue; | |
| case 'S': | |
| popFront(); | |
| putComma(n); | |
| if ( mayBeMangledNameArg() ) | |
| { | |
| auto l = dst.length; | |
| auto p = pos; | |
| auto b = brp; | |
| debug(trace) printf( "may be mangled name arg\n" ); | |
| if (parseMangledNameArg()) | |
| continue; | |
| dst.len = l; | |
| pos = p; | |
| brp = b; | |
| 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(errStatus); | |
| if (errStatus) | |
| return; | |
| qlen /= 10; // last digit needed for QualifiedName | |
| pos--; | |
| auto l = dst.length; | |
| auto p = pos; | |
| auto b = brp; | |
| while ( qlen > 0 ) | |
| { | |
| errStatus = false; | |
| parseQualifiedName(errStatus); | |
| if (!errStatus) | |
| { | |
| if ( pos == p + qlen ) | |
| continue L_nextArg; | |
| } | |
| qlen /= 10; // retry with one digit less | |
| pos = --p; | |
| dst.len = l; | |
| brp = b; | |
| } | |
| } | |
| parseQualifiedName(errStatus); | |
| if (errStatus) | |
| return; | |
| continue; | |
| case 'X': | |
| popFront(); | |
| putComma(n); | |
| { | |
| string errMsg; | |
| parseLName(errMsg); | |
| if (errMsg) | |
| return; | |
| } | |
| continue; | |
| default: | |
| return; | |
| } | |
| } | |
| } | |
| bool mayBeMangledNameArg() nothrow | |
| { | |
| debug(trace) printf( "mayBeMangledNameArg+\n" ); | |
| debug(trace) scope(success) printf( "mayBeMangledNameArg-\n" ); | |
| bool errStatus; | |
| auto p = pos; | |
| scope(exit) pos = p; | |
| if ( isDigit( buf[pos] ) ) | |
| { | |
| auto n = decodeNumber(errStatus); | |
| return !errStatus && n >= 4 && | |
| pos < buf.length && '_' == buf[pos++] && | |
| pos < buf.length && 'D' == buf[pos++] && | |
| isDigit( buf[pos] ); | |
| } | |
| else | |
| { | |
| const isSNF = isSymbolNameFront(errStatus); | |
| return !errStatus && | |
| pos < buf.length && '_' == buf[pos++] && | |
| pos < buf.length && 'D' == buf[pos++] && | |
| isSNF; | |
| } | |
| } | |
| bool parseMangledNameArg() nothrow | |
| { | |
| debug(trace) printf( "parseMangledNameArg+\n" ); | |
| debug(trace) scope(success) printf( "parseMangledNameArg-\n" ); | |
| bool errStatus; | |
| size_t n = 0; | |
| if ( isDigit( front ) ) | |
| { | |
| n = decodeNumber(errStatus); | |
| if (errStatus) | |
| return false; | |
| } | |
| parseMangledName(errStatus, false, n ); | |
| return !errStatus; | |
| } | |
| /* | |
| TemplateInstanceName: | |
| Number __T LName TemplateArgs Z | |
| */ | |
| void parseTemplateInstanceName(out bool errStatus, bool hasNumber) scope nothrow | |
| { | |
| debug(trace) printf( "parseTemplateInstanceName+\n" ); | |
| debug(trace) scope(success) printf( "parseTemplateInstanceName-\n" ); | |
| auto sav = pos; | |
| auto saveBrp = brp; | |
| void onError() | |
| { | |
| errStatus = true; | |
| pos = sav; | |
| brp = saveBrp; | |
| } | |
| size_t n = 0; | |
| if (hasNumber) | |
| { | |
| n = decodeNumber(errStatus); | |
| if (errStatus) | |
| return onError(); | |
| } | |
| auto beg = pos; | |
| errStatus = !match( "__T" ); | |
| if (errStatus) | |
| return onError(); | |
| { | |
| string errMsg; | |
| parseLName(errMsg); | |
| if (errMsg !is null) | |
| return onError(); | |
| } | |
| put( "!(" ); | |
| parseTemplateArgs(errStatus); | |
| if (errStatus) | |
| return onError(); | |
| if (!match('Z')) | |
| return onError(); | |
| if ( hasNumber && pos - beg != n ) | |
| { | |
| // Template name length mismatch | |
| return onError(); | |
| } | |
| put( ')' ); | |
| } | |
| bool mayBeTemplateInstanceName() scope nothrow | |
| { | |
| debug(trace) printf( "mayBeTemplateInstanceName+\n" ); | |
| debug(trace) scope(success) printf( "mayBeTemplateInstanceName-\n" ); | |
| auto p = pos; | |
| scope(exit) pos = p; | |
| bool errStatus; | |
| auto n = decodeNumber(errStatus); | |
| if (errStatus) | |
| return false; | |
| return n >= 5 && | |
| pos < buf.length && '_' == buf[pos++] && | |
| pos < buf.length && '_' == buf[pos++] && | |
| pos < buf.length && 'T' == buf[pos++]; | |
| } | |
| /* | |
| SymbolName: | |
| LName | |
| TemplateInstanceName | |
| */ | |
| void parseSymbolName(out bool errStatus) scope nothrow | |
| { | |
| debug(trace) printf( "parseSymbolName+\n" ); | |
| debug(trace) scope(success) printf( "parseSymbolName-\n" ); | |
| // LName -> Number | |
| // TemplateInstanceName -> Number "__T" | |
| switch ( front ) | |
| { | |
| case '_': | |
| // no length encoding for templates for new mangling | |
| parseTemplateInstanceName(errStatus, false); | |
| return; | |
| case '0': .. case '9': | |
| if ( mayBeTemplateInstanceName() ) | |
| { | |
| auto t = dst.length; | |
| debug(trace) printf( "may be template instance name\n" ); | |
| parseTemplateInstanceName(errStatus, true); | |
| if (!errStatus) | |
| return; | |
| else | |
| { | |
| debug(trace) printf( "not a template instance name\n" ); | |
| dst.len = t; | |
| } | |
| } | |
| goto case; | |
| case 'Q': | |
| string errMsg; | |
| parseLName(errMsg); | |
| errStatus = errMsg !is null; | |
| return; | |
| default: | |
| errStatus = true; | |
| } | |
| } | |
| // 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 | |
| BufSlice parseFunctionTypeNoReturn( bool keepAttr = false ) return scope nothrow | |
| { | |
| // try to demangle a function, in case we are pointing to some function local | |
| auto prevpos = pos; | |
| auto prevlen = dst.length; | |
| auto prevbrp = brp; | |
| if ( 'M' == front ) | |
| { | |
| // do not emit "needs this" | |
| popFront(); | |
| auto modifiers = parseModifier(); | |
| while (auto str = typeCtors.toStringConsume(modifiers)) | |
| { | |
| put(str); | |
| put(' '); | |
| } | |
| } | |
| if ( isCallConvention( front ) ) | |
| { | |
| BufSlice attr = dst.bslice_empty; | |
| // we don't want calling convention and attributes in the qualified name | |
| bool errStatus; | |
| parseCallConvention(errStatus); | |
| if (!errStatus) | |
| { | |
| auto attributes = parseFuncAttr(errStatus); | |
| if (!errStatus) | |
| { | |
| if (keepAttr) { | |
| while (auto str = funcAttrs.toStringConsume(attributes)) | |
| { | |
| put(str); | |
| put(' '); | |
| } | |
| attr = dst[prevlen .. $]; | |
| } | |
| put( '(' ); | |
| parseFuncArguments(errStatus); | |
| if (errStatus) | |
| return dst.bslice_empty; | |
| put( ')' ); | |
| return attr; | |
| } | |
| } | |
| // not part of a qualified name, so back up | |
| pos = prevpos; | |
| dst.len = prevlen; | |
| brp = prevbrp; | |
| } | |
| return dst.bslice_empty; | |
| } | |
| /* | |
| QualifiedName: | |
| SymbolName | |
| SymbolName QualifiedName | |
| */ | |
| void parseQualifiedName(out bool errStatus) return scope nothrow | |
| { | |
| debug(trace) printf( "parseQualifiedName+\n" ); | |
| debug(trace) scope(success) printf( "parseQualifiedName-\n" ); | |
| size_t n = 0; | |
| bool is_sym_name_front; | |
| do | |
| { | |
| if ( n++ ) | |
| put( '.' ); | |
| parseSymbolName(errStatus); | |
| if (errStatus) | |
| return; | |
| parseFunctionTypeNoReturn(); | |
| is_sym_name_front = isSymbolNameFront(errStatus); | |
| if (errStatus) | |
| return; | |
| } while ( is_sym_name_front ); | |
| } | |
| /* | |
| MangledName: | |
| _D QualifiedName Type | |
| _D QualifiedName M Type | |
| */ | |
| void parseMangledName( out bool errStatus, bool displayType, size_t n = 0 ) scope nothrow | |
| { | |
| debug(trace) printf( "parseMangledName+\n" ); | |
| debug(trace) scope(success) printf( "parseMangledName-\n" ); | |
| BufSlice name = dst.bslice_empty; | |
| auto end = pos + n; | |
| eat( '_' ); | |
| errStatus = !match( 'D' ); | |
| if (errStatus) | |
| return; | |
| do | |
| { | |
| size_t beg = dst.length; | |
| size_t nameEnd = dst.length; | |
| BufSlice attr = dst.bslice_empty; | |
| bool is_sym_name_front; | |
| do | |
| { | |
| if ( attr.length ) | |
| dst.remove(attr); // dump attributes of parent symbols | |
| if (beg != dst.length) | |
| put( '.' ); | |
| parseSymbolName(errStatus); | |
| if (errStatus) | |
| return; | |
| nameEnd = dst.length; | |
| attr = parseFunctionTypeNoReturn( displayType ); | |
| is_sym_name_front = isSymbolNameFront(errStatus); | |
| if (errStatus) | |
| return; | |
| } while (is_sym_name_front); | |
| if ( displayType ) | |
| { | |
| attr = shift( attr ); | |
| nameEnd = dst.length - attr.length; // name includes function arguments | |
| } | |
| name = dst[beg .. nameEnd]; | |
| debug(info) printf( "name (%.*s)\n", cast(int) name.length, name.getSlice.ptr ); | |
| if ( 'M' == front ) | |
| popFront(); // has 'this' pointer | |
| auto lastlen = dst.length; | |
| auto type = parseType(errStatus); | |
| if (errStatus) | |
| return; | |
| if ( displayType ) | |
| { | |
| if ( type.length ) | |
| put( ' ' ); | |
| // sort (name,attr,type) -> (attr,type,name) | |
| shift( name ); | |
| } | |
| else | |
| { | |
| // remove type | |
| assert( attr.length == 0 ); | |
| dst.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(out bool errStatus) nothrow | |
| { | |
| parseMangledName(errStatus, AddType.yes == addType); | |
| } | |
| char[] doDemangle(alias FUNC)() return scope nothrow | |
| { | |
| while ( true ) | |
| { | |
| debug(info) printf( "demangle(%.*s)\n", cast(int) buf.length, buf.ptr ); | |
| bool errStatus; | |
| FUNC(errStatus); | |
| if (!errStatus) | |
| { | |
| return dst[0 .. $].getSlice; | |
| } | |
| else | |
| { | |
| debug(info) printf( "error" ); | |
| return dst.copyInput(buf); | |
| } | |
| } | |
| } | |
| char[] demangleName() nothrow | |
| { | |
| return doDemangle!parseMangledName(); | |
| } | |
| char[] demangleType() nothrow | |
| { | |
| return doDemangle!parseType(); | |
| } | |
| } | |
| /** | |
| * Demangles D/C++ mangled names. If it is not a D/C++ mangled name, it | |
| * returns its argument name. | |
| * | |
| * Params: | |
| * buf = The string to demangle. | |
| * dst = An optional destination buffer. | |
| * __cxa_demangle = optional C++ demangler | |
| * | |
| * Returns: | |
| * The demangled name or the original string if the name is not a mangled | |
| * D/C++ name. | |
| */ | |
| char[] demangle(return scope const(char)[] buf, return scope char[] dst = null, CXX_DEMANGLER __cxa_demangle = null) nothrow pure @safe | |
| { | |
| if (__cxa_demangle && buf.length > 2 && buf[0..2] == "_Z") | |
| return demangleCXX(buf, __cxa_demangle, dst); | |
| auto d = Demangle!()(buf, dst); | |
| // fast path (avoiding throwing & catching exception) for obvious | |
| // non-D mangled names | |
| if (buf.length < 2 || !(buf[0] == 'D' || buf[0..2] == "_D")) | |
| return d.dst.copyInput(buf); | |
| return d.demangleName(); | |
| } | |
| /** | |
| * Demangles a D mangled type. | |
| * | |
| * Params: | |
| * buf = The string to demangle. | |
| * dst = An optional destination buffer. | |
| * | |
| * Returns: | |
| * 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 ) nothrow pure @safe | |
| { | |
| 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(return scope 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 @safe: | |
| size_t positionInResult(size_t pos) scope | |
| { | |
| 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) scope | |
| { | |
| 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(out string errMsg, scope ref Remangle d) scope @trusted nothrow | |
| { | |
| bool error(string msg) | |
| { | |
| errMsg = msg; | |
| return false; | |
| } | |
| 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) | |
| return error("invalid back reference"); | |
| auto savepos = d.pos; | |
| scope(exit) d.pos = savepos; | |
| size_t srcpos = refpos - n; | |
| bool errStatus; | |
| auto idlen = d.decodeNumber(errStatus); | |
| if (errStatus) | |
| return error("invalid number"); | |
| if (d.pos + idlen > d.buf.length) | |
| return error("invalid back reference"); | |
| auto id = d.buf[d.pos .. d.pos + idlen]; | |
| auto pid = id in idpos; | |
| if (!pid) | |
| return error("invalid back reference"); | |
| npos = positionInResult(*pid); | |
| } | |
| encodeBackref(reslen - npos); | |
| const pos = d.pos; // work around issues.dlang.org/show_bug.cgi?id=20675 | |
| replacements ~= Replacement(pos, result.length); | |
| } | |
| else | |
| { | |
| bool errStatus; | |
| auto n = d.decodeNumber(errStatus); | |
| if (errStatus) | |
| return error("invalid number"); | |
| if (!n || n > d.buf.length || n > d.buf.length - d.pos) | |
| return error("LName too short 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); | |
| const pos = d.pos; // work around issues.dlang.org/show_bug.cgi?id=20675 | |
| replacements ~= Replacement(pos, result.length); | |
| } | |
| else | |
| { | |
| idpos[id] = refpos; //! scope variable id used as AA key, makes this function @trusted | |
| result ~= d.buf[refpos .. d.pos]; | |
| } | |
| } | |
| lastpos = d.pos; | |
| return true; | |
| } | |
| char[] parseType( out bool errStatus, ref Remangle d, char[] name ) return scope nothrow | |
| { | |
| if (d.front != 'Q') | |
| return null; | |
| flushPosition(d); | |
| auto refPos = d.pos; | |
| d.popFront(); | |
| auto n = d.decodeBackref(); | |
| if (n == 0 || n > refPos) | |
| { | |
| // invalid back reference | |
| errStatus = true; | |
| return null; | |
| } | |
| 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) scope | |
| { | |
| 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 | |
| bool errStatus; | |
| d.parseMangledName(errStatus); | |
| if (errStatus) | |
| { | |
| // error cannot occur | |
| return mangled.dup; | |
| } | |
| if (d.hooks.lastpos < d.pos) | |
| d.hooks.result ~= d.buf[d.hooks.lastpos .. d.pos]; | |
| return d.hooks.result; | |
| } | |
| /** | |
| * Mangles a D symbol. | |
| * | |
| * Params: | |
| * T = The type of the symbol. | |
| * fqn = The fully qualified name of the symbol. | |
| * dst = An optional destination buffer. | |
| * | |
| * Returns: | |
| * The mangled name for a symbols of type T and the given fully | |
| * qualified name. | |
| */ | |
| char[] mangle(T)(return scope const(char)[] fqn, return scope char[] dst = null) @safe pure nothrow | |
| { | |
| import core.internal.string : numDigits, unsignedToTempString; | |
| static struct DotSplitter | |
| { | |
| @safe pure nothrow: | |
| const(char)[] s; | |
| @property bool empty() const { return !s.length; } | |
| @property const(char)[] front() const return scope | |
| { | |
| immutable i = indexOfDot(); | |
| return i == -1 ? s[0 .. $] : s[0 .. i]; | |
| } | |
| void popFront() scope | |
| { | |
| immutable i = indexOfDot(); | |
| s = i == -1 ? s[$ .. $] : s[i+1 .. $]; | |
| } | |
| private ptrdiff_t indexOfDot() const scope | |
| { | |
| foreach (i, c; s) if (c == '.') return i; | |
| return -1; | |
| } | |
| } | |
| size_t len = "_D".length; | |
| foreach (comp; DotSplitter(fqn)) | |
| len += numDigits(comp.length) + comp.length; | |
| len += T.mangleof.length; | |
| if (dst.length < len) dst.length = len; | |
| size_t i = "_D".length; | |
| dst[0 .. i] = "_D"; | |
| foreach (comp; DotSplitter(fqn)) | |
| { | |
| const ndigits = numDigits(comp.length); | |
| unsignedToTempString(comp.length, dst[i .. i + ndigits]); | |
| i += ndigits; | |
| dst[i .. i + comp.length] = comp[]; | |
| i += comp.length; | |
| } | |
| dst[i .. i + T.mangleof.length] = T.mangleof[]; | |
| i += T.mangleof.length; | |
| static if (hasTypeBackRef) | |
| return reencodeMangled(dst[0 .. i]); | |
| else | |
| return dst[0 .. i]; | |
| } | |
| /// | |
| @safe pure nothrow unittest | |
| { | |
| assert(mangle!int("a.b") == "_D1a1bi"); | |
| assert(mangle!(char[])("test.foo") == "_D4test3fooAa"); | |
| assert(mangle!(int function(int))("a.b") == "_D1a1bPFiZi"); | |
| } | |
| @safe pure nothrow unittest | |
| { | |
| static assert(mangle!int("a.b") == "_D1a1bi"); | |
| auto buf = new char[](10); | |
| buf = mangle!int("a.b", buf); | |
| assert(buf == "_D1a1bi"); | |
| buf = mangle!(char[])("test.foo", buf); | |
| assert(buf == "_D4test3fooAa"); | |
| buf = mangle!(real delegate(int))("modµ.dg"); | |
| assert(buf == "_D5modµ2dgDFiZe", buf); | |
| } | |
| /** | |
| * Mangles a D function. | |
| * | |
| * Params: | |
| * T = function pointer type. | |
| * fqn = The fully qualified name of the symbol. | |
| * dst = An optional destination buffer. | |
| * | |
| * Returns: | |
| * The mangled name for a function with function pointer type T and | |
| * the given fully qualified name. | |
| */ | |
| char[] mangleFunc(T:FT*, FT)(return scope const(char)[] fqn, return scope char[] dst = null) @safe pure nothrow if (is(FT == function)) | |
| { | |
| static if (isExternD!FT) | |
| { | |
| return mangle!FT(fqn, dst); | |
| } | |
| else static if (hasPlainMangling!FT) | |
| { | |
| dst.length = fqn.length; | |
| dst[] = fqn[]; | |
| return dst; | |
| } | |
| else static if (isExternCPP!FT) | |
| { | |
| static assert(0, "Can't mangle extern(C++) functions."); | |
| } | |
| else | |
| { | |
| static assert(0, "Can't mangle function with unknown linkage ("~FT.stringof~")."); | |
| } | |
| } | |
| private enum hasTypeBackRef = (int function(void**,void**)).mangleof[$-4 .. $] == "QdZi"; | |
| @safe pure nothrow unittest | |
| { | |
| assert(mangleFunc!(int function(int))("a.b") == "_D1a1bFiZi"); | |
| 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"); | |
| auto remngl = reencodeMangled(mngl); | |
| assert(remngl == "_D6object6Object8opEqualsFCQsZi"); | |
| } | |
| // trigger back tracking with ambiguity on '__T', template or identifier | |
| assert(reencodeMangled("_D3std4conv4conv7__T3std4convi") == "_D3std4convQf7__T3stdQpi"); | |
| } | |
| @safe pure nothrow unittest | |
| { | |
| int function(lazy int[], ...) fp; | |
| assert(mangle!(typeof(fp))("demangle.test") == "_D8demangle4testPFLAiYi"); | |
| assert(mangle!(typeof(*fp))("demangle.test") == "_D8demangle4testFLAiYi"); | |
| } | |
| private template isExternD(FT) if (is(FT == function)) | |
| { | |
| enum isExternD = __traits(getLinkage, FT) == "D"; | |
| } | |
| private template isExternCPP(FT) if (is(FT == function)) | |
| { | |
| enum isExternCPP = __traits(getLinkage, FT) == "C++"; | |
| } | |
| private template hasPlainMangling(FT) if (is(FT == function)) | |
| { | |
| enum lnk = __traits(getLinkage, FT); | |
| // C || Windows | |
| enum hasPlainMangling = lnk == "C" || lnk == "Windows" || lnk == "System"; | |
| } | |
| @safe pure nothrow unittest | |
| { | |
| static extern(D) void fooD(); | |
| static extern(C) void fooC(); | |
| static extern(Windows) void fooW(); | |
| static extern(C++) void fooCPP(); | |
| bool check(FT)(bool isD, bool isCPP, bool isPlain) | |
| { | |
| return isExternD!FT == isD && isExternCPP!FT == isCPP && | |
| hasPlainMangling!FT == isPlain; | |
| } | |
| static assert(check!(typeof(fooD))(true, false, false)); | |
| static assert(check!(typeof(fooC))(false, false, true)); | |
| static assert(check!(typeof(fooW))(false, false, true)); | |
| static assert(check!(typeof(fooCPP))(false, true, false)); | |
| static assert(__traits(compiles, mangleFunc!(typeof(&fooD))(""))); | |
| static assert(__traits(compiles, mangleFunc!(typeof(&fooC))(""))); | |
| static assert(__traits(compiles, mangleFunc!(typeof(&fooW))(""))); | |
| static assert(!__traits(compiles, mangleFunc!(typeof(&fooCPP))(""))); | |
| } | |
| /*** | |
| * C name mangling is done by adding a prefix on some platforms. | |
| */ | |
| version (Win32) | |
| enum string cPrefix = "_"; | |
| else version (Darwin) | |
| enum string cPrefix = "_"; | |
| else | |
| enum string cPrefix = ""; | |
| @safe pure nothrow unittest | |
| { | |
| immutable string[2][] table = | |
| [ | |
| ["printf", "printf"], | |
| ["_foo", "_foo"], | |
| ["_D88", "_D88"], | |
| ["_D3fooQeFIAyaZv", "void foo.foo(in immutable(char)[])" ], | |
| ["_D3barQeFIKAyaZv", "void bar.bar(in ref immutable(char)[])" ], | |
| ["_D4test3fooAa", "char[] test.foo"], | |
| ["_D8demangle8demangleFAaZAa", "char[] demangle.demangle(char[])"], | |
| ["_D6object6Object8opEqualsFC6ObjectZi", "int object.Object.opEquals(Object)"], | |
| ["_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"], | |
| ["_D8demangle4testFLC6ObjectLDFLiZiZi", "int demangle.test(lazy Object, lazy int delegate(lazy int))"], | |
| ["_D8demangle4testFAiXi", "int demangle.test(int[]...)"], | |
| ["_D8demangle4testFAiYi", "int demangle.test(int[], ...)"], | |
| ["_D8demangle4testFLAiXi", "int demangle.test(lazy int[]...)"], | |
| ["_D8demangle4testFLAiYi", "int demangle.test(lazy int[], ...)"], | |
| ["_D6plugin8generateFiiZAya", "immutable(char)[] plugin.generate(int, int)"], | |
| ["_D6plugin8generateFiiZAxa", "const(char)[] plugin.generate(int, int)"], | |
| ["_D6plugin8generateFiiZAOa", "shared(char)[] plugin.generate(int, int)"], | |
| ["_D8demangle3fnAFZ3fnBMFZv", "void demangle.fnA().fnB()"], | |
| ["_D8demangle4mainFZ1S3fnCMFZv", "void demangle.main().S.fnC()"], | |
| ["_D8demangle4mainFZ1S3fnDMFZv", "void demangle.main().S.fnD()"], | |
| ["_D8demangle20__T2fnVAiA4i1i2i3i4Z2fnFZv", "void demangle.fn!([1, 2, 3, 4]).fn()"], | |
| ["_D8demangle10__T2fnVi1Z2fnFZv", "void demangle.fn!(1).fn()"], | |
| ["_D8demangle26__T2fnVS8demangle1SS2i1i2Z2fnFZv", "void demangle.fn!(demangle.S(1, 2)).fn()"], | |
| ["_D8demangle13__T2fnVeeNANZ2fnFZv", "void demangle.fn!(real.nan).fn()"], | |
| ["_D8demangle14__T2fnVeeNINFZ2fnFZv", "void demangle.fn!(-real.infinity).fn()"], | |
| ["_D8demangle13__T2fnVeeINFZ2fnFZv", "void demangle.fn!(real.infinity).fn()"], | |
| ["_D8demangle21__T2fnVHiiA2i1i2i3i4Z2fnFZv", "void demangle.fn!([1:2, 3:4]).fn()"], | |
| ["_D8demangle2fnFNgiZNgi", "inout(int) demangle.fn(inout(int))"], | |
| ["_D8demangle29__T2fnVa97Va9Va0Vu257Vw65537Z2fnFZv", "void demangle.fn!('a', '\\t', \\x00, '\\u0101', '\\U00010001').fn()"], | |
| ["_D2gc11gctemplates56__T8mkBitmapTS3std5range13__T4iotaTiTiZ4iotaFiiZ6ResultZ8mkBitmapFNbNiNfPmmZv", | |
| "nothrow @nogc @safe void gc.gctemplates.mkBitmap!(std.range.iota!(int, int).iota(int, int).Result).mkBitmap(ulong*, ulong)"], | |
| ["_D8serenity9persister6Sqlite69__T15SqlitePersisterTS8serenity9persister6Sqlite11__unittest6FZ4TestZ15SqlitePersister12__T7opIndexZ7opIndexMFmZS8serenity9persister6Sqlite11__unittest6FZ4Test", | |
| "serenity.persister.Sqlite.__unittest6().Test serenity.persister.Sqlite.SqlitePersister!(serenity.persister.Sqlite.__unittest6().Test).SqlitePersister.opIndex!().opIndex(ulong)"], | |
| ["_D8bug100274mainFZ5localMFZi","int bug10027.main().local()"], | |
| ["_D8demangle4testFNhG16gZv", "void demangle.test(__vector(byte[16]))"], | |
| ["_D8demangle4testFNhG8sZv", "void demangle.test(__vector(short[8]))"], | |
| ["_D8demangle4testFNhG4iZv", "void demangle.test(__vector(int[4]))"], | |
| ["_D8demangle4testFNhG2lZv", "void demangle.test(__vector(long[2]))"], | |
| ["_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!(bug11192.main().bar).foo()"], | |
| ["_D13libd_demangle12__ModuleInfoZ", "libd_demangle.__ModuleInfo"], | |
| ["_D15TypeInfo_Struct6__vtblZ", "TypeInfo_Struct.__vtbl"], | |
| ["_D3std5stdio12__ModuleInfoZ", "std.stdio.__ModuleInfo"], | |
| ["_D3std6traits15__T8DemangleTkZ8Demangle6__initZ", "std.traits.Demangle!(uint).Demangle.__init"], | |
| ["_D3foo3Bar7__ClassZ", "foo.Bar.__Class"], | |
| ["_D3foo3Bar6__vtblZ", "foo.Bar.__vtbl"], | |
| ["_D3foo3Bar11__interfaceZ", "foo.Bar.__interface"], | |
| ["_D3foo7__arrayZ", "foo.__array"], | |
| ["_D8link657428__T3fooVE8link65746Methodi0Z3fooFZi", "int link6574.foo!(0).foo()"], | |
| ["_D8link657429__T3fooHVE8link65746Methodi0Z3fooFZi", "int link6574.foo!(0).foo()"], | |
| ["_D4test22__T4funcVAyaa3_610a62Z4funcFNaNbNiNmNfZAya", `pure nothrow @nogc @live @safe immutable(char)[] test.func!("a\x0ab").func()`], | |
| ["_D3foo3barFzkZzi", "cent foo.bar(ucent)"], | |
| ["_D5bug145Class3fooMFNlZPv", "scope void* bug14.Class.foo()"], | |
| ["_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()"], | |
| ["_D6mangle__T8fun21753VSQv6S21753S1f_DQBj10__lambda71MFNaNbNiNfZvZQCbQp", | |
| "void function() pure nothrow @nogc @safe mangle.fun21753!(mangle.S21753(mangle.__lambda71())).fun21753"], | |
| // 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 | |
| ["_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()"], | |
| // formerly ambiguous on 'V', template value argument or pascal function | |
| // pascal functions have now been removed (in v2.095.0) | |
| ["_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))`], | |
| ["_D4test4rrs1FKPiZv", "void test.rrs1(ref int*)"], | |
| ["_D4test4rrs1FMNkJPiZv", "void test.rrs1(scope return out int*)"], | |
| ["_D4test4rrs1FMNkKPiZv", "void test.rrs1(scope return ref int*)"], | |
| ["_D4test4rrs1FNkJPiZv", "void test.rrs1(return out int*)"], | |
| ["_D4test4rrs1FNkKPiZv", "void test.rrs1(return ref int*)"], | |
| ["_D4test4rrs1FNkMJPiZv", "void test.rrs1(return scope out int*)"], | |
| ["_D4test4rrs1FNkMKPiZv", "void test.rrs1(return scope ref int*)"], | |
| ["_D4test4rrs1FNkMPiZv", "void test.rrs1(return scope int*)"], | |
| // `scope` and `return` combinations | |
| ["_D3foo3Foo3barMNgFNjNlNfZNgPv", "inout return scope @safe inout(void*) foo.Foo.bar()"], | |
| ["_D3foo3FooQiMNgFNlNfZv", "inout scope @safe void foo.Foo.foo()"], | |
| ["_D3foo3Foo4foorMNgFNjNfZv", "inout return @safe void foo.Foo.foor()"], | |
| ["_D3foo3Foo3rabMNgFNlNjNfZv", "inout scope return @safe void foo.Foo.rab()"], | |
| // Hex float digit overflow | |
| ["_D3foo__T1fVdeFA3D0FBFB72A3C33FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "_D3foo__T1fVdeFA3D0FBFB72A3C33FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"], | |
| ]; | |
| template staticIota(int x) | |
| { | |
| template Seq(T...){ alias Seq = T; } | |
| static if (x == 0) | |
| alias staticIota = Seq!(); | |
| else | |
| alias staticIota = Seq!(staticIota!(x - 1), x - 1); | |
| } | |
| foreach ( i, name; table ) | |
| { | |
| auto r = demangle( name[0] ); | |
| assert( r == name[1], | |
| "demangled `" ~ name[0] ~ "` as `" ~ r ~ "` but expected `" ~ name[1] ~ "`"); | |
| } | |
| foreach ( i; staticIota!(table.length) ) | |
| { | |
| enum r = demangle( table[i][0] ); | |
| static assert( r == table[i][1], | |
| "demangled `" ~ table[i][0] ~ "` as `" ~ r ~ "` but expected `" ~ table[i][1] ~ "`"); | |
| } | |
| { | |
| // https://issues.dlang.org/show_bug.cgi?id=18531 | |
| auto symbol = `_D3std3uni__T6toCaseS_DQvQt12toLowerIndexFNaNbNiNewZtVii1043S_DQCjQCi10toLowerTabFNaNbNiNemZwSQDo5ascii7toLowerTAyaZQDzFNaNeQmZ14__foreachbody2MFNaNeKmKwZ14__foreachbody3MFNaNeKwZi`; | |
| auto demangled = `pure @trusted int std.uni.toCase!(std.uni.toLowerIndex(dchar), 1043, std.uni.toLowerTab(ulong), std.ascii.toLower, immutable(char)[]).toCase(immutable(char)[]).__foreachbody2(ref ulong, ref dchar).__foreachbody3(ref dchar)`; | |
| auto dst = new char[200]; | |
| auto ret = demangle( symbol, dst); | |
| assert( ret == demangled ); | |
| } | |
| } | |
| unittest | |
| { | |
| // https://issues.dlang.org/show_bug.cgi?id=18300 | |
| string s = demangle.mangleof; | |
| foreach (i; 1..77) | |
| { | |
| char[] buf = new char[i]; | |
| auto ds = demangle(s, buf); | |
| assert(ds == "pure nothrow @safe char[] core.demangle.demangle(scope return const(char)[], scope return char[], extern (C) char* function(const(char*), char*, ulong*, int*) pure nothrow @trusted*)" || | |
| ds == "pure nothrow @safe char[] core.demangle.demangle(return scope const(char)[], return scope char[], extern (C) char* function(const(char*), char*, ulong*, int*) pure nothrow @trusted*)" || | |
| ds == "pure nothrow @safe char[] core.demangle.demangle(scope return const(char)[], scope return char[], extern (C) char* function(const(char*), char*, uint*, int*) pure nothrow @trusted*)" || | |
| ds == "pure nothrow @safe char[] core.demangle.demangle(return scope const(char)[], return scope char[], extern (C) char* function(const(char*), char*, uint*, int*) pure nothrow @trusted*)", ds); | |
| } | |
| } | |
| unittest | |
| { | |
| // https://issues.dlang.org/show_bug.cgi?id=18300 | |
| string s = "_D1"; | |
| string expected = "int "; | |
| foreach (_; 0..10_000) | |
| { | |
| s ~= "a1"; | |
| expected ~= "a."; | |
| } | |
| s ~= "FiZi"; | |
| expected ~= "F"; | |
| assert(s.demangle == expected); | |
| // https://issues.dlang.org/show_bug.cgi?id=23562 | |
| assert(demangle("_Zv") == "_Zv"); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=22235 | |
| unittest | |
| { | |
| enum parent = __MODULE__ ~ '.' ~ __traits(identifier, __traits(parent, {})); | |
| static noreturn abort() { assert(false); } | |
| assert(demangle(abort.mangleof) == "pure nothrow @nogc @safe noreturn " ~ parent ~ "().abort()"); | |
| static void accept(noreturn) {} | |
| assert(demangle(accept.mangleof) == "pure nothrow @nogc @safe void " ~ parent ~ "().accept(noreturn)"); | |
| static void templ(T)(T, T) {} | |
| assert(demangle(templ!noreturn.mangleof) == "pure nothrow @nogc @safe void " ~ parent ~ "().templ!(noreturn).templ(noreturn, noreturn)"); | |
| static struct S(T) {} | |
| static void aggr(S!noreturn) { assert(0); } | |
| assert(demangle(aggr.mangleof) == "pure nothrow @nogc @safe void " ~ parent ~ "().aggr(" ~ parent ~ "().S!(noreturn).S)"); | |
| } | |
| // locally purified for internal use here only | |
| extern (C) private | |
| { | |
| pure @trusted @nogc nothrow pragma(mangle, "fakePureReprintReal") void pureReprintReal(char[] nptr); | |
| 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.ptr, null); | |
| snprintf(nptr.ptr, nptr.length, "%#Lg", val); | |
| errno = err; | |
| } | |
| } | |
| private struct ManglingFlagInfo | |
| { | |
| /// The flag value to use | |
| ushort flag; | |
| /// Human-readable representation | |
| string value; | |
| } | |
| private enum TypeCtor : ushort { | |
| None = 0, | |
| //// 'x' | |
| Const = (1 << 1), | |
| /// 'y' | |
| Immutable = (1 << 2), | |
| /// 'O' | |
| Shared = (1 << 3), | |
| /// | |
| InOut = (1 << 4), | |
| } | |
| private immutable ManglingFlagInfo[] typeCtors = [ | |
| ManglingFlagInfo(TypeCtor.Immutable, "immutable"), | |
| ManglingFlagInfo(TypeCtor.Shared, "shared"), | |
| ManglingFlagInfo(TypeCtor.InOut, "inout"), | |
| ManglingFlagInfo(TypeCtor.Const, "const"), | |
| ]; | |
| private enum FuncAttributes : ushort { | |
| None = 0, | |
| //// 'a' | |
| Pure = (1 << 1), | |
| //// 'b' | |
| Nothrow = (1 << 2), | |
| //// 'c' | |
| Ref = (1 << 3), | |
| //// 'd' | |
| Property = (1 << 4), | |
| //// 'e' | |
| Trusted = (1 << 5), | |
| //// 'f' | |
| Safe = (1 << 6), | |
| //// 'i' | |
| NoGC = (1 << 7), | |
| //// 'j' | |
| Return = (1 << 8), | |
| //// 'l' | |
| Scope = (1 << 9), | |
| //// 'm' | |
| Live = (1 << 10), | |
| /// Their order matter | |
| ReturnScope = (1 << 11), | |
| ScopeReturn = (1 << 12), | |
| } | |
| // The order in which we process is the same as in compiler/dmd/src/dmangle.d | |
| private immutable ManglingFlagInfo[] funcAttrs = [ | |
| ManglingFlagInfo(FuncAttributes.Pure, "pure"), | |
| ManglingFlagInfo(FuncAttributes.Nothrow, "nothrow"), | |
| ManglingFlagInfo(FuncAttributes.Ref, "ref"), | |
| ManglingFlagInfo(FuncAttributes.Property, "@property"), | |
| ManglingFlagInfo(FuncAttributes.NoGC, "@nogc"), | |
| ManglingFlagInfo(FuncAttributes.ReturnScope, "return scope"), | |
| ManglingFlagInfo(FuncAttributes.ScopeReturn, "scope return"), | |
| ManglingFlagInfo(FuncAttributes.Return, "return"), | |
| ManglingFlagInfo(FuncAttributes.Scope, "scope"), | |
| ManglingFlagInfo(FuncAttributes.Live, "@live"), | |
| ManglingFlagInfo(FuncAttributes.Trusted, "@trusted"), | |
| ManglingFlagInfo(FuncAttributes.Safe, "@safe"), | |
| ]; | |
| private string toStringConsume (immutable ManglingFlagInfo[] infos, ref ushort base) | |
| @safe pure nothrow @nogc | |
| { | |
| foreach (const ref info; infos) | |
| { | |
| if ((base & info.flag) == info.flag) | |
| { | |
| base &= ~info.flag; | |
| return info.value; | |
| } | |
| } | |
| return null; | |
| } | |
| private shared CXX_DEMANGLER __cxa_demangle; | |
| /** | |
| * Returns: | |
| * a CXX_DEMANGLER if a C++ stdlib is loaded | |
| */ | |
| CXX_DEMANGLER getCXXDemangler() nothrow @trusted | |
| { | |
| import core.atomic : atomicLoad, atomicStore; | |
| if (__cxa_demangle is null) | |
| version (Posix) | |
| { | |
| import core.sys.posix.dlfcn : dlsym; | |
| version (DragonFlyBSD) import core.sys.dragonflybsd.dlfcn : RTLD_DEFAULT; | |
| version (FreeBSD) import core.sys.freebsd.dlfcn : RTLD_DEFAULT; | |
| version (linux) import core.sys.linux.dlfcn : RTLD_DEFAULT; | |
| version (NetBSD) import core.sys.netbsd.dlfcn : RTLD_DEFAULT; | |
| version (OpenBSD) import core.sys.openbsd.dlfcn : RTLD_DEFAULT; | |
| version (Darwin) import core.sys.darwin.dlfcn : RTLD_DEFAULT; | |
| version (Solaris) import core.sys.solaris.dlfcn : RTLD_DEFAULT; | |
| if (auto found = cast(CXX_DEMANGLER) dlsym(RTLD_DEFAULT, "__cxa_demangle")) | |
| atomicStore(__cxa_demangle, found); | |
| } | |
| if (__cxa_demangle is null) | |
| { | |
| static extern(C) char* _(const char* mangled_name, char* output_buffer, | |
| size_t* length, int* status) nothrow pure @trusted | |
| { | |
| *status = -1; | |
| return null; | |
| } | |
| atomicStore(__cxa_demangle, &_); | |
| } | |
| return atomicLoad(__cxa_demangle); | |
| } | |
| /** | |
| * Demangles C++ mangled names. If it is not a C++ mangled name, it | |
| * returns its argument name. | |
| * | |
| * Params: | |
| * buf = The string to demangle. | |
| * __cxa_demangle = C++ demangler | |
| * dst = An optional destination buffer. | |
| * | |
| * Returns: | |
| * The demangled name or the original string if the name is not a mangled | |
| * C++ name. | |
| */ | |
| private char[] demangleCXX(return scope const(char)[] buf, CXX_DEMANGLER __cxa_demangle, return scope char[] dst = null,) nothrow pure @trusted | |
| { | |
| char[] c_string = dst; // temporarily use dst buffer if possible | |
| c_string.length = buf.length + 1; | |
| c_string[0 .. buf.length] = buf[0 .. buf.length]; | |
| c_string[buf.length] = '\0'; | |
| int status; | |
| size_t demangled_length; | |
| auto demangled = __cxa_demangle(&c_string[0], null, &demangled_length, &status); | |
| scope (exit) { | |
| import core.memory; | |
| pureFree(cast(void*) demangled); | |
| } | |
| if (status == 0) | |
| { | |
| dst.length = demangled_length; | |
| dst[] = demangled[0 .. demangled_length]; | |
| return dst; | |
| } | |
| dst.length = buf.length; | |
| dst[] = buf[]; | |
| return dst; | |
| } | |
| private struct Buffer | |
| { | |
| enum size_t minSize = 4000; | |
| @safe pure: | |
| private char[] dst; | |
| private size_t len; | |
| public alias opDollar = len; | |
| public size_t length () const scope @safe pure nothrow @nogc | |
| { | |
| return this.len; | |
| } | |
| public BufSlice opSlice (size_t from, size_t to) | |
| return scope @safe pure nothrow @nogc | |
| { | |
| return bslice(from, to); | |
| } | |
| static bool contains(scope const(char)[] a, scope const BufSlice b) @safe nothrow | |
| { | |
| return | |
| b.from < a.length && | |
| b.to <= a.length; | |
| } | |
| char[] copyInput(scope const(char)[] buf) | |
| return scope nothrow | |
| { | |
| if (dst.length < buf.length) | |
| dst.length = buf.length; | |
| char[] r = dst[0 .. buf.length]; | |
| r[] = buf[]; | |
| return r; | |
| } | |
| private void checkAndStretchBuf(size_t len_to_add) scope nothrow | |
| { | |
| const required = len + len_to_add; | |
| if (required > dst.length) | |
| dst.length = dst.length + len_to_add; | |
| } | |
| // move val to the end of the dst buffer | |
| BufSlice shift(scope const BufSlice val) return scope nothrow | |
| { | |
| version (DigitalMars) pragma(inline, false); // tame dmd inliner | |
| if (val.length) | |
| { | |
| const ptrdiff_t s = val.from; | |
| const size_t f = len; | |
| assert(contains( dst[0 .. len], val ), | |
| "\ndst=\""~dst[0 .. len]~"\"\n"~ | |
| "val=\""~val.getSlice~"\"\n" | |
| ); | |
| checkAndStretchBuf(val.length); | |
| // store value temporary over len index | |
| dst[len .. len + val.length] = val.getSlice(); | |
| // shift all chars including temporary saved above | |
| // if buf was allocated above it will be leave for further usage | |
| for (size_t p = s; p < f; p++) | |
| dst[p] = dst[p + val.length]; | |
| return bslice(len - val.length, len); | |
| } | |
| return bslice_empty; | |
| } | |
| // remove val from dst buffer | |
| void remove(scope BufSlice val) scope nothrow | |
| { | |
| version (DigitalMars) pragma(inline, false); // tame dmd inliner | |
| if ( val.length ) | |
| { | |
| assert( contains( dst[0 .. len], val ) ); | |
| assert( len >= val.length && len <= dst.length ); | |
| len -= val.length; | |
| for (size_t p = val.from; p < len; p++) | |
| dst[p] = dst[p + val.length]; | |
| } | |
| } | |
| void append(scope const(char)[] val) scope nothrow | |
| { | |
| version (DigitalMars) pragma(inline, false); // tame dmd inliner | |
| if (val.length) | |
| { | |
| if ( !dst.length ) | |
| dst.length = minSize; | |
| debug(info) printf( "appending (%.*s)\n", cast(int) val.length, val.ptr ); | |
| checkAndStretchBuf(val.length); | |
| // data is already not in place? | |
| if ( &dst[len] != &val[0] ) | |
| dst[len .. len + val.length] = val[]; | |
| len += val.length; | |
| } | |
| } | |
| @nogc: | |
| private scope bslice(size_t from, size_t to) nothrow | |
| { | |
| return BufSlice(dst, from, to); | |
| } | |
| private static scope bslice_empty() nothrow | |
| { | |
| return BufSlice.init; | |
| } | |
| } | |
| private struct BufSlice | |
| { | |
| char[] buf; | |
| size_t from; | |
| size_t to; | |
| @safe pure nothrow: | |
| @disable this(); | |
| this(return scope char[] buf) scope nothrow @nogc | |
| { | |
| this(buf, 0, 0); | |
| } | |
| this(return scope char[] buf, size_t from, size_t to, bool lastArgIsLen = false) scope nothrow @nogc | |
| { | |
| this.buf = buf; | |
| this.from = from; | |
| if (lastArgIsLen) | |
| this.to = from + to; | |
| else | |
| this.to = to; | |
| } | |
| invariant | |
| { | |
| if (buf is null) | |
| { | |
| assert(from == 0); | |
| assert(to == 0); | |
| } | |
| assert(from <= to); | |
| } | |
| auto getSlice() inout nothrow scope { return buf[from .. to]; } | |
| size_t length() const scope { return to - from; } | |
| } |