| /* Helper routines for D support in GDB. |
| |
| Copyright (C) 2014 Free Software Foundation, Inc. |
| |
| This file is part of GDB. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program 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 General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
| |
| #include "defs.h" |
| #include "d-lang.h" |
| #include "gdb_obstack.h" |
| |
| #include "safe-ctype.h" |
| |
| static const char *parse_function_args (struct obstack *, const char *); |
| static const char *parse_type (struct obstack *, const char *); |
| |
| |
| /* Demangle the calling convention from MANGLE and append it to TEMPBUF. |
| Return the remaining string on success or NULL on failure. */ |
| |
| static const char * |
| parse_call_convention (struct obstack *tempbuf, const char *mangle) |
| { |
| if ((mangle == NULL) || (*mangle == '\0')) |
| return mangle; |
| |
| switch (*mangle) |
| { |
| case 'F': /* (D) */ |
| mangle++; |
| break; |
| case 'U': /* (C) */ |
| mangle++; |
| obstack_grow_str (tempbuf, "extern(C) "); |
| break; |
| case 'W': /* (Windows) */ |
| mangle++; |
| obstack_grow_str (tempbuf, "extern(Windows) "); |
| break; |
| case 'V': /* (Pascal) */ |
| mangle++; |
| obstack_grow_str (tempbuf, "extern(Pascal) "); |
| break; |
| case 'R': /* (C++) */ |
| mangle++; |
| obstack_grow_str (tempbuf, "extern(C++) "); |
| break; |
| default: |
| return NULL; |
| } |
| |
| return mangle; |
| } |
| |
| /* Demangle the D function attributes from MANGLE and append it to TEMPBUF. |
| Return the remaining string on success or NULL on failure. */ |
| |
| static const char * |
| parse_attributes (struct obstack *tempbuf, const char *mangle) |
| { |
| if ((mangle == NULL) || (*mangle == '\0')) |
| return mangle; |
| |
| while (*mangle == 'N') |
| { |
| mangle++; |
| switch (*mangle) |
| { |
| case 'a': /* pure */ |
| mangle++; |
| obstack_grow_str (tempbuf, "pure "); |
| continue; |
| case 'b': /* nothrow */ |
| mangle++; |
| obstack_grow_str (tempbuf, "nothrow "); |
| continue; |
| case 'c': /* ref */ |
| mangle++; |
| obstack_grow_str (tempbuf, "ref "); |
| continue; |
| case 'd': /* @property */ |
| mangle++; |
| obstack_grow_str (tempbuf, "@property "); |
| continue; |
| case 'e': /* @trusted */ |
| mangle++; |
| obstack_grow_str (tempbuf, "@trusted "); |
| continue; |
| case 'f': /* @safe */ |
| mangle++; |
| obstack_grow_str (tempbuf, "@safe "); |
| continue; |
| case 'g': |
| case 'h': |
| /* inout parameter is represented as 'Ng'. |
| vector parameter is represented as 'Nh'. |
| If we see this, then we know we're really in the |
| parameter list. Rewind and break. */ |
| mangle--; |
| } |
| break; |
| } |
| return mangle; |
| } |
| |
| /* Demangle the function type from MANGLE and append it to TEMPBUF. |
| Return the remaining string on success or NULL on failure. */ |
| |
| static const char * |
| parse_function_type (struct obstack *tempbuf, const char *mangle) |
| { |
| struct obstack obattr, obargs, obtype; |
| char *attr, *args, *type; |
| size_t szattr, szargs, sztype; |
| |
| if ((mangle == NULL) || (*mangle == '\0')) |
| return mangle; |
| |
| /* The order of the mangled string is: |
| TypeFunction :: |
| CallConvention FuncAttrs Arguments ArgClose Type |
| |
| The demangled string is re-ordered as: |
| CallConvention Type Arguments FuncAttrs |
| */ |
| obstack_init (&obattr); |
| obstack_init (&obargs); |
| obstack_init (&obtype); |
| |
| /* Function call convention. */ |
| mangle = parse_call_convention (tempbuf, mangle); |
| |
| /* Function attributes. */ |
| mangle = parse_attributes (&obattr, mangle); |
| szattr = obstack_object_size (&obattr); |
| attr = obstack_finish (&obattr); |
| |
| /* Function arguments. */ |
| mangle = parse_function_args (&obargs, mangle); |
| szargs = obstack_object_size (&obargs); |
| args = obstack_finish (&obargs); |
| |
| /* Function return type. */ |
| mangle = parse_type (&obtype, mangle); |
| sztype = obstack_object_size (&obtype); |
| type = obstack_finish (&obtype); |
| |
| /* Append to buffer in order. */ |
| obstack_grow (tempbuf, type, sztype); |
| obstack_grow_str (tempbuf, "("); |
| obstack_grow (tempbuf, args, szargs); |
| obstack_grow_str (tempbuf, ") "); |
| obstack_grow (tempbuf, attr, szattr); |
| |
| obstack_free (&obattr, NULL); |
| obstack_free (&obargs, NULL); |
| obstack_free (&obtype, NULL); |
| return mangle; |
| } |
| |
| /* Demangle the argument list from MANGLE and append it to TEMPBUF. |
| Return the remaining string on success or NULL on failure. */ |
| |
| static const char * |
| parse_function_args (struct obstack *tempbuf, const char *mangle) |
| { |
| size_t n = 0; |
| |
| while ((mangle != NULL) && (*mangle != '\0')) |
| { |
| switch (*mangle) |
| { |
| case 'X': /* (variadic T t...) style. */ |
| mangle++; |
| obstack_grow_str (tempbuf, "..."); |
| return mangle; |
| case 'Y': /* (variadic T t, ...) style. */ |
| mangle++; |
| obstack_grow_str (tempbuf, ", ..."); |
| return mangle; |
| case 'Z': /* Normal function. */ |
| mangle++; |
| return mangle; |
| } |
| |
| if (n++) |
| obstack_grow_str (tempbuf, ", "); |
| |
| if (*mangle == 'M') /* scope(T) */ |
| { |
| mangle++; |
| obstack_grow_str (tempbuf, "scope "); |
| } |
| |
| switch (*mangle) |
| { |
| case 'J': /* out(T) */ |
| mangle++; |
| obstack_grow_str (tempbuf, "out "); |
| break; |
| case 'K': /* ref(T) */ |
| mangle++; |
| obstack_grow_str (tempbuf, "ref "); |
| break; |
| case 'L': /* lazy(T) */ |
| mangle++; |
| obstack_grow_str (tempbuf, "lazy "); |
| break; |
| } |
| mangle = parse_type (tempbuf, mangle); |
| } |
| return mangle; |
| } |
| |
| /* Demangle the type from MANGLE and append it to TEMPBUF. |
| Return the remaining string on success or NULL on failure. */ |
| |
| static const char * |
| parse_type (struct obstack *tempbuf, const char *mangle) |
| { |
| if ((mangle == NULL) || (*mangle == '\0')) |
| return mangle; |
| |
| switch (*mangle) |
| { |
| case 'O': /* shared(T) */ |
| mangle++; |
| obstack_grow_str (tempbuf, "shared("); |
| mangle = parse_type (tempbuf, mangle); |
| obstack_grow_str (tempbuf, ")"); |
| return mangle; |
| case 'x': /* const(T) */ |
| mangle++; |
| obstack_grow_str (tempbuf, "const("); |
| mangle = parse_type (tempbuf, mangle); |
| obstack_grow_str (tempbuf, ")"); |
| return mangle; |
| case 'y': /* immutable(T) */ |
| mangle++; |
| obstack_grow_str (tempbuf, "immutable("); |
| mangle = parse_type (tempbuf, mangle); |
| obstack_grow_str (tempbuf, ")"); |
| return mangle; |
| case 'N': |
| mangle++; |
| if (*mangle == 'g') /* wild(T) */ |
| { |
| mangle++; |
| obstack_grow_str (tempbuf, "inout("); |
| mangle = parse_type (tempbuf, mangle); |
| obstack_grow_str (tempbuf, ")"); |
| return mangle; |
| } |
| else if (*mangle == 'h') /* vector(T) */ |
| { |
| mangle++; |
| /* The basetype for vectors are always static arrays. */ |
| if (*mangle != 'G') |
| return NULL; |
| obstack_grow_str (tempbuf, "__vector("); |
| mangle = parse_type (tempbuf, mangle); |
| obstack_grow_str (tempbuf, ")"); |
| return mangle; |
| } |
| else |
| return NULL; |
| case 'A': /* dynamic array (T[]) */ |
| mangle++; |
| mangle = parse_type (tempbuf, mangle); |
| obstack_grow_str (tempbuf, "[]"); |
| return mangle; |
| case 'G': /* static array (T[N]) */ |
| { |
| const char *numptr; |
| size_t num = 0; |
| mangle++; |
| |
| numptr = mangle; |
| while (ISDIGIT (*mangle)) |
| { |
| num++; |
| mangle++; |
| } |
| mangle = parse_type (tempbuf, mangle); |
| obstack_grow_str (tempbuf, "["); |
| obstack_grow (tempbuf, numptr, num); |
| obstack_grow_str (tempbuf, "]"); |
| return mangle; |
| } |
| case 'H': /* associative array (T[T]) */ |
| { |
| struct obstack obtype; |
| char *type; |
| size_t sztype; |
| mangle++; |
| |
| obstack_init (&obtype); |
| mangle = parse_type (&obtype, mangle); |
| sztype = obstack_object_size (&obtype); |
| type = obstack_finish (&obtype); |
| |
| mangle = parse_type (tempbuf, mangle); |
| obstack_grow_str (tempbuf, "["); |
| obstack_grow (tempbuf, type, sztype); |
| obstack_grow_str (tempbuf, "]"); |
| |
| obstack_free (&obtype, NULL); |
| return mangle; |
| } |
| case 'P': /* pointer (T*) */ |
| mangle++; |
| mangle = parse_type (tempbuf, mangle); |
| obstack_grow_str (tempbuf, "*"); |
| return mangle; |
| case 'I': /* ident T */ |
| case 'C': /* class T */ |
| case 'S': /* struct T */ |
| case 'E': /* enum T */ |
| case 'T': /* typedef T */ |
| mangle++; |
| return d_parse_symbol (tempbuf, mangle); |
| case 'D': /* delegate T */ |
| mangle++; |
| mangle = parse_function_type (tempbuf, mangle); |
| obstack_grow_str (tempbuf, "delegate"); |
| return mangle; |
| case 'B': /* tuple T */ |
| /* TODO: Handle this. */ |
| return NULL; |
| |
| /* Function types */ |
| case 'F': case 'U': case 'W': |
| case 'V': case 'R': |
| mangle = parse_function_type (tempbuf, mangle); |
| obstack_grow_str (tempbuf, "function"); |
| return mangle; |
| |
| /* Basic types */ |
| case 'n': |
| mangle++; |
| obstack_grow_str (tempbuf, "none"); |
| return mangle; |
| case 'v': |
| mangle++; |
| obstack_grow_str (tempbuf, "void"); |
| return mangle; |
| case 'g': |
| mangle++; |
| obstack_grow_str (tempbuf, "byte"); |
| return mangle; |
| case 'h': |
| mangle++; |
| obstack_grow_str (tempbuf, "ubyte"); |
| return mangle; |
| case 's': |
| mangle++; |
| obstack_grow_str (tempbuf, "short"); |
| return mangle; |
| case 't': |
| mangle++; |
| obstack_grow_str (tempbuf, "ushort"); |
| return mangle; |
| case 'i': |
| mangle++; |
| obstack_grow_str (tempbuf, "int"); |
| return mangle; |
| case 'k': |
| mangle++; |
| obstack_grow_str (tempbuf, "uint"); |
| return mangle; |
| case 'l': |
| mangle++; |
| obstack_grow_str (tempbuf, "long"); |
| return mangle; |
| case 'm': |
| mangle++; |
| obstack_grow_str (tempbuf, "ulong"); |
| return mangle; |
| case 'f': |
| mangle++; |
| obstack_grow_str (tempbuf, "float"); |
| return mangle; |
| case 'd': |
| mangle++; |
| obstack_grow_str (tempbuf, "double"); |
| return mangle; |
| case 'e': |
| mangle++; |
| obstack_grow_str (tempbuf, "real"); |
| return mangle; |
| |
| /* Imaginary and Complex types */ |
| case 'o': |
| mangle++; |
| obstack_grow_str (tempbuf, "ifloat"); |
| return mangle; |
| case 'p': |
| mangle++; |
| obstack_grow_str (tempbuf, "idouble"); |
| return mangle; |
| case 'j': |
| mangle++; |
| obstack_grow_str (tempbuf, "ireal"); |
| return mangle; |
| case 'q': |
| mangle++; |
| obstack_grow_str (tempbuf, "cfloat"); |
| return mangle; |
| case 'r': |
| mangle++; |
| obstack_grow_str (tempbuf, "cdouble"); |
| return mangle; |
| case 'c': |
| mangle++; |
| obstack_grow_str (tempbuf, "creal"); |
| return mangle; |
| |
| /* Other types */ |
| case 'b': |
| mangle++; |
| obstack_grow_str (tempbuf, "bool"); |
| return mangle; |
| case 'a': |
| mangle++; |
| obstack_grow_str (tempbuf, "char"); |
| return mangle; |
| case 'u': |
| mangle++; |
| obstack_grow_str (tempbuf, "wchar"); |
| return mangle; |
| case 'w': |
| mangle++; |
| obstack_grow_str (tempbuf, "dchar"); |
| return mangle; |
| |
| default: /* unhandled */ |
| return NULL; |
| } |
| } |
| |
| /* Extract the identifier from MANGLE and append it to TEMPBUF. |
| Return the remaining string on success or NULL on failure. */ |
| |
| static const char * |
| parse_identifier (struct obstack *tempbuf, const char *mangle) |
| { |
| if ((mangle == NULL) || (*mangle == '\0')) |
| return mangle; |
| |
| if (ISDIGIT (*mangle)) |
| { |
| char *endptr; |
| long i = strtol (mangle, &endptr, 10); |
| |
| if (i <= 0 || strlen (endptr) < i) |
| return NULL; |
| |
| mangle = endptr; |
| |
| /* No support for demangling templates. */ |
| if (i >= 5 && strncmp (mangle, "__T", 3) == 0) |
| return NULL; |
| |
| if (strncmp (mangle, "__ctor", i) == 0) |
| { |
| /* Constructor symbol for a class/struct. */ |
| obstack_grow_str (tempbuf, "this"); |
| mangle += i; |
| return mangle; |
| } |
| else if (strncmp (mangle, "__dtor", i) == 0) |
| { |
| /* Destructor symbol for a class/struct. */ |
| obstack_grow_str (tempbuf, "~this"); |
| mangle += i; |
| return mangle; |
| } |
| else if (strncmp (mangle, "__postblit", i) == 0) |
| { |
| /* Postblit symbol for a struct. */ |
| obstack_grow_str (tempbuf, "this(this)"); |
| mangle += i; |
| return mangle; |
| } |
| else if (strncmp (mangle, "__initZ", i+1) == 0) |
| { |
| /* The static initialiser for a given symbol. */ |
| obstack_grow_str (tempbuf, "init$"); |
| mangle += i + 1; |
| return mangle; |
| } |
| else if (strncmp (mangle, "__ClassZ", i+1) == 0) |
| { |
| /* The classinfo symbol for a given class. */ |
| obstack_grow_str (tempbuf, "classinfo$"); |
| mangle += i + 1; |
| return mangle; |
| } |
| else if (strncmp (mangle, "__vtblZ", i+1) == 0) |
| { |
| /* The vtable symbol for a given class. */ |
| obstack_grow_str (tempbuf, "vtbl$"); |
| mangle += i + 1; |
| return mangle; |
| } |
| else if (strncmp (mangle, "__InterfaceZ", i+1) == 0) |
| { |
| /* The interface symbol for a given class. */ |
| obstack_grow_str (tempbuf, "interface$"); |
| mangle += i + 1; |
| return mangle; |
| } |
| else if (strncmp (mangle, "__ModuleInfoZ", i+1) == 0) |
| { |
| /* The ModuleInfo symbol for a given module. */ |
| obstack_grow_str (tempbuf, "moduleinfo$"); |
| mangle += i + 1; |
| return mangle; |
| } |
| obstack_grow (tempbuf, mangle, i); |
| mangle += i; |
| } |
| else |
| return NULL; |
| |
| return mangle; |
| } |
| |
| /* Test whether the current position of MANGLE is pointing to the |
| beginning of a mangled function parameter list, which starts with |
| the calling convention. Returns 1 on success or 0 on failure. */ |
| |
| static int |
| call_convention_p (const char *mangle) |
| { |
| size_t i; |
| |
| if ((mangle == NULL) || (*mangle == '\0')) |
| return 0; |
| |
| switch (*mangle) |
| { |
| case 'F': case 'U': case 'V': |
| case 'W': case 'R': |
| return 1; |
| |
| case 'M': /* Prefix for functions needing 'this'. */ |
| i = 1; |
| if (mangle[i] == 'x') |
| i++; |
| |
| switch (mangle[i]) |
| { |
| case 'F': case 'U': case 'V': |
| case 'W': case 'R': |
| return 1; |
| } |
| |
| default: |
| return 0; |
| } |
| } |
| |
| /* Extract and demangle the symbol in MANGLE and append it to TEMPBUF. |
| Return the remaining signature on success or NULL on failure. */ |
| |
| const char * |
| d_parse_symbol (struct obstack *tempbuf, const char *mangle) |
| { |
| size_t n = 0; |
| do |
| { |
| if (n++) |
| obstack_grow_str (tempbuf, "."); |
| |
| mangle = parse_identifier (tempbuf, mangle); |
| |
| if (call_convention_p (mangle)) |
| { |
| char *saved; |
| |
| /* Skip over 'this' parameter. */ |
| if (*mangle == 'M') |
| mangle += (mangle[1] == 'x') ? 2 : 1; |
| |
| /* Skip over calling convention and attributes in qualified name. */ |
| saved = obstack_next_free (tempbuf); |
| mangle = parse_call_convention (tempbuf, mangle); |
| mangle = parse_attributes (tempbuf, mangle); |
| obstack_next_free (tempbuf) = saved; |
| |
| obstack_grow_str (tempbuf, "("); |
| mangle = parse_function_args (tempbuf, mangle); |
| obstack_grow_str (tempbuf, ")"); |
| |
| /* Demangle the function return type as a kind of sanity test. */ |
| if ((mangle != NULL) && !ISDIGIT (*mangle)) |
| { |
| saved = obstack_next_free (tempbuf); |
| mangle = parse_type (tempbuf, mangle); |
| obstack_next_free (tempbuf) = saved; |
| } |
| } |
| } |
| while ((mangle != NULL) && ISDIGIT (*mangle)); |
| |
| return mangle; |
| } |
| |