mirror of
https://gcc.gnu.org/git/gcc.git
synced 2026-02-22 12:00:03 -05:00
D front-end changes: - Import latest fixes from dmd v2.110.0-rc.1. D runtime changes: - Import latest fixes from druntime v2.110.0-rc.1. Phobos changes: - Import latest fixes from phobos v2.110.0-rc.1. Included in the merge are fixes for the following PRs: PR d/118438 PR d/118448 PR d/118449 gcc/d/ChangeLog: * dmd/MERGE: Merge upstream dmd d6f693b46a. * d-incpath.cc (add_import_paths): Update for new front-end interface. libphobos/ChangeLog: * libdruntime/MERGE: Merge upstream druntime d6f693b46a. * src/MERGE: Merge upstream phobos 336bed6d8. * testsuite/libphobos.init_fini/custom_gc.d: Adjust test.
3225 lines
94 KiB
D
3225 lines
94 KiB
D
/**
|
|
* 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; }
|
|
}
|