/* 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 . */ #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; }