387d0773f3
The storage class `in' is now a first-class citizen with its own mangle symbol, of which also permits `in ref'. Previously, `in' was an alias to `const [scope]', which is a type constructor. The mangle symbol repurposed for this is `I', which was originally used by identifier types. However, while TypeIdentifier is part of the grammar, it must be resolved to some other entity during the semantic passes, and so shouldn't appear anywhere in the mangled name. Old tests that are now no longer valid have been removed. libiberty/ChangeLog: * d-demangle.c (dlang_function_args): Handle 'in' and 'in ref' parameter storage classes. (dlang_type): Remove identifier type. * testsuite/d-demangle-expected: Update tests.
1920 lines
45 KiB
C
1920 lines
45 KiB
C
/* Demangler for the D programming language
|
|
Copyright (C) 2014-2020 Free Software Foundation, Inc.
|
|
Written by Iain Buclaw (ibuclaw@gdcproject.org)
|
|
|
|
This file is part of the libiberty library.
|
|
Libiberty is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
In addition to the permissions in the GNU Library General Public
|
|
License, the Free Software Foundation gives you unlimited permission
|
|
to link the compiled version of this file into combinations with other
|
|
programs, and to distribute those combinations without any restriction
|
|
coming from the use of this file. (The Library Public License
|
|
restrictions do apply in other respects; for example, they cover
|
|
modification of the file, and distribution when not linked into a
|
|
combined executable.)
|
|
|
|
Libiberty is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
License along with libiberty; see the file COPYING.LIB.
|
|
If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
/* This file exports one function; dlang_demangle. */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "safe-ctype.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
#ifdef HAVE_STDLIB_H
|
|
#include <stdlib.h>
|
|
#endif
|
|
|
|
#include <demangle.h>
|
|
#include "libiberty.h"
|
|
|
|
/* A mini string-handling package */
|
|
|
|
typedef struct string /* Beware: these aren't required to be */
|
|
{ /* '\0' terminated. */
|
|
char *b; /* pointer to start of string */
|
|
char *p; /* pointer after last character */
|
|
char *e; /* pointer after end of allocated space */
|
|
} string;
|
|
|
|
static void
|
|
string_need (string *s, int n)
|
|
{
|
|
int tem;
|
|
|
|
if (s->b == NULL)
|
|
{
|
|
if (n < 32)
|
|
{
|
|
n = 32;
|
|
}
|
|
s->p = s->b = XNEWVEC (char, n);
|
|
s->e = s->b + n;
|
|
}
|
|
else if (s->e - s->p < n)
|
|
{
|
|
tem = s->p - s->b;
|
|
n += tem;
|
|
n *= 2;
|
|
s->b = XRESIZEVEC (char, s->b, n);
|
|
s->p = s->b + tem;
|
|
s->e = s->b + n;
|
|
}
|
|
}
|
|
|
|
static void
|
|
string_delete (string *s)
|
|
{
|
|
if (s->b != NULL)
|
|
{
|
|
XDELETEVEC (s->b);
|
|
s->b = s->e = s->p = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
string_init (string *s)
|
|
{
|
|
s->b = s->p = s->e = NULL;
|
|
}
|
|
|
|
static int
|
|
string_length (string *s)
|
|
{
|
|
if (s->p == s->b)
|
|
{
|
|
return 0;
|
|
}
|
|
return s->p - s->b;
|
|
}
|
|
|
|
static void
|
|
string_setlength (string *s, int n)
|
|
{
|
|
if (n - string_length (s) < 0)
|
|
{
|
|
s->p = s->b + n;
|
|
}
|
|
}
|
|
|
|
static void
|
|
string_append (string *p, const char *s)
|
|
{
|
|
int n = strlen (s);
|
|
string_need (p, n);
|
|
memcpy (p->p, s, n);
|
|
p->p += n;
|
|
}
|
|
|
|
static void
|
|
string_appendn (string *p, const char *s, int n)
|
|
{
|
|
if (n != 0)
|
|
{
|
|
string_need (p, n);
|
|
memcpy (p->p, s, n);
|
|
p->p += n;
|
|
}
|
|
}
|
|
|
|
static void
|
|
string_prependn (string *p, const char *s, int n)
|
|
{
|
|
char *q;
|
|
|
|
if (n != 0)
|
|
{
|
|
string_need (p, n);
|
|
for (q = p->p - 1; q >= p->b; q--)
|
|
{
|
|
q[n] = q[0];
|
|
}
|
|
memcpy (p->b, s, n);
|
|
p->p += n;
|
|
}
|
|
}
|
|
|
|
static void
|
|
string_prepend (string *p, const char *s)
|
|
{
|
|
if (s != NULL && *s != '\0')
|
|
{
|
|
string_prependn (p, s, strlen (s));
|
|
}
|
|
}
|
|
|
|
/* Demangle information structure we pass around. */
|
|
struct dlang_info
|
|
{
|
|
/* The string we are demangling. */
|
|
const char *s;
|
|
/* The index of the last back reference. */
|
|
int last_backref;
|
|
};
|
|
|
|
/* Pass as the LEN to dlang_parse_template if symbol length is not known. */
|
|
enum { TEMPLATE_LENGTH_UNKNOWN = -1 };
|
|
|
|
/* Prototypes for forward referenced functions */
|
|
static const char *dlang_function_type (string *, const char *,
|
|
struct dlang_info *);
|
|
|
|
static const char *dlang_function_args (string *, const char *,
|
|
struct dlang_info *);
|
|
|
|
static const char *dlang_type (string *, const char *, struct dlang_info *);
|
|
|
|
static const char *dlang_value (string *, const char *, const char *, char);
|
|
|
|
static const char *dlang_parse_qualified (string *, const char *,
|
|
struct dlang_info *, int);
|
|
|
|
static const char *dlang_parse_mangle (string *, const char *,
|
|
struct dlang_info *);
|
|
|
|
static const char *dlang_parse_tuple (string *, const char *,
|
|
struct dlang_info *);
|
|
|
|
static const char *dlang_parse_template (string *, const char *,
|
|
struct dlang_info *, long);
|
|
|
|
static const char *dlang_lname (string *, const char *, long);
|
|
|
|
|
|
/* Extract the number from MANGLED, and assign the result to RET.
|
|
Return the remaining string on success or NULL on failure. */
|
|
static const char *
|
|
dlang_number (const char *mangled, long *ret)
|
|
{
|
|
/* Return NULL if trying to extract something that isn't a digit. */
|
|
if (mangled == NULL || !ISDIGIT (*mangled))
|
|
return NULL;
|
|
|
|
(*ret) = 0;
|
|
|
|
while (ISDIGIT (*mangled))
|
|
{
|
|
(*ret) *= 10;
|
|
|
|
/* If an overflow occured when multiplying by ten, the result
|
|
will not be a multiple of ten. */
|
|
if ((*ret % 10) != 0)
|
|
return NULL;
|
|
|
|
(*ret) += mangled[0] - '0';
|
|
mangled++;
|
|
}
|
|
|
|
if (*mangled == '\0' || *ret < 0)
|
|
return NULL;
|
|
|
|
return mangled;
|
|
}
|
|
|
|
/* Extract the hex-digit from MANGLED, and assign the result to RET.
|
|
Return the remaining string on success or NULL on failure. */
|
|
static const char *
|
|
dlang_hexdigit (const char *mangled, char *ret)
|
|
{
|
|
char c;
|
|
|
|
/* Return NULL if trying to extract something that isn't a hexdigit. */
|
|
if (mangled == NULL || !ISXDIGIT (mangled[0]) || !ISXDIGIT (mangled[1]))
|
|
return NULL;
|
|
|
|
c = mangled[0];
|
|
if (!ISDIGIT (c))
|
|
(*ret) = (c - (ISUPPER (c) ? 'A' : 'a') + 10);
|
|
else
|
|
(*ret) = (c - '0');
|
|
|
|
c = mangled[1];
|
|
if (!ISDIGIT (c))
|
|
(*ret) = (*ret << 4) | (c - (ISUPPER (c) ? 'A' : 'a') + 10);
|
|
else
|
|
(*ret) = (*ret << 4) | (c - '0');
|
|
|
|
mangled += 2;
|
|
|
|
return mangled;
|
|
}
|
|
|
|
/* Extract the function calling convention from MANGLED and
|
|
return 1 on success or 0 on failure. */
|
|
static int
|
|
dlang_call_convention_p (const char *mangled)
|
|
{
|
|
switch (*mangled)
|
|
{
|
|
case 'F': case 'U': case 'V':
|
|
case 'W': case 'R': case 'Y':
|
|
return 1;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Extract the back reference position from MANGLED, and assign the result
|
|
to RET. Return the remaining string on success or NULL on failure. */
|
|
static const char *
|
|
dlang_decode_backref (const char *mangled, long *ret)
|
|
{
|
|
/* Return NULL if trying to extract something that isn't a digit. */
|
|
if (mangled == NULL || !ISALPHA (*mangled))
|
|
return NULL;
|
|
|
|
/* Any identifier or non-basic type that has been emitted to the mangled
|
|
symbol before will not be emitted again, but is referenced by a special
|
|
sequence encoding the relative position of the original occurrence in the
|
|
mangled symbol name.
|
|
|
|
Numbers in back references are encoded with base 26 by upper case letters
|
|
A-Z for higher digits but lower case letters a-z for the last digit.
|
|
|
|
NumberBackRef:
|
|
[a-z]
|
|
[A-Z] NumberBackRef
|
|
^
|
|
*/
|
|
(*ret) = 0;
|
|
|
|
while (ISALPHA (*mangled))
|
|
{
|
|
(*ret) *= 26;
|
|
|
|
/* If an overflow occured when multiplying by 26, the result
|
|
will not be a multiple of 26. */
|
|
if ((*ret % 26) != 0)
|
|
return NULL;
|
|
|
|
if (mangled[0] >= 'a' && mangled[0] <= 'z')
|
|
{
|
|
(*ret) += mangled[0] - 'a';
|
|
return mangled + 1;
|
|
}
|
|
|
|
(*ret) += mangled[0] - 'A';
|
|
mangled++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Extract the symbol pointed at by the back reference and assign the result
|
|
to RET. Return the remaining string on success or NULL on failure. */
|
|
static const char *
|
|
dlang_backref (const char *mangled, const char **ret, struct dlang_info *info)
|
|
{
|
|
(*ret) = NULL;
|
|
|
|
if (mangled == NULL || *mangled != 'Q')
|
|
return NULL;
|
|
|
|
/* Position of 'Q'. */
|
|
const char *qpos = mangled;
|
|
long refpos;
|
|
mangled++;
|
|
|
|
mangled = dlang_decode_backref (mangled, &refpos);
|
|
if (mangled == NULL)
|
|
return NULL;
|
|
|
|
if (refpos <= 0 || refpos > qpos - info->s)
|
|
return NULL;
|
|
|
|
/* Set the position of the back reference. */
|
|
(*ret) = qpos - refpos;
|
|
|
|
return mangled;
|
|
}
|
|
|
|
/* Demangle a back referenced symbol from MANGLED and append it to DECL.
|
|
Return the remaining string on success or NULL on failure. */
|
|
static const char *
|
|
dlang_symbol_backref (string *decl, const char *mangled,
|
|
struct dlang_info *info)
|
|
{
|
|
/* An identifier back reference always points to a digit 0 to 9.
|
|
|
|
IdentifierBackRef:
|
|
Q NumberBackRef
|
|
^
|
|
*/
|
|
const char *backref;
|
|
long len;
|
|
|
|
/* Get position of the back reference. */
|
|
mangled = dlang_backref (mangled, &backref, info);
|
|
|
|
/* Must point to a simple identifier. */
|
|
backref = dlang_number (backref, &len);
|
|
if (backref == NULL)
|
|
return NULL;
|
|
|
|
backref = dlang_lname (decl, backref, len);
|
|
if (backref == NULL)
|
|
return NULL;
|
|
|
|
return mangled;
|
|
}
|
|
|
|
/* Demangle a back referenced type from MANGLED and append it to DECL.
|
|
IS_FUNCTION is 1 if the back referenced type is expected to be a function.
|
|
Return the remaining string on success or NULL on failure. */
|
|
static const char *
|
|
dlang_type_backref (string *decl, const char *mangled, struct dlang_info *info,
|
|
int is_function)
|
|
{
|
|
/* A type back reference always points to a letter.
|
|
|
|
TypeBackRef:
|
|
Q NumberBackRef
|
|
^
|
|
*/
|
|
const char *backref;
|
|
|
|
/* If we appear to be moving backwards through the mangle string, then
|
|
bail as this may be a recursive back reference. */
|
|
if (mangled - info->s >= info->last_backref)
|
|
return NULL;
|
|
|
|
int save_refpos = info->last_backref;
|
|
info->last_backref = mangled - info->s;
|
|
|
|
/* Get position of the back reference. */
|
|
mangled = dlang_backref (mangled, &backref, info);
|
|
|
|
/* Must point to a type. */
|
|
if (is_function)
|
|
backref = dlang_function_type (decl, backref, info);
|
|
else
|
|
backref = dlang_type (decl, backref, info);
|
|
|
|
info->last_backref = save_refpos;
|
|
|
|
if (backref == NULL)
|
|
return NULL;
|
|
|
|
return mangled;
|
|
}
|
|
|
|
/* Extract the beginning of a symbol name from MANGLED and
|
|
return 1 on success or 0 on failure. */
|
|
static int
|
|
dlang_symbol_name_p (const char *mangled, struct dlang_info *info)
|
|
{
|
|
long ret;
|
|
const char *qref = mangled;
|
|
|
|
if (ISDIGIT (*mangled))
|
|
return 1;
|
|
|
|
if (mangled[0] == '_' && mangled[1] == '_'
|
|
&& (mangled[2] == 'T' || mangled[2] == 'U'))
|
|
return 1;
|
|
|
|
if (*mangled != 'Q')
|
|
return 0;
|
|
|
|
mangled = dlang_decode_backref (mangled + 1, &ret);
|
|
if (mangled == NULL || ret <= 0 || ret > qref - info->s)
|
|
return 0;
|
|
|
|
return ISDIGIT (qref[-ret]);
|
|
}
|
|
|
|
/* Demangle the calling convention from MANGLED and append it to DECL.
|
|
Return the remaining string on success or NULL on failure. */
|
|
static const char *
|
|
dlang_call_convention (string *decl, const char *mangled)
|
|
{
|
|
if (mangled == NULL || *mangled == '\0')
|
|
return NULL;
|
|
|
|
switch (*mangled)
|
|
{
|
|
case 'F': /* (D) */
|
|
mangled++;
|
|
break;
|
|
case 'U': /* (C) */
|
|
mangled++;
|
|
string_append (decl, "extern(C) ");
|
|
break;
|
|
case 'W': /* (Windows) */
|
|
mangled++;
|
|
string_append (decl, "extern(Windows) ");
|
|
break;
|
|
case 'V': /* (Pascal) */
|
|
mangled++;
|
|
string_append (decl, "extern(Pascal) ");
|
|
break;
|
|
case 'R': /* (C++) */
|
|
mangled++;
|
|
string_append (decl, "extern(C++) ");
|
|
break;
|
|
case 'Y': /* (Objective-C) */
|
|
mangled++;
|
|
string_append (decl, "extern(Objective-C) ");
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
return mangled;
|
|
}
|
|
|
|
/* Extract the type modifiers from MANGLED and append them to DECL.
|
|
Returns the remaining signature on success or NULL on failure. */
|
|
static const char *
|
|
dlang_type_modifiers (string *decl, const char *mangled)
|
|
{
|
|
if (mangled == NULL || *mangled == '\0')
|
|
return NULL;
|
|
|
|
switch (*mangled)
|
|
{
|
|
case 'x': /* const */
|
|
mangled++;
|
|
string_append (decl, " const");
|
|
return mangled;
|
|
case 'y': /* immutable */
|
|
mangled++;
|
|
string_append (decl, " immutable");
|
|
return mangled;
|
|
case 'O': /* shared */
|
|
mangled++;
|
|
string_append (decl, " shared");
|
|
return dlang_type_modifiers (decl, mangled);
|
|
case 'N':
|
|
mangled++;
|
|
if (*mangled == 'g') /* wild */
|
|
{
|
|
mangled++;
|
|
string_append (decl, " inout");
|
|
return dlang_type_modifiers (decl, mangled);
|
|
}
|
|
else
|
|
return NULL;
|
|
|
|
default:
|
|
return mangled;
|
|
}
|
|
}
|
|
|
|
/* Demangle the D function attributes from MANGLED and append it to DECL.
|
|
Return the remaining string on success or NULL on failure. */
|
|
static const char *
|
|
dlang_attributes (string *decl, const char *mangled)
|
|
{
|
|
if (mangled == NULL || *mangled == '\0')
|
|
return NULL;
|
|
|
|
while (*mangled == 'N')
|
|
{
|
|
mangled++;
|
|
switch (*mangled)
|
|
{
|
|
case 'a': /* pure */
|
|
mangled++;
|
|
string_append (decl, "pure ");
|
|
continue;
|
|
case 'b': /* nothrow */
|
|
mangled++;
|
|
string_append (decl, "nothrow ");
|
|
continue;
|
|
case 'c': /* ref */
|
|
mangled++;
|
|
string_append (decl, "ref ");
|
|
continue;
|
|
case 'd': /* @property */
|
|
mangled++;
|
|
string_append (decl, "@property ");
|
|
continue;
|
|
case 'e': /* @trusted */
|
|
mangled++;
|
|
string_append (decl, "@trusted ");
|
|
continue;
|
|
case 'f': /* @safe */
|
|
mangled++;
|
|
string_append (decl, "@safe ");
|
|
continue;
|
|
case 'g':
|
|
case 'h':
|
|
case 'k':
|
|
/* inout parameter is represented as 'Ng'.
|
|
vector parameter is represented as 'Nh'.
|
|
return paramenter is represented as 'Nk'.
|
|
If we see this, then we know we're really in the
|
|
parameter list. Rewind and break. */
|
|
mangled--;
|
|
break;
|
|
case 'i': /* @nogc */
|
|
mangled++;
|
|
string_append (decl, "@nogc ");
|
|
continue;
|
|
case 'j': /* return */
|
|
mangled++;
|
|
string_append (decl, "return ");
|
|
continue;
|
|
case 'l': /* scope */
|
|
mangled++;
|
|
string_append (decl, "scope ");
|
|
continue;
|
|
case 'm': /* @live */
|
|
mangled++;
|
|
string_append (decl, "@live ");
|
|
continue;
|
|
|
|
default: /* unknown attribute */
|
|
return NULL;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return mangled;
|
|
}
|
|
|
|
/* Demangle the function type from MANGLED without the return type.
|
|
The arguments are appended to ARGS, the calling convention is appended
|
|
to CALL and attributes are appended to ATTR. Any of these can be NULL
|
|
to throw the information away. Return the remaining string on success
|
|
or NULL on failure. */
|
|
static const char *
|
|
dlang_function_type_noreturn (string *args, string *call, string *attr,
|
|
const char *mangled, struct dlang_info *info)
|
|
{
|
|
string dump;
|
|
string_init (&dump);
|
|
|
|
/* Skip over calling convention and attributes. */
|
|
mangled = dlang_call_convention (call ? call : &dump, mangled);
|
|
mangled = dlang_attributes (attr ? attr : &dump, mangled);
|
|
|
|
if (args)
|
|
string_append (args, "(");
|
|
|
|
mangled = dlang_function_args (args ? args : &dump, mangled, info);
|
|
if (args)
|
|
string_append (args, ")");
|
|
|
|
string_delete (&dump);
|
|
return mangled;
|
|
}
|
|
|
|
/* Demangle the function type from MANGLED and append it to DECL.
|
|
Return the remaining string on success or NULL on failure. */
|
|
static const char *
|
|
dlang_function_type (string *decl, const char *mangled, struct dlang_info *info)
|
|
{
|
|
string attr, args, type;
|
|
|
|
if (mangled == NULL || *mangled == '\0')
|
|
return NULL;
|
|
|
|
/* The order of the mangled string is:
|
|
CallConvention FuncAttrs Arguments ArgClose Type
|
|
|
|
The demangled string is re-ordered as:
|
|
CallConvention Type Arguments FuncAttrs
|
|
*/
|
|
string_init (&attr);
|
|
string_init (&args);
|
|
string_init (&type);
|
|
|
|
mangled = dlang_function_type_noreturn (&args, decl, &attr, mangled, info);
|
|
|
|
/* Function return type. */
|
|
mangled = dlang_type (&type, mangled, info);
|
|
|
|
/* Append to decl in order. */
|
|
string_appendn (decl, type.b, string_length (&type));
|
|
string_appendn (decl, args.b, string_length (&args));
|
|
string_append (decl, " ");
|
|
string_appendn (decl, attr.b, string_length (&attr));
|
|
|
|
string_delete (&attr);
|
|
string_delete (&args);
|
|
string_delete (&type);
|
|
return mangled;
|
|
}
|
|
|
|
/* Demangle the argument list from MANGLED and append it to DECL.
|
|
Return the remaining string on success or NULL on failure. */
|
|
static const char *
|
|
dlang_function_args (string *decl, const char *mangled, struct dlang_info *info)
|
|
{
|
|
size_t n = 0;
|
|
|
|
while (mangled && *mangled != '\0')
|
|
{
|
|
switch (*mangled)
|
|
{
|
|
case 'X': /* (variadic T t...) style. */
|
|
mangled++;
|
|
string_append (decl, "...");
|
|
return mangled;
|
|
case 'Y': /* (variadic T t, ...) style. */
|
|
mangled++;
|
|
if (n != 0)
|
|
string_append (decl, ", ");
|
|
string_append (decl, "...");
|
|
return mangled;
|
|
case 'Z': /* Normal function. */
|
|
mangled++;
|
|
return mangled;
|
|
}
|
|
|
|
if (n++)
|
|
string_append (decl, ", ");
|
|
|
|
if (*mangled == 'M') /* scope(T) */
|
|
{
|
|
mangled++;
|
|
string_append (decl, "scope ");
|
|
}
|
|
|
|
if (mangled[0] == 'N' && mangled[1] == 'k') /* return(T) */
|
|
{
|
|
mangled += 2;
|
|
string_append (decl, "return ");
|
|
}
|
|
|
|
switch (*mangled)
|
|
{
|
|
case 'I': /* in(T) */
|
|
mangled++;
|
|
string_append (decl, "in ");
|
|
if (*mangled == 'K') /* in ref(T) */
|
|
{
|
|
mangled++;
|
|
string_append (decl, "ref ");
|
|
}
|
|
break;
|
|
case 'J': /* out(T) */
|
|
mangled++;
|
|
string_append (decl, "out ");
|
|
break;
|
|
case 'K': /* ref(T) */
|
|
mangled++;
|
|
string_append (decl, "ref ");
|
|
break;
|
|
case 'L': /* lazy(T) */
|
|
mangled++;
|
|
string_append (decl, "lazy ");
|
|
break;
|
|
}
|
|
mangled = dlang_type (decl, mangled, info);
|
|
}
|
|
|
|
return mangled;
|
|
}
|
|
|
|
/* Demangle the type from MANGLED and append it to DECL.
|
|
Return the remaining string on success or NULL on failure. */
|
|
static const char *
|
|
dlang_type (string *decl, const char *mangled, struct dlang_info *info)
|
|
{
|
|
if (mangled == NULL || *mangled == '\0')
|
|
return NULL;
|
|
|
|
switch (*mangled)
|
|
{
|
|
case 'O': /* shared(T) */
|
|
mangled++;
|
|
string_append (decl, "shared(");
|
|
mangled = dlang_type (decl, mangled, info);
|
|
string_append (decl, ")");
|
|
return mangled;
|
|
case 'x': /* const(T) */
|
|
mangled++;
|
|
string_append (decl, "const(");
|
|
mangled = dlang_type (decl, mangled, info);
|
|
string_append (decl, ")");
|
|
return mangled;
|
|
case 'y': /* immutable(T) */
|
|
mangled++;
|
|
string_append (decl, "immutable(");
|
|
mangled = dlang_type (decl, mangled, info);
|
|
string_append (decl, ")");
|
|
return mangled;
|
|
case 'N':
|
|
mangled++;
|
|
if (*mangled == 'g') /* wild(T) */
|
|
{
|
|
mangled++;
|
|
string_append (decl, "inout(");
|
|
mangled = dlang_type (decl, mangled, info);
|
|
string_append (decl, ")");
|
|
return mangled;
|
|
}
|
|
else if (*mangled == 'h') /* vector(T) */
|
|
{
|
|
mangled++;
|
|
string_append (decl, "__vector(");
|
|
mangled = dlang_type (decl, mangled, info);
|
|
string_append (decl, ")");
|
|
return mangled;
|
|
}
|
|
else
|
|
return NULL;
|
|
case 'A': /* dynamic array (T[]) */
|
|
mangled++;
|
|
mangled = dlang_type (decl, mangled, info);
|
|
string_append (decl, "[]");
|
|
return mangled;
|
|
case 'G': /* static array (T[N]) */
|
|
{
|
|
const char *numptr;
|
|
size_t num = 0;
|
|
mangled++;
|
|
|
|
numptr = mangled;
|
|
while (ISDIGIT (*mangled))
|
|
{
|
|
num++;
|
|
mangled++;
|
|
}
|
|
mangled = dlang_type (decl, mangled, info);
|
|
string_append (decl, "[");
|
|
string_appendn (decl, numptr, num);
|
|
string_append (decl, "]");
|
|
return mangled;
|
|
}
|
|
case 'H': /* associative array (T[T]) */
|
|
{
|
|
string type;
|
|
size_t sztype;
|
|
mangled++;
|
|
|
|
string_init (&type);
|
|
mangled = dlang_type (&type, mangled, info);
|
|
sztype = string_length (&type);
|
|
|
|
mangled = dlang_type (decl, mangled, info);
|
|
string_append (decl, "[");
|
|
string_appendn (decl, type.b, sztype);
|
|
string_append (decl, "]");
|
|
|
|
string_delete (&type);
|
|
return mangled;
|
|
}
|
|
case 'P': /* pointer (T*) */
|
|
mangled++;
|
|
if (!dlang_call_convention_p (mangled))
|
|
{
|
|
mangled = dlang_type (decl, mangled, info);
|
|
string_append (decl, "*");
|
|
return mangled;
|
|
}
|
|
/* Fall through */
|
|
case 'F': /* function T (D) */
|
|
case 'U': /* function T (C) */
|
|
case 'W': /* function T (Windows) */
|
|
case 'V': /* function T (Pascal) */
|
|
case 'R': /* function T (C++) */
|
|
case 'Y': /* function T (Objective-C) */
|
|
/* Function pointer types don't include the trailing asterisk. */
|
|
mangled = dlang_function_type (decl, mangled, info);
|
|
string_append (decl, "function");
|
|
return mangled;
|
|
case 'C': /* class T */
|
|
case 'S': /* struct T */
|
|
case 'E': /* enum T */
|
|
case 'T': /* typedef T */
|
|
mangled++;
|
|
return dlang_parse_qualified (decl, mangled, info, 0);
|
|
case 'D': /* delegate T */
|
|
{
|
|
string mods;
|
|
size_t szmods;
|
|
mangled++;
|
|
|
|
string_init (&mods);
|
|
mangled = dlang_type_modifiers (&mods, mangled);
|
|
szmods = string_length (&mods);
|
|
|
|
/* Back referenced function type. */
|
|
if (*mangled == 'Q')
|
|
mangled = dlang_type_backref (decl, mangled, info, 1);
|
|
else
|
|
mangled = dlang_function_type (decl, mangled, info);
|
|
|
|
string_append (decl, "delegate");
|
|
string_appendn (decl, mods.b, szmods);
|
|
|
|
string_delete (&mods);
|
|
return mangled;
|
|
}
|
|
case 'B': /* tuple T */
|
|
mangled++;
|
|
return dlang_parse_tuple (decl, mangled, info);
|
|
|
|
/* Basic types */
|
|
case 'n':
|
|
mangled++;
|
|
string_append (decl, "none");
|
|
return mangled;
|
|
case 'v':
|
|
mangled++;
|
|
string_append (decl, "void");
|
|
return mangled;
|
|
case 'g':
|
|
mangled++;
|
|
string_append (decl, "byte");
|
|
return mangled;
|
|
case 'h':
|
|
mangled++;
|
|
string_append (decl, "ubyte");
|
|
return mangled;
|
|
case 's':
|
|
mangled++;
|
|
string_append (decl, "short");
|
|
return mangled;
|
|
case 't':
|
|
mangled++;
|
|
string_append (decl, "ushort");
|
|
return mangled;
|
|
case 'i':
|
|
mangled++;
|
|
string_append (decl, "int");
|
|
return mangled;
|
|
case 'k':
|
|
mangled++;
|
|
string_append (decl, "uint");
|
|
return mangled;
|
|
case 'l':
|
|
mangled++;
|
|
string_append (decl, "long");
|
|
return mangled;
|
|
case 'm':
|
|
mangled++;
|
|
string_append (decl, "ulong");
|
|
return mangled;
|
|
case 'f':
|
|
mangled++;
|
|
string_append (decl, "float");
|
|
return mangled;
|
|
case 'd':
|
|
mangled++;
|
|
string_append (decl, "double");
|
|
return mangled;
|
|
case 'e':
|
|
mangled++;
|
|
string_append (decl, "real");
|
|
return mangled;
|
|
|
|
/* Imaginary and Complex types */
|
|
case 'o':
|
|
mangled++;
|
|
string_append (decl, "ifloat");
|
|
return mangled;
|
|
case 'p':
|
|
mangled++;
|
|
string_append (decl, "idouble");
|
|
return mangled;
|
|
case 'j':
|
|
mangled++;
|
|
string_append (decl, "ireal");
|
|
return mangled;
|
|
case 'q':
|
|
mangled++;
|
|
string_append (decl, "cfloat");
|
|
return mangled;
|
|
case 'r':
|
|
mangled++;
|
|
string_append (decl, "cdouble");
|
|
return mangled;
|
|
case 'c':
|
|
mangled++;
|
|
string_append (decl, "creal");
|
|
return mangled;
|
|
|
|
/* Other types */
|
|
case 'b':
|
|
mangled++;
|
|
string_append (decl, "bool");
|
|
return mangled;
|
|
case 'a':
|
|
mangled++;
|
|
string_append (decl, "char");
|
|
return mangled;
|
|
case 'u':
|
|
mangled++;
|
|
string_append (decl, "wchar");
|
|
return mangled;
|
|
case 'w':
|
|
mangled++;
|
|
string_append (decl, "dchar");
|
|
return mangled;
|
|
case 'z':
|
|
mangled++;
|
|
switch (*mangled)
|
|
{
|
|
case 'i':
|
|
mangled++;
|
|
string_append (decl, "cent");
|
|
return mangled;
|
|
case 'k':
|
|
mangled++;
|
|
string_append (decl, "ucent");
|
|
return mangled;
|
|
}
|
|
return NULL;
|
|
|
|
/* Back referenced type. */
|
|
case 'Q':
|
|
return dlang_type_backref (decl, mangled, info, 0);
|
|
|
|
default: /* unhandled */
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Extract the identifier from MANGLED and append it to DECL.
|
|
Return the remaining string on success or NULL on failure. */
|
|
static const char *
|
|
dlang_identifier (string *decl, const char *mangled, struct dlang_info *info)
|
|
{
|
|
long len;
|
|
|
|
if (mangled == NULL || *mangled == '\0')
|
|
return NULL;
|
|
|
|
if (*mangled == 'Q')
|
|
return dlang_symbol_backref (decl, mangled, info);
|
|
|
|
/* May be a template instance without a length prefix. */
|
|
if (mangled[0] == '_' && mangled[1] == '_'
|
|
&& (mangled[2] == 'T' || mangled[2] == 'U'))
|
|
return dlang_parse_template (decl, mangled, info, TEMPLATE_LENGTH_UNKNOWN);
|
|
|
|
const char *endptr = dlang_number (mangled, &len);
|
|
|
|
if (endptr == NULL || len == 0)
|
|
return NULL;
|
|
|
|
if (strlen (endptr) < (size_t) len)
|
|
return NULL;
|
|
|
|
mangled = endptr;
|
|
|
|
/* May be a template instance with a length prefix. */
|
|
if (len >= 5 && mangled[0] == '_' && mangled[1] == '_'
|
|
&& (mangled[2] == 'T' || mangled[2] == 'U'))
|
|
return dlang_parse_template (decl, mangled, info, len);
|
|
|
|
return dlang_lname (decl, mangled, len);
|
|
}
|
|
|
|
/* Extract the plain identifier from MANGLED and prepend/append it to DECL
|
|
with special treatment for some magic compiler generted symbols.
|
|
Return the remaining string on success or NULL on failure. */
|
|
static const char *
|
|
dlang_lname (string *decl, const char *mangled, long len)
|
|
{
|
|
switch (len)
|
|
{
|
|
case 6:
|
|
if (strncmp (mangled, "__ctor", len) == 0)
|
|
{
|
|
/* Constructor symbol for a class/struct. */
|
|
string_append (decl, "this");
|
|
mangled += len;
|
|
return mangled;
|
|
}
|
|
else if (strncmp (mangled, "__dtor", len) == 0)
|
|
{
|
|
/* Destructor symbol for a class/struct. */
|
|
string_append (decl, "~this");
|
|
mangled += len;
|
|
return mangled;
|
|
}
|
|
else if (strncmp (mangled, "__initZ", len + 1) == 0)
|
|
{
|
|
/* The static initialiser for a given symbol. */
|
|
string_prepend (decl, "initializer for ");
|
|
string_setlength (decl, string_length (decl) - 1);
|
|
mangled += len;
|
|
return mangled;
|
|
}
|
|
else if (strncmp (mangled, "__vtblZ", len + 1) == 0)
|
|
{
|
|
/* The vtable symbol for a given class. */
|
|
string_prepend (decl, "vtable for ");
|
|
string_setlength (decl, string_length (decl) - 1);
|
|
mangled += len;
|
|
return mangled;
|
|
}
|
|
break;
|
|
|
|
case 7:
|
|
if (strncmp (mangled, "__ClassZ", len + 1) == 0)
|
|
{
|
|
/* The classinfo symbol for a given class. */
|
|
string_prepend (decl, "ClassInfo for ");
|
|
string_setlength (decl, string_length (decl) - 1);
|
|
mangled += len;
|
|
return mangled;
|
|
}
|
|
break;
|
|
|
|
case 10:
|
|
if (strncmp (mangled, "__postblitMFZ", len + 3) == 0)
|
|
{
|
|
/* Postblit symbol for a struct. */
|
|
string_append (decl, "this(this)");
|
|
mangled += len + 3;
|
|
return mangled;
|
|
}
|
|
break;
|
|
|
|
case 11:
|
|
if (strncmp (mangled, "__InterfaceZ", len + 1) == 0)
|
|
{
|
|
/* The interface symbol for a given class. */
|
|
string_prepend (decl, "Interface for ");
|
|
string_setlength (decl, string_length (decl) - 1);
|
|
mangled += len;
|
|
return mangled;
|
|
}
|
|
break;
|
|
|
|
case 12:
|
|
if (strncmp (mangled, "__ModuleInfoZ", len + 1) == 0)
|
|
{
|
|
/* The ModuleInfo symbol for a given module. */
|
|
string_prepend (decl, "ModuleInfo for ");
|
|
string_setlength (decl, string_length (decl) - 1);
|
|
mangled += len;
|
|
return mangled;
|
|
}
|
|
break;
|
|
}
|
|
|
|
string_appendn (decl, mangled, len);
|
|
mangled += len;
|
|
|
|
return mangled;
|
|
}
|
|
|
|
/* Extract the integer value from MANGLED and append it to DECL,
|
|
where TYPE is the type it should be represented as.
|
|
Return the remaining string on success or NULL on failure. */
|
|
static const char *
|
|
dlang_parse_integer (string *decl, const char *mangled, char type)
|
|
{
|
|
if (type == 'a' || type == 'u' || type == 'w')
|
|
{
|
|
/* Parse character value. */
|
|
char value[20];
|
|
int pos = sizeof(value);
|
|
int width = 0;
|
|
long val;
|
|
|
|
mangled = dlang_number (mangled, &val);
|
|
if (mangled == NULL)
|
|
return NULL;
|
|
|
|
string_append (decl, "'");
|
|
|
|
if (type == 'a' && val >= 0x20 && val < 0x7F)
|
|
{
|
|
/* Represent as a character literal. */
|
|
char c = (char) val;
|
|
string_appendn (decl, &c, 1);
|
|
}
|
|
else
|
|
{
|
|
/* Represent as a hexadecimal value. */
|
|
switch (type)
|
|
{
|
|
case 'a': /* char */
|
|
string_append (decl, "\\x");
|
|
width = 2;
|
|
break;
|
|
case 'u': /* wchar */
|
|
string_append (decl, "\\u");
|
|
width = 4;
|
|
break;
|
|
case 'w': /* dchar */
|
|
string_append (decl, "\\U");
|
|
width = 8;
|
|
break;
|
|
}
|
|
|
|
while (val > 0)
|
|
{
|
|
int digit = val % 16;
|
|
|
|
if (digit < 10)
|
|
value[--pos] = (char)(digit + '0');
|
|
else
|
|
value[--pos] = (char)((digit - 10) + 'a');
|
|
|
|
val /= 16;
|
|
width--;
|
|
}
|
|
|
|
for (; width > 0; width--)
|
|
value[--pos] = '0';
|
|
|
|
string_appendn (decl, &(value[pos]), sizeof(value) - pos);
|
|
}
|
|
string_append (decl, "'");
|
|
}
|
|
else if (type == 'b')
|
|
{
|
|
/* Parse boolean value. */
|
|
long val;
|
|
|
|
mangled = dlang_number (mangled, &val);
|
|
if (mangled == NULL)
|
|
return NULL;
|
|
|
|
string_append (decl, val ? "true" : "false");
|
|
}
|
|
else
|
|
{
|
|
/* Parse integer value. */
|
|
const char *numptr = mangled;
|
|
size_t num = 0;
|
|
|
|
if (! ISDIGIT (*mangled))
|
|
return NULL;
|
|
|
|
while (ISDIGIT (*mangled))
|
|
{
|
|
num++;
|
|
mangled++;
|
|
}
|
|
string_appendn (decl, numptr, num);
|
|
|
|
/* Append suffix. */
|
|
switch (type)
|
|
{
|
|
case 'h': /* ubyte */
|
|
case 't': /* ushort */
|
|
case 'k': /* uint */
|
|
string_append (decl, "u");
|
|
break;
|
|
case 'l': /* long */
|
|
string_append (decl, "L");
|
|
break;
|
|
case 'm': /* ulong */
|
|
string_append (decl, "uL");
|
|
break;
|
|
}
|
|
}
|
|
|
|
return mangled;
|
|
}
|
|
|
|
/* Extract the floating-point value from MANGLED and append it to DECL.
|
|
Return the remaining string on success or NULL on failure. */
|
|
static const char *
|
|
dlang_parse_real (string *decl, const char *mangled)
|
|
{
|
|
/* Handle NAN and +-INF. */
|
|
if (strncmp (mangled, "NAN", 3) == 0)
|
|
{
|
|
string_append (decl, "NaN");
|
|
mangled += 3;
|
|
return mangled;
|
|
}
|
|
else if (strncmp (mangled, "INF", 3) == 0)
|
|
{
|
|
string_append (decl, "Inf");
|
|
mangled += 3;
|
|
return mangled;
|
|
}
|
|
else if (strncmp (mangled, "NINF", 4) == 0)
|
|
{
|
|
string_append (decl, "-Inf");
|
|
mangled += 4;
|
|
return mangled;
|
|
}
|
|
|
|
/* Hexadecimal prefix and leading bit. */
|
|
if (*mangled == 'N')
|
|
{
|
|
string_append (decl, "-");
|
|
mangled++;
|
|
}
|
|
|
|
if (!ISXDIGIT (*mangled))
|
|
return NULL;
|
|
|
|
string_append (decl, "0x");
|
|
string_appendn (decl, mangled, 1);
|
|
string_append (decl, ".");
|
|
mangled++;
|
|
|
|
/* Significand. */
|
|
while (ISXDIGIT (*mangled))
|
|
{
|
|
string_appendn (decl, mangled, 1);
|
|
mangled++;
|
|
}
|
|
|
|
/* Exponent. */
|
|
if (*mangled != 'P')
|
|
return NULL;
|
|
|
|
string_append (decl, "p");
|
|
mangled++;
|
|
|
|
if (*mangled == 'N')
|
|
{
|
|
string_append (decl, "-");
|
|
mangled++;
|
|
}
|
|
|
|
while (ISDIGIT (*mangled))
|
|
{
|
|
string_appendn (decl, mangled, 1);
|
|
mangled++;
|
|
}
|
|
|
|
return mangled;
|
|
}
|
|
|
|
/* Extract the string value from MANGLED and append it to DECL.
|
|
Return the remaining string on success or NULL on failure. */
|
|
static const char *
|
|
dlang_parse_string (string *decl, const char *mangled)
|
|
{
|
|
char type = *mangled;
|
|
long len;
|
|
|
|
mangled++;
|
|
mangled = dlang_number (mangled, &len);
|
|
if (mangled == NULL || *mangled != '_')
|
|
return NULL;
|
|
|
|
mangled++;
|
|
string_append (decl, "\"");
|
|
while (len--)
|
|
{
|
|
char val;
|
|
const char *endptr = dlang_hexdigit (mangled, &val);
|
|
|
|
if (endptr == NULL)
|
|
return NULL;
|
|
|
|
/* Sanitize white and non-printable characters. */
|
|
switch (val)
|
|
{
|
|
case ' ':
|
|
string_append (decl, " ");
|
|
break;
|
|
case '\t':
|
|
string_append (decl, "\\t");
|
|
break;
|
|
case '\n':
|
|
string_append (decl, "\\n");
|
|
break;
|
|
case '\r':
|
|
string_append (decl, "\\r");
|
|
break;
|
|
case '\f':
|
|
string_append (decl, "\\f");
|
|
break;
|
|
case '\v':
|
|
string_append (decl, "\\v");
|
|
break;
|
|
|
|
default:
|
|
if (ISPRINT (val))
|
|
string_appendn (decl, &val, 1);
|
|
else
|
|
{
|
|
string_append (decl, "\\x");
|
|
string_appendn (decl, mangled, 2);
|
|
}
|
|
}
|
|
|
|
mangled = endptr;
|
|
}
|
|
string_append (decl, "\"");
|
|
|
|
if (type != 'a')
|
|
string_appendn (decl, &type, 1);
|
|
|
|
return mangled;
|
|
}
|
|
|
|
/* Extract the static array value from MANGLED and append it to DECL.
|
|
Return the remaining string on success or NULL on failure. */
|
|
static const char *
|
|
dlang_parse_arrayliteral (string *decl, const char *mangled)
|
|
{
|
|
long elements;
|
|
|
|
mangled = dlang_number (mangled, &elements);
|
|
if (mangled == NULL)
|
|
return NULL;
|
|
|
|
string_append (decl, "[");
|
|
while (elements--)
|
|
{
|
|
mangled = dlang_value (decl, mangled, NULL, '\0');
|
|
if (mangled == NULL)
|
|
return NULL;
|
|
|
|
if (elements != 0)
|
|
string_append (decl, ", ");
|
|
}
|
|
|
|
string_append (decl, "]");
|
|
return mangled;
|
|
}
|
|
|
|
/* Extract the associative array value from MANGLED and append it to DECL.
|
|
Return the remaining string on success or NULL on failure. */
|
|
static const char *
|
|
dlang_parse_assocarray (string *decl, const char *mangled)
|
|
{
|
|
long elements;
|
|
|
|
mangled = dlang_number (mangled, &elements);
|
|
if (mangled == NULL)
|
|
return NULL;
|
|
|
|
string_append (decl, "[");
|
|
while (elements--)
|
|
{
|
|
mangled = dlang_value (decl, mangled, NULL, '\0');
|
|
if (mangled == NULL)
|
|
return NULL;
|
|
|
|
string_append (decl, ":");
|
|
mangled = dlang_value (decl, mangled, NULL, '\0');
|
|
if (mangled == NULL)
|
|
return NULL;
|
|
|
|
if (elements != 0)
|
|
string_append (decl, ", ");
|
|
}
|
|
|
|
string_append (decl, "]");
|
|
return mangled;
|
|
}
|
|
|
|
/* Extract the struct literal value for NAME from MANGLED and append it to DECL.
|
|
Return the remaining string on success or NULL on failure. */
|
|
static const char *
|
|
dlang_parse_structlit (string *decl, const char *mangled, const char *name)
|
|
{
|
|
long args;
|
|
|
|
mangled = dlang_number (mangled, &args);
|
|
if (mangled == NULL)
|
|
return NULL;
|
|
|
|
if (name != NULL)
|
|
string_append (decl, name);
|
|
|
|
string_append (decl, "(");
|
|
while (args--)
|
|
{
|
|
mangled = dlang_value (decl, mangled, NULL, '\0');
|
|
if (mangled == NULL)
|
|
return NULL;
|
|
|
|
if (args != 0)
|
|
string_append (decl, ", ");
|
|
}
|
|
|
|
string_append (decl, ")");
|
|
return mangled;
|
|
}
|
|
|
|
/* Extract the value from MANGLED and append it to DECL.
|
|
Return the remaining string on success or NULL on failure. */
|
|
static const char *
|
|
dlang_value (string *decl, const char *mangled, const char *name, char type)
|
|
{
|
|
if (mangled == NULL || *mangled == '\0')
|
|
return NULL;
|
|
|
|
switch (*mangled)
|
|
{
|
|
/* Null value. */
|
|
case 'n':
|
|
mangled++;
|
|
string_append (decl, "null");
|
|
break;
|
|
|
|
/* Integral values. */
|
|
case 'N':
|
|
mangled++;
|
|
string_append (decl, "-");
|
|
mangled = dlang_parse_integer (decl, mangled, type);
|
|
break;
|
|
|
|
case 'i':
|
|
mangled++;
|
|
/* Fall through */
|
|
|
|
/* There really should always be an `i' before encoded numbers, but there
|
|
wasn't in early versions of D2, so this case range must remain for
|
|
backwards compatibility. */
|
|
case '0': case '1': case '2': case '3': case '4':
|
|
case '5': case '6': case '7': case '8': case '9':
|
|
mangled = dlang_parse_integer (decl, mangled, type);
|
|
break;
|
|
|
|
/* Real value. */
|
|
case 'e':
|
|
mangled++;
|
|
mangled = dlang_parse_real (decl, mangled);
|
|
break;
|
|
|
|
/* Complex value. */
|
|
case 'c':
|
|
mangled++;
|
|
mangled = dlang_parse_real (decl, mangled);
|
|
string_append (decl, "+");
|
|
if (mangled == NULL || *mangled != 'c')
|
|
return NULL;
|
|
mangled++;
|
|
mangled = dlang_parse_real (decl, mangled);
|
|
string_append (decl, "i");
|
|
break;
|
|
|
|
/* String values. */
|
|
case 'a': /* UTF8 */
|
|
case 'w': /* UTF16 */
|
|
case 'd': /* UTF32 */
|
|
mangled = dlang_parse_string (decl, mangled);
|
|
break;
|
|
|
|
/* Array values. */
|
|
case 'A':
|
|
mangled++;
|
|
if (type == 'H')
|
|
mangled = dlang_parse_assocarray (decl, mangled);
|
|
else
|
|
mangled = dlang_parse_arrayliteral (decl, mangled);
|
|
break;
|
|
|
|
/* Struct values. */
|
|
case 'S':
|
|
mangled++;
|
|
mangled = dlang_parse_structlit (decl, mangled, name);
|
|
break;
|
|
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
return mangled;
|
|
}
|
|
|
|
/* Extract and demangle the symbol in MANGLED and append it to DECL.
|
|
Returns the remaining signature on success or NULL on failure. */
|
|
static const char *
|
|
dlang_parse_mangle (string *decl, const char *mangled, struct dlang_info *info)
|
|
{
|
|
/* A D mangled symbol is comprised of both scope and type information.
|
|
|
|
MangleName:
|
|
_D QualifiedName Type
|
|
_D QualifiedName Z
|
|
^
|
|
The caller should have guaranteed that the start pointer is at the
|
|
above location.
|
|
Note that type is never a function type, but only the return type of
|
|
a function or the type of a variable.
|
|
*/
|
|
mangled += 2;
|
|
|
|
mangled = dlang_parse_qualified (decl, mangled, info, 1);
|
|
|
|
if (mangled != NULL)
|
|
{
|
|
/* Artificial symbols end with 'Z' and have no type. */
|
|
if (*mangled == 'Z')
|
|
mangled++;
|
|
else
|
|
{
|
|
/* Discard the declaration or return type. */
|
|
string type;
|
|
|
|
string_init (&type);
|
|
mangled = dlang_type (&type, mangled, info);
|
|
string_delete (&type);
|
|
}
|
|
}
|
|
|
|
return mangled;
|
|
}
|
|
|
|
/* Extract and demangle the qualified symbol in MANGLED and append it to DECL.
|
|
SUFFIX_MODIFIERS is 1 if we are printing modifiers on this after the symbol.
|
|
Returns the remaining signature on success or NULL on failure. */
|
|
static const char *
|
|
dlang_parse_qualified (string *decl, const char *mangled,
|
|
struct dlang_info *info, int suffix_modifiers)
|
|
{
|
|
/* Qualified names are identifiers separated by their encoded length.
|
|
Nested functions also encode their argument types without specifying
|
|
what they return.
|
|
|
|
QualifiedName:
|
|
SymbolFunctionName
|
|
SymbolFunctionName QualifiedName
|
|
^
|
|
|
|
SymbolFunctionName:
|
|
SymbolName
|
|
SymbolName TypeFunctionNoReturn
|
|
SymbolName M TypeFunctionNoReturn
|
|
SymbolName M TypeModifiers TypeFunctionNoReturn
|
|
|
|
The start pointer should be at the above location.
|
|
*/
|
|
size_t n = 0;
|
|
do
|
|
{
|
|
if (n++)
|
|
string_append (decl, ".");
|
|
|
|
/* Skip over anonymous symbols. */
|
|
while (*mangled == '0')
|
|
mangled++;
|
|
|
|
mangled = dlang_identifier (decl, mangled, info);
|
|
|
|
/* Consume the encoded arguments. However if this is not followed by the
|
|
next encoded length or mangle type, then this is not a continuation of
|
|
a qualified name, in which case we backtrack and return the current
|
|
unconsumed position of the mangled decl. */
|
|
if (mangled && (*mangled == 'M' || dlang_call_convention_p (mangled)))
|
|
{
|
|
string mods;
|
|
const char *start = mangled;
|
|
int saved = string_length (decl);
|
|
|
|
/* Save the type modifiers for appending at the end if needed. */
|
|
string_init (&mods);
|
|
|
|
/* Skip over 'this' parameter and type modifiers. */
|
|
if (*mangled == 'M')
|
|
{
|
|
mangled++;
|
|
mangled = dlang_type_modifiers (&mods, mangled);
|
|
string_setlength (decl, saved);
|
|
}
|
|
|
|
mangled = dlang_function_type_noreturn (decl, NULL, NULL,
|
|
mangled, info);
|
|
if (suffix_modifiers)
|
|
string_appendn (decl, mods.b, string_length (&mods));
|
|
|
|
if (mangled == NULL || *mangled == '\0')
|
|
{
|
|
/* Did not match the rule we were looking for. */
|
|
mangled = start;
|
|
string_setlength (decl, saved);
|
|
}
|
|
|
|
string_delete (&mods);
|
|
}
|
|
}
|
|
while (mangled && dlang_symbol_name_p (mangled, info));
|
|
|
|
return mangled;
|
|
}
|
|
|
|
/* Demangle the tuple from MANGLED and append it to DECL.
|
|
Return the remaining string on success or NULL on failure. */
|
|
static const char *
|
|
dlang_parse_tuple (string *decl, const char *mangled, struct dlang_info *info)
|
|
{
|
|
long elements;
|
|
|
|
mangled = dlang_number (mangled, &elements);
|
|
if (mangled == NULL)
|
|
return NULL;
|
|
|
|
string_append (decl, "Tuple!(");
|
|
|
|
while (elements--)
|
|
{
|
|
mangled = dlang_type (decl, mangled, info);
|
|
if (mangled == NULL)
|
|
return NULL;
|
|
|
|
if (elements != 0)
|
|
string_append (decl, ", ");
|
|
}
|
|
|
|
string_append (decl, ")");
|
|
return mangled;
|
|
}
|
|
|
|
/* Demangle the template symbol parameter from MANGLED and append it to DECL.
|
|
Return the remaining string on success or NULL on failure. */
|
|
static const char *
|
|
dlang_template_symbol_param (string *decl, const char *mangled,
|
|
struct dlang_info *info)
|
|
{
|
|
if (strncmp (mangled, "_D", 2) == 0
|
|
&& dlang_symbol_name_p (mangled + 2, info))
|
|
return dlang_parse_mangle (decl, mangled, info);
|
|
|
|
if (*mangled == 'Q')
|
|
return dlang_parse_qualified (decl, mangled, info, 0);
|
|
|
|
long len;
|
|
const char *endptr = dlang_number (mangled, &len);
|
|
|
|
if (endptr == NULL || len == 0)
|
|
return NULL;
|
|
|
|
/* In template parameter symbols generated by the frontend up to 2.076,
|
|
the symbol length is encoded and the first character of the mangled
|
|
name can be a digit. This causes ambiguity issues because the digits
|
|
of the two numbers are adjacent. */
|
|
long psize = len;
|
|
const char *pend;
|
|
int saved = string_length (decl);
|
|
|
|
/* Work backwards until a match is found. */
|
|
for (pend = endptr; endptr != NULL; pend--)
|
|
{
|
|
mangled = pend;
|
|
|
|
/* Reached the beginning of the pointer to the name length,
|
|
try parsing the entire symbol. */
|
|
if (psize == 0)
|
|
{
|
|
psize = len;
|
|
pend = endptr;
|
|
endptr = NULL;
|
|
}
|
|
|
|
/* Check whether template parameter is a function with a valid
|
|
return type or an untyped identifier. */
|
|
if (dlang_symbol_name_p (mangled, info))
|
|
mangled = dlang_parse_qualified (decl, mangled, info, 0);
|
|
else if (strncmp (mangled, "_D", 2) == 0
|
|
&& dlang_symbol_name_p (mangled + 2, info))
|
|
mangled = dlang_parse_mangle (decl, mangled, info);
|
|
|
|
/* Check for name length mismatch. */
|
|
if (mangled && (endptr == NULL || (mangled - pend) == psize))
|
|
return mangled;
|
|
|
|
psize /= 10;
|
|
string_setlength (decl, saved);
|
|
}
|
|
|
|
/* No match on any combinations. */
|
|
return NULL;
|
|
}
|
|
|
|
/* Demangle the argument list from MANGLED and append it to DECL.
|
|
Return the remaining string on success or NULL on failure. */
|
|
static const char *
|
|
dlang_template_args (string *decl, const char *mangled, struct dlang_info *info)
|
|
{
|
|
size_t n = 0;
|
|
|
|
while (mangled && *mangled != '\0')
|
|
{
|
|
switch (*mangled)
|
|
{
|
|
case 'Z': /* End of parameter list. */
|
|
mangled++;
|
|
return mangled;
|
|
}
|
|
|
|
if (n++)
|
|
string_append (decl, ", ");
|
|
|
|
/* Skip over specialised template prefix. */
|
|
if (*mangled == 'H')
|
|
mangled++;
|
|
|
|
switch (*mangled)
|
|
{
|
|
case 'S': /* Symbol parameter. */
|
|
mangled++;
|
|
mangled = dlang_template_symbol_param (decl, mangled, info);
|
|
break;
|
|
case 'T': /* Type parameter. */
|
|
mangled++;
|
|
mangled = dlang_type (decl, mangled, info);
|
|
break;
|
|
case 'V': /* Value parameter. */
|
|
{
|
|
string name;
|
|
char type;
|
|
|
|
/* Peek at the type. */
|
|
mangled++;
|
|
type = *mangled;
|
|
|
|
if (type == 'Q')
|
|
{
|
|
/* Value type is a back reference, peek at the real type. */
|
|
const char *backref;
|
|
if (dlang_backref (mangled, &backref, info) == NULL)
|
|
return NULL;
|
|
|
|
type = *backref;
|
|
}
|
|
|
|
/* In the few instances where the type is actually desired in
|
|
the output, it should precede the value from dlang_value. */
|
|
string_init (&name);
|
|
mangled = dlang_type (&name, mangled, info);
|
|
string_need (&name, 1);
|
|
*(name.p) = '\0';
|
|
|
|
mangled = dlang_value (decl, mangled, name.b, type);
|
|
string_delete (&name);
|
|
break;
|
|
}
|
|
case 'X': /* Externally mangled parameter. */
|
|
{
|
|
long len;
|
|
const char *endptr;
|
|
|
|
mangled++;
|
|
endptr = dlang_number (mangled, &len);
|
|
if (endptr == NULL || strlen (endptr) < (size_t) len)
|
|
return NULL;
|
|
|
|
string_appendn (decl, endptr, len);
|
|
mangled = endptr + len;
|
|
break;
|
|
}
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return mangled;
|
|
}
|
|
|
|
/* Extract and demangle the template symbol in MANGLED, expected to
|
|
be made up of LEN characters (-1 if unknown), and append it to DECL.
|
|
Returns the remaining signature on success or NULL on failure. */
|
|
static const char *
|
|
dlang_parse_template (string *decl, const char *mangled,
|
|
struct dlang_info *info, long len)
|
|
{
|
|
const char *start = mangled;
|
|
string args;
|
|
|
|
/* Template instance names have the types and values of its parameters
|
|
encoded into it.
|
|
|
|
TemplateInstanceName:
|
|
Number __T LName TemplateArgs Z
|
|
Number __U LName TemplateArgs Z
|
|
^
|
|
The start pointer should be at the above location, and LEN should be
|
|
the value of the decoded number.
|
|
*/
|
|
|
|
/* Template symbol. */
|
|
if (!dlang_symbol_name_p (mangled + 3, info) || mangled[3] == '0')
|
|
return NULL;
|
|
|
|
mangled += 3;
|
|
|
|
/* Template identifier. */
|
|
mangled = dlang_identifier (decl, mangled, info);
|
|
|
|
/* Template arguments. */
|
|
string_init (&args);
|
|
mangled = dlang_template_args (&args, mangled, info);
|
|
|
|
string_append (decl, "!(");
|
|
string_appendn (decl, args.b, string_length (&args));
|
|
string_append (decl, ")");
|
|
|
|
string_delete (&args);
|
|
|
|
/* Check for template name length mismatch. */
|
|
if (len != TEMPLATE_LENGTH_UNKNOWN && mangled && (mangled - start) != len)
|
|
return NULL;
|
|
|
|
return mangled;
|
|
}
|
|
|
|
/* Initialize the information structure we use to pass around information. */
|
|
static void
|
|
dlang_demangle_init_info (const char *mangled, int last_backref,
|
|
struct dlang_info *info)
|
|
{
|
|
info->s = mangled;
|
|
info->last_backref = last_backref;
|
|
}
|
|
|
|
/* Extract and demangle the symbol in MANGLED. Returns the demangled
|
|
signature on success or NULL on failure. */
|
|
|
|
char *
|
|
dlang_demangle (const char *mangled, int option ATTRIBUTE_UNUSED)
|
|
{
|
|
string decl;
|
|
char *demangled = NULL;
|
|
|
|
if (mangled == NULL || *mangled == '\0')
|
|
return NULL;
|
|
|
|
if (strncmp (mangled, "_D", 2) != 0)
|
|
return NULL;
|
|
|
|
string_init (&decl);
|
|
|
|
if (strcmp (mangled, "_Dmain") == 0)
|
|
{
|
|
string_append (&decl, "D main");
|
|
}
|
|
else
|
|
{
|
|
struct dlang_info info;
|
|
|
|
dlang_demangle_init_info (mangled, strlen (mangled), &info);
|
|
mangled = dlang_parse_mangle (&decl, mangled, &info);
|
|
|
|
/* Check that the entire symbol was successfully demangled. */
|
|
if (mangled == NULL || *mangled != '\0')
|
|
string_delete (&decl);
|
|
}
|
|
|
|
if (string_length (&decl) > 0)
|
|
{
|
|
string_need (&decl, 1);
|
|
*(decl.p) = '\0';
|
|
demangled = decl.b;
|
|
}
|
|
|
|
return demangled;
|
|
}
|
|
|