Mercurial > hg > CbC > CbC_gcc
diff libiberty/d-demangle.c @ 111:04ced10e8804
gcc 7
author | kono |
---|---|
date | Fri, 27 Oct 2017 22:46:09 +0900 |
parents | |
children | 84e7813d76e9 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libiberty/d-demangle.c Fri Oct 27 22:46:09 2017 +0900 @@ -0,0 +1,1646 @@ +/* Demangler for the D programming language + Copyright (C) 2014-2017 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)); + } +} + +/* What kinds of symbol we could be parsing. */ +enum dlang_symbol_kinds +{ + /* Top-level symbol, needs it's type checked. */ + dlang_top_level, + /* Function symbol, needs it's type checked. */ + dlang_function, + /* Strongly typed name, such as for classes, structs and enums. */ + dlang_type_name, + /* Template identifier. */ + dlang_template_ident, + /* Template symbol parameter. */ + dlang_template_param +}; + +/* Prototypes for forward referenced functions */ +static const char *dlang_function_args (string *, const char *); + +static const char *dlang_type (string *, const char *); + +static const char *dlang_value (string *, const char *, const char *, char); + +static const char *dlang_parse_qualified (string *, const char *, + enum dlang_symbol_kinds); + +static const char *dlang_parse_mangle (string *, const char *, + enum dlang_symbol_kinds); + +static const char *dlang_parse_tuple (string *, const char *); + +static const char *dlang_parse_template (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; + } +} + +/* 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; + + default: /* unknown attribute */ + return NULL; + } + break; + } + + 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) +{ + string attr, args, type; + size_t szattr, szargs, sztype; + + 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); + + /* Function call convention. */ + mangled = dlang_call_convention (decl, mangled); + + /* Function attributes. */ + mangled = dlang_attributes (&attr, mangled); + szattr = string_length (&attr); + + /* Function arguments. */ + mangled = dlang_function_args (&args, mangled); + szargs = string_length (&args); + + /* Function return type. */ + mangled = dlang_type (&type, mangled); + sztype = string_length (&type); + + /* Append to decl in order. */ + string_appendn (decl, type.b, sztype); + string_append (decl, "("); + string_appendn (decl, args.b, szargs); + string_append (decl, ") "); + string_appendn (decl, attr.b, szattr); + + 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) +{ + 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 '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); + } + + 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) +{ + if (mangled == NULL || *mangled == '\0') + return NULL; + + switch (*mangled) + { + case 'O': /* shared(T) */ + mangled++; + string_append (decl, "shared("); + mangled = dlang_type (decl, mangled); + string_append (decl, ")"); + return mangled; + case 'x': /* const(T) */ + mangled++; + string_append (decl, "const("); + mangled = dlang_type (decl, mangled); + string_append (decl, ")"); + return mangled; + case 'y': /* immutable(T) */ + mangled++; + string_append (decl, "immutable("); + mangled = dlang_type (decl, mangled); + string_append (decl, ")"); + return mangled; + case 'N': + mangled++; + if (*mangled == 'g') /* wild(T) */ + { + mangled++; + string_append (decl, "inout("); + mangled = dlang_type (decl, mangled); + string_append (decl, ")"); + return mangled; + } + else if (*mangled == 'h') /* vector(T) */ + { + mangled++; + string_append (decl, "__vector("); + mangled = dlang_type (decl, mangled); + string_append (decl, ")"); + return mangled; + } + else + return NULL; + case 'A': /* dynamic array (T[]) */ + mangled++; + mangled = dlang_type (decl, mangled); + 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); + 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); + sztype = string_length (&type); + + mangled = dlang_type (decl, mangled); + 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); + 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); + string_append (decl, "function"); + return mangled; + case 'I': /* ident T */ + case 'C': /* class T */ + case 'S': /* struct T */ + case 'E': /* enum T */ + case 'T': /* typedef T */ + mangled++; + return dlang_parse_qualified (decl, mangled, dlang_type_name); + case 'D': /* delegate T */ + { + string mods; + size_t szmods; + mangled++; + + string_init (&mods); + mangled = dlang_type_modifiers (&mods, mangled); + szmods = string_length (&mods); + + mangled = dlang_function_type (decl, mangled); + 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); + + /* 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; + + 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, + enum dlang_symbol_kinds kind) +{ + long len; + const char *endptr = dlang_number (mangled, &len); + + if (endptr == NULL || len == 0) + return NULL; + + /* In template parameter symbols, the first character of the mangled + name can be a digit. This causes ambiguity issues because the + digits of the two numbers are adjacent. */ + if (kind == dlang_template_param) + { + 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 (ISDIGIT (*mangled)) + mangled = dlang_parse_qualified (decl, mangled, + dlang_template_ident); + else if (strncmp (mangled, "_D", 2) == 0) + mangled = dlang_parse_mangle (decl, mangled, dlang_function); + + /* Check for name length mismatch. */ + if (mangled && (mangled - pend) == psize) + return mangled; + + psize /= 10; + string_setlength (decl, saved); + } + + /* No match on any combinations. */ + return NULL; + } + else + { + if (strlen (endptr) < (size_t) len) + return NULL; + + mangled = endptr; + + /* May be a template instance. */ + if (len >= 5 && mangled[0] == '_' && mangled[1] == '_' + && (mangled[2] == 'T' || mangled[2] == 'U')) + return dlang_parse_template (decl, mangled, 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[10]; + int pos = 10; + 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]), 10 - 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 (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'); + string_append (decl, ":"); + mangled = dlang_value (decl, mangled, NULL, '\0'); + + 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 (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, + enum dlang_symbol_kinds kind) +{ + /* A D mangled symbol is comprised of both scope and type information. + + MangleName: + _D QualifiedName Type + _D QualifiedName M Type + _D QualifiedName Z + ^ + The caller should have guaranteed that the start pointer is at the + above location. + */ + mangled += 2; + + mangled = dlang_parse_qualified (decl, mangled, dlang_top_level); + + if (mangled != NULL) + { + /* Artificial symbols end with 'Z' and have no type. */ + if (*mangled == 'Z') + mangled++; + else + { + string mods; + int saved; + + /* Skip over 'this' parameter. */ + if (*mangled == 'M') + mangled++; + + /* Save the type modifiers for appending at the end if needed. */ + string_init (&mods); + mangled = dlang_type_modifiers (&mods, mangled); + + if (mangled && dlang_call_convention_p (mangled)) + { + /* Skip over calling convention and attributes. */ + saved = string_length (decl); + mangled = dlang_call_convention (decl, mangled); + mangled = dlang_attributes (decl, mangled); + string_setlength (decl, saved); + + string_append (decl, "("); + mangled = dlang_function_args (decl, mangled); + string_append (decl, ")"); + + /* Add any const/immutable/shared modifier. */ + string_appendn (decl, mods.b, string_length (&mods)); + } + + /* Consume the decl type of symbol. */ + saved = string_length (decl); + mangled = dlang_type (decl, mangled); + string_setlength (decl, saved); + + string_delete (&mods); + } + } + + /* Check that the entire symbol was successfully demangled. */ + if (kind == dlang_top_level) + { + if (mangled == NULL || *mangled != '\0') + return NULL; + } + + return mangled; +} + +/* Extract and demangle the qualified symbol in MANGLED and append it to DECL. + Returns the remaining signature on success or NULL on failure. */ +static const char * +dlang_parse_qualified (string *decl, const char *mangled, + enum dlang_symbol_kinds kind) +{ + /* Qualified names are identifiers separated by their encoded length. + Nested functions also encode their argument types without specifying + what they return. + + QualifiedName: + SymbolName + SymbolName QualifiedName + SymbolName TypeFunctionNoReturn QualifiedName + SymbolName M TypeModifiers TypeFunctionNoReturn QualifiedName + ^ + 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, kind); + + /* Consume the encoded arguments. However if this is not followed by the + next encoded length, 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))) + { + const char *start = mangled; + int saved = string_length (decl); + + /* Skip over 'this' parameter and type modifiers. */ + if (*mangled == 'M') + { + mangled++; + mangled = dlang_type_modifiers (decl, mangled); + string_setlength (decl, saved); + } + + /* The rule we expect to match in the mangled string is: + + TypeFunctionNoReturn: + CallConvention FuncAttrs Arguments ArgClose + + The calling convention and function attributes are not included + in the demangled string. */ + mangled = dlang_call_convention (decl, mangled); + mangled = dlang_attributes (decl, mangled); + string_setlength (decl, saved); + + string_append (decl, "("); + mangled = dlang_function_args (decl, mangled); + string_append (decl, ")"); + + if (mangled == NULL || !ISDIGIT (*mangled)) + { + /* Did not match the rule we were looking for. */ + mangled = start; + string_setlength (decl, saved); + } + } + } + while (mangled && ISDIGIT (*mangled)); + + 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) +{ + long elements; + + mangled = dlang_number (mangled, &elements); + if (mangled == NULL) + return NULL; + + string_append (decl, "Tuple!("); + + while (elements--) + { + mangled = dlang_type (decl, mangled); + if (elements != 0) + string_append (decl, ", "); + } + + string_append (decl, ")"); + 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_template_args (string *decl, const char *mangled) +{ + 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_identifier (decl, mangled, dlang_template_param); + break; + case 'T': /* Type parameter. */ + mangled++; + mangled = dlang_type (decl, mangled); + break; + case 'V': /* Value parameter. */ + { + string name; + char type; + + /* Peek at the type. */ + mangled++; + type = *mangled; + + /* 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); + string_need (&name, 1); + *(name.p) = '\0'; + + mangled = dlang_value (decl, mangled, name.b, type); + string_delete (&name); + break; + } + + default: + return NULL; + } + } + + return mangled; +} + +/* Extract and demangle the template symbol in MANGLED, expected to + be made up of LEN characters, 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, long len) +{ + const char *start = mangled; + + /* 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 (!ISDIGIT (mangled[3]) || mangled[3] == '0') + return NULL; + + mangled += 3; + + /* Template identifier. */ + mangled = dlang_identifier (decl, mangled, dlang_template_ident); + + /* Template arguments. */ + string_append (decl, "!("); + mangled = dlang_template_args (decl, mangled); + string_append (decl, ")"); + + /* Check for template name length mismatch. */ + if (mangled && (mangled - start) != len) + return NULL; + + return mangled; +} + +/* 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 + { + if (dlang_parse_mangle (&decl, mangled, dlang_top_level) == NULL) + string_delete (&decl); + } + + if (string_length (&decl) > 0) + { + string_need (&decl, 1); + *(decl.p) = '\0'; + demangled = decl.b; + } + + return demangled; +} +