public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 5/5] Fix for D demangling in GDB
@ 2014-01-09 13:13 Iain Buclaw
  2014-01-09 21:54 ` Tom Tromey
  0 siblings, 1 reply; 13+ messages in thread
From: Iain Buclaw @ 2014-01-09 13:13 UTC (permalink / raw)
  To: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 489 bytes --]

This both fixes D demangling in GDB and updates it to the current D
mangling spec.

2014-01-07  Iain Buclaw  <ibuclaw@gdcproject.org>

    gdb/

        * d-lang.h (d_parse_symbol): Add declaration.
        * d-lang.c (extract_identifiers)
        (extract_type_info): Remove functions.
        (d_demangle): Use d_parse_symbol implemented in d-support.c to
        demangle D symbols.
        * d-support.c: New file.

    gdb/testsuite/

        * gdb.dlang/demangle.exp: New file.

---

[-- Attachment #2: dlang-p5.patch --]
[-- Type: text/x-patch, Size: 38368 bytes --]

 gdb/ChangeLog                        |    9 +
 gdb/Makefile.in                      |    3 +-
 gdb/d-lang.c                         |  165 +--------
 gdb/d-lang.h                         |    4 +
 gdb/d-support.c                      |  606 ++++++++++++++++++++++++++++++++++
 gdb/testsuite/ChangeLog              |    4 +
 gdb/testsuite/gdb.dlang/demangle.exp |  223 +++++++++++++
 7 files changed, 862 insertions(+), 152 deletions(-)

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 824b26b..f557bfe 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -727,7 +727,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
 	charset.c cleanups.c cli-out.c coffread.c coff-pe-read.c \
 	complaints.c completer.c continuations.c corefile.c corelow.c \
 	cp-abi.c cp-support.c cp-namespace.c cp-valprint.c \
-	d-lang.c d-valprint.c \
+	d-lang.c d-support.c d-valprint.c \
 	cp-name-parser.y \
 	dbxread.c demangle.c dictionary.c disasm.c doublest.c dummy-frame.c \
 	dwarf2expr.c dwarf2loc.c dwarf2read.c dwarf2-frame.c \
@@ -947,6 +947,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
 	inline-frame.o \
 	gnu-v2-abi.o gnu-v3-abi.o cp-abi.o cp-support.o \
 	cp-namespace.o \
+	d-support.o \
 	reggroups.o regset.o \
 	trad-frame.o \
 	tramp-frame.o \
diff --git a/gdb/d-lang.c b/gdb/d-lang.c
index e74b047..5d61d02 100644
--- a/gdb/d-lang.c
+++ b/gdb/d-lang.c
@@ -23,12 +23,9 @@
 #include "varobj.h"
 #include "d-lang.h"
 #include "c-lang.h"
-#include <string.h>
 #include "parser-defs.h"
 #include "gdb_obstack.h"
 
-#include <ctype.h>
-
 /* The name of the symbol to use to get the name of the main subprogram.  */
 static const char D_MAIN[] = "D main";
 
@@ -50,175 +47,41 @@ d_main_name (void)
   return NULL;
 }
 
-/* Extract identifiers from MANGLED_STR and append it to TEMPBUF.
-   Return 1 on success or 0 on failure.  */
-static int
-extract_identifiers (const char *mangled_str, struct obstack *tempbuf)
-{
-  long i = 0;
-
-  while (isdigit (*mangled_str))
-    {
-      char *end_ptr;
-
-      i = strtol (mangled_str, &end_ptr, 10);
-      mangled_str = end_ptr;
-      if (i <= 0 || strlen (mangled_str) < i)
-        return 0;
-      obstack_grow (tempbuf, mangled_str, i);
-      mangled_str += i;
-      obstack_grow_str (tempbuf, ".");
-    }
-  if (*mangled_str == '\0' || i == 0)
-    return 0;
-  obstack_blank (tempbuf, -1);
-  return 1;
-}
-
-/* Extract and demangle type from MANGLED_STR and append it to TEMPBUF.
-   Return 1 on success or 0 on failure.  */
-static int
-extract_type_info (const char *mangled_str, struct obstack *tempbuf)
-{
-  if (*mangled_str == '\0')
-    return 0;
-  switch (*mangled_str++)
-    {
-      case 'A': /* dynamic array */
-      case 'G': /* static array */
-      case 'H': /* associative array */
-	if (!extract_type_info (mangled_str, tempbuf))
-	  return 0;
-	obstack_grow_str (tempbuf, "[]");
-	return 1;
-      case 'P': /* pointer */
-	if (!extract_type_info (mangled_str, tempbuf))
-	  return 0;
-	obstack_grow_str (tempbuf, "*");
-	return 1;
-      case 'R': /* reference */
-	if (!extract_type_info (mangled_str, tempbuf))
-	  return 0;
-	obstack_grow_str (tempbuf, "&");
-	return 1;
-      case 'Z': /* return value */
-	return extract_type_info (mangled_str, tempbuf);
-      case 'J': /* out */
-	obstack_grow_str (tempbuf, "out ");
-	return extract_type_info (mangled_str, tempbuf);
-      case 'K': /* inout */
-	obstack_grow_str (tempbuf, "inout ");
-	return extract_type_info (mangled_str, tempbuf);
-      case 'E': /* enum */
-      case 'T': /* typedef */
-      case 'D': /* delegate */
-      case 'C': /* class */
-      case 'S': /* struct */
-	return extract_identifiers (mangled_str, tempbuf);
-
-      /* basic types: */
-      case 'n': obstack_grow_str (tempbuf, "none"); return 1;
-      case 'v': obstack_grow_str (tempbuf, "void"); return 1;
-      case 'g': obstack_grow_str (tempbuf, "byte"); return 1;
-      case 'h': obstack_grow_str (tempbuf, "ubyte"); return 1;
-      case 's': obstack_grow_str (tempbuf, "short"); return 1;
-      case 't': obstack_grow_str (tempbuf, "ushort"); return 1;
-      case 'i': obstack_grow_str (tempbuf, "int"); return 1;
-      case 'k': obstack_grow_str (tempbuf, "uint"); return 1;
-      case 'l': obstack_grow_str (tempbuf, "long"); return 1;
-      case 'm': obstack_grow_str (tempbuf, "ulong"); return 1;
-      case 'f': obstack_grow_str (tempbuf, "float"); return 1;
-      case 'd': obstack_grow_str (tempbuf, "double"); return 1;
-      case 'e': obstack_grow_str (tempbuf, "real"); return 1;
-
-      /* imaginary and complex: */
-      case 'o': obstack_grow_str (tempbuf, "ifloat"); return 1;
-      case 'p': obstack_grow_str (tempbuf, "idouble"); return 1;
-      case 'j': obstack_grow_str (tempbuf, "ireal"); return 1;
-      case 'q': obstack_grow_str (tempbuf, "cfloat"); return 1;
-      case 'r': obstack_grow_str (tempbuf, "cdouble"); return 1;
-      case 'c': obstack_grow_str (tempbuf, "creal"); return 1;
-
-      /* other types: */
-      case 'b': obstack_grow_str (tempbuf, "bit"); return 1;
-      case 'a': obstack_grow_str (tempbuf, "char"); return 1;
-      case 'u': obstack_grow_str (tempbuf, "wchar"); return 1;
-      case 'w': obstack_grow_str (tempbuf, "dchar"); return 1;
-
-      default:
-	obstack_grow_str (tempbuf, "unknown");
-	return 1;
-    }
-}
-
 /* Implements the la_demangle language_defn routine for language D.  */
 char *
 d_demangle (const char *symbol, int options)
 {
   struct obstack tempbuf;
-  char *out_str;
-  unsigned char is_func = 0;
+  char *result;
 
-  if (symbol == NULL)
+  if (symbol == NULL || *symbol == '\0')
     return NULL;
   else if (strcmp (symbol, "_Dmain") == 0)
     return xstrdup ("D main");
 
   obstack_init (&tempbuf);
-  
-  if (symbol[0] == '_' && symbol[1] == 'D')
-    {
-      symbol += 2;
-      is_func = 1;
-    }
-  else if (strncmp (symbol, "__Class_", 8) == 0)
-    symbol += 8;
-  else if (strncmp (symbol, "__init_", 7) == 0)
-    symbol += 7;
-  else if (strncmp (symbol, "__vtbl_", 7) == 0)
-    symbol += 7;
-  else if (strncmp (symbol, "__modctor_", 10) == 0)
-    symbol += 10;
-  else if (strncmp (symbol, "__moddtor_", 10) == 0)
-    symbol += 10;
-  else if (strncmp (symbol, "__ModuleInfo_", 13) == 0)
-    symbol += 13;
+
+  if (strncmp (symbol, "_D", 2) == 0)
+    symbol += 2;
   else
     {
       obstack_free (&tempbuf, NULL);
       return NULL;
     }
-  
-  if (!extract_identifiers (symbol, &tempbuf))
+
+  if (d_parse_symbol (&tempbuf, symbol) != NULL)
+    {
+      obstack_grow_str0 (&tempbuf, "");
+      result = xstrdup (obstack_finish (&tempbuf));
+      obstack_free (&tempbuf, NULL);
+    }
+  else
     {
       obstack_free (&tempbuf, NULL);
       return NULL;
     }
 
-  obstack_grow_str (&tempbuf, "(");
-  if (is_func == 1 && *symbol == 'F')
-    {
-      symbol++;
-      while (*symbol != '\0' && *symbol != 'Z')
-	{
-	  if (is_func == 1)
-	    is_func++;
-	  else
-	    obstack_grow_str (&tempbuf, ", ");
-	  if (!extract_type_info (symbol, &tempbuf))
-	    {
-	      obstack_free (&tempbuf, NULL);
-	      return NULL;
-	   }
-	}
-     }
-  obstack_grow_str0 (&tempbuf, ")");
-
-  /* Doesn't display the return type, but wouldn't be too hard to do.  */
-
-  out_str = xstrdup (obstack_finish (&tempbuf));
-  obstack_free (&tempbuf, NULL);
-  return out_str;
+  return result;
 }
 
 /* Table mapping opcodes into strings for printing operators
diff --git a/gdb/d-lang.h b/gdb/d-lang.h
index 7a6c608..0612f26 100644
--- a/gdb/d-lang.h
+++ b/gdb/d-lang.h
@@ -66,4 +66,8 @@ extern void d_val_print (struct type *type, const gdb_byte *valaddr,
 			 const struct value *val,
 			 const struct value_print_options *options);
 
+/* Defined in d-support.c  */
+
+extern const char *d_parse_symbol (struct obstack *, const char *);
+
 #endif /* !defined (D_LANG_H) */
diff --git a/gdb/d-support.c b/gdb/d-support.c
new file mode 100644
index 0000000..318acf8
--- /dev/null
+++ b/gdb/d-support.c
@@ -0,0 +1,606 @@
+/* D language support routines for GDB, the GNU debugger.
+
+   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 && *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;
+}
+
+static int
+call_convention_p (const char *mangle)
+{
+  size_t i;
+
+  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 (mangle && 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 && !ISDIGIT (*mangle))
+	    {
+	      saved = obstack_next_free (tempbuf);
+	      mangle = parse_type (tempbuf, mangle);
+	      obstack_next_free (tempbuf) = saved;
+	    }
+	}
+    }
+  while (mangle && ISDIGIT (*mangle));
+
+  return mangle;
+}
+
diff --git a/gdb/testsuite/gdb.dlang/demangle.exp b/gdb/testsuite/gdb.dlang/demangle.exp
new file mode 100644
index 0000000..4c237c5
--- /dev/null
+++ b/gdb/testsuite/gdb.dlang/demangle.exp
@@ -0,0 +1,223 @@
+# Copyright (C) 2014 Free Software Foundation, Inc.
+
+# 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/>.
+
+# Test D demangling.
+# The tests here intentionally do not require a D compiler.
+
+load_lib "d-support.exp"
+
+if { [skip_d_tests] } { continue }
+
+### Utility function for test_demangling and test_demangling_exact.
+proc test_demangling {test result} {
+    gdb_test_exact "maintenance demangle $test" $result $test
+}
+
+#
+#  Test D demangler
+#
+
+proc test_d_demangling {} {
+
+    test_demangling "_Dmain" "D main"
+
+    test_demangling "_D8demangle4testFaZv" "demangle.test(char)"
+    test_demangling "_D8demangle4testFbZv" "demangle.test(bool)"
+    test_demangling "_D8demangle4testFcZv" "demangle.test(creal)"
+    test_demangling "_D8demangle4testFdZv" "demangle.test(double)"
+    test_demangling "_D8demangle4testFeZv" "demangle.test(real)"
+    test_demangling "_D8demangle4testFfZv" "demangle.test(float)"
+    test_demangling "_D8demangle4testFgZv" "demangle.test(byte)"
+    test_demangling "_D8demangle4testFhZv" "demangle.test(ubyte)"
+    test_demangling "_D8demangle4testFiZv" "demangle.test(int)"
+    test_demangling "_D8demangle4testFjZv" "demangle.test(ireal)"
+    test_demangling "_D8demangle4testFkZv" "demangle.test(uint)"
+    test_demangling "_D8demangle4testFlZv" "demangle.test(long)"
+    test_demangling "_D8demangle4testFmZv" "demangle.test(ulong)"
+    test_demangling "_D8demangle4testFnZv" "demangle.test(none)"
+    test_demangling "_D8demangle4testFoZv" "demangle.test(ifloat)"
+    test_demangling "_D8demangle4testFpZv" "demangle.test(idouble)"
+    test_demangling "_D8demangle4testFqZv" "demangle.test(cfloat)"
+    test_demangling "_D8demangle4testFrZv" "demangle.test(cdouble)"
+    test_demangling "_D8demangle4testFsZv" "demangle.test(short)"
+    test_demangling "_D8demangle4testFtZv" "demangle.test(ushort)"
+    test_demangling "_D8demangle4testFuZv" "demangle.test(wchar)"
+    test_demangling "_D8demangle4testFvZv" "demangle.test(void)"
+    test_demangling "_D8demangle4testFwZv" "demangle.test(dchar)"
+
+    test_demangling "_D8demangle4testFOaZv" "demangle.test(shared(char))"
+    test_demangling "_D8demangle4testFxaZv" "demangle.test(const(char))"
+    test_demangling "_D8demangle4testFyaZv" "demangle.test(immutable(char))"
+    test_demangling "_D8demangle4testFNgaZv" "demangle.test(inout(char))"
+    test_demangling "_D8demangle4testFOxaZv" "demangle.test(shared(const(char)))"
+    test_demangling "_D8demangle4testFONgaZv" "demangle.test(shared(inout(char)))"
+    test_demangling "_D8demangle4testFAaZv" "demangle.test(char\[\])"
+    test_demangling "_D8demangle4testFAAaZv" "demangle.test(char\[\]\[\])"
+    test_demangling "_D8demangle4testFAAAaZv" "demangle.test(char\[\]\[\]\[\])"
+    test_demangling "_D8demangle4testFG42aZv" "demangle.test(char\[42\])"
+    test_demangling "_D8demangle4testFG42G42aZv" "demangle.test(char\[42\]\[42\])"
+    test_demangling "_D8demangle4testFG42G42G42aZv" "demangle.test(char\[42\]\[42\]\[42\])"
+    test_demangling "_D8demangle4testFG1234567890aZv" "demangle.test(char\[1234567890\])"
+    test_demangling "_D8demangle4testFHaaZv" "demangle.test(char\[char\])"
+    test_demangling "_D8demangle4testFHHaaaZv" "demangle.test(char\[char\[char\]\])"
+    test_demangling "_D8demangle4testFPaZv" "demangle.test(char*)"
+    test_demangling "_D8demangle4testFPPaZv" "demangle.test(char**)"
+    test_demangling "_D8demangle4testFPPPaZv" "demangle.test(char***)"
+
+    test_demangling "_D8demangle4testFNhG8gZv" "demangle.test(__vector(byte\[8\]))"
+    test_demangling "_D8demangle4testFNhG16gZv" "demangle.test(__vector(byte\[16\]))"
+    test_demangling "_D8demangle4testFNhG32gZv" "demangle.test(__vector(byte\[32\]))"
+    test_demangling "_D8demangle4testFNhG4sZv" "demangle.test(__vector(short\[4\]))"
+    test_demangling "_D8demangle4testFNhG8sZv" "demangle.test(__vector(short\[8\]))"
+    test_demangling "_D8demangle4testFNhG16sZv" "demangle.test(__vector(short\[16\]))"
+    test_demangling "_D8demangle4testFNhG2iZv" "demangle.test(__vector(int\[2\]))"
+    test_demangling "_D8demangle4testFNhG4iZv" "demangle.test(__vector(int\[4\]))"
+    test_demangling "_D8demangle4testFNhG8iZv" "demangle.test(__vector(int\[8\]))"
+    test_demangling "_D8demangle4testFNhG1lZv" "demangle.test(__vector(long\[1\]))"
+    test_demangling "_D8demangle4testFNhG2lZv" "demangle.test(__vector(long\[2\]))"
+    test_demangling "_D8demangle4testFNhG4lZv" "demangle.test(__vector(long\[4\]))"
+    test_demangling "_D8demangle4testFNhG2fZv" "demangle.test(__vector(float\[2\]))"
+    test_demangling "_D8demangle4testFNhG4fZv" "demangle.test(__vector(float\[4\]))"
+    test_demangling "_D8demangle4testFNhG8fZv" "demangle.test(__vector(float\[8\]))"
+    test_demangling "_D8demangle4testFNhG1dZv" "demangle.test(__vector(double\[1\]))"
+    test_demangling "_D8demangle4testFNhG2dZv" "demangle.test(__vector(double\[2\]))"
+    test_demangling "_D8demangle4testFNhG4dZv" "demangle.test(__vector(double\[4\]))"
+
+    test_demangling "_D8demangle4testFI5identZv" "demangle.test(ident)"
+    test_demangling "_D8demangle4testFI5ident4testZv" "demangle.test(ident.test)"
+    test_demangling "_D8demangle4testFC5classZv" "demangle.test(class)"
+    test_demangling "_D8demangle4testFC5class4testZv" "demangle.test(class.test)"
+    test_demangling "_D8demangle4testFS6structZv" "demangle.test(struct)"
+    test_demangling "_D8demangle4testFS6struct4testZv" "demangle.test(struct.test)"
+    test_demangling "_D8demangle4testFE4enumZv" "demangle.test(enum)"
+    test_demangling "_D8demangle4testFE4enum4testZv" "demangle.test(enum.test)"
+    test_demangling "_D8demangle4testFT7typedefZv" "demangle.test(typedef)"
+    test_demangling "_D8demangle4testFT7typedef4testZv" "demangle.test(typedef.test)"
+
+    test_demangling "_D8demangle4testFJaZv" "demangle.test(out char)"
+    test_demangling "_D8demangle4testFKaZv" "demangle.test(ref char)"
+    test_demangling "_D8demangle4testFLaZv" "demangle.test(lazy char)"
+    test_demangling "_D8demangle4testFMaZv" "demangle.test(scope char)"
+    test_demangling "_D8demangle4testFaXv" "demangle.test(char...)"
+    test_demangling "_D8demangle4testFaYv" "demangle.test(char, ...)"
+    test_demangling "_D8demangle4testFaaYv" "demangle.test(char, char, ...)"
+    test_demangling "_D8demangle4testFaaZv" "demangle.test(char, char)"
+
+    test_demangling "_D8demangle4testFDFZaZv" "demangle.test(char() delegate)"
+    test_demangling "_D8demangle4testFDUZaZv" "demangle.test(extern(C) char() delegate)"
+    test_demangling "_D8demangle4testFDWZaZv" "demangle.test(extern(Windows) char() delegate)"
+    test_demangling "_D8demangle4testFDVZaZv" "demangle.test(extern(Pascal) char() delegate)"
+    test_demangling "_D8demangle4testFDRZaZv" "demangle.test(extern(C++) char() delegate)"
+
+    test_demangling "_D8demangle4testFFZaZv" "demangle.test(char() function)"
+    test_demangling "_D8demangle4testFUZaZv" "demangle.test(extern(C) char() function)"
+    test_demangling "_D8demangle4testFWZaZv" "demangle.test(extern(Windows) char() function)"
+    test_demangling "_D8demangle4testFVZaZv" "demangle.test(extern(Pascal) char() function)"
+    test_demangling "_D8demangle4testFRZaZv" "demangle.test(extern(C++) char() function)"
+
+    test_demangling "_D8demangle4testFDFNaZaZv" "demangle.test(char() pure delegate)"
+    test_demangling "_D8demangle4testFDFNbZaZv" "demangle.test(char() nothrow delegate)"
+    test_demangling "_D8demangle4testFDFNcZaZv" "demangle.test(char() ref delegate)"
+    test_demangling "_D8demangle4testFDFNdZaZv" "demangle.test(char() @property delegate)"
+    test_demangling "_D8demangle4testFDFNeZaZv" "demangle.test(char() @trusted delegate)"
+    test_demangling "_D8demangle4testFDFNfZaZv" "demangle.test(char() @safe delegate)"
+
+    test_demangling "_D8demangle4testFFNaZaZv" "demangle.test(char() pure function)"
+    test_demangling "_D8demangle4testFFNbZaZv" "demangle.test(char() nothrow function)"
+    test_demangling "_D8demangle4testFFNcZaZv" "demangle.test(char() ref function)"
+    test_demangling "_D8demangle4testFFNdZaZv" "demangle.test(char() @property function)"
+    test_demangling "_D8demangle4testFFNeZaZv" "demangle.test(char() @trusted function)"
+    test_demangling "_D8demangle4testFFNfZaZv" "demangle.test(char() @safe function)"
+    test_demangling "_D8demangle4testFFNaNbZaZv" "demangle.test(char() pure nothrow function)"
+    test_demangling "_D8demangle4testFFNbNaZaZv" "demangle.test(char() nothrow pure function)"
+    test_demangling "_D8demangle4testFFNdNfNaZaZv" "demangle.test(char() @property @safe pure function)"
+
+    test_demangling "_D8demangle4test6__vtblZ" "demangle.test.vtbl$"
+    test_demangling "_D8demangle4test6__initZ" "demangle.test.init$"
+    test_demangling "_D8demangle4test12__ModuleInfoZ" "demangle.test.moduleinfo$"
+    test_demangling "_D8demangle4test7__ClassZ" "demangle.test.classinfo$"
+    test_demangling "_D8demangle4test11__InterfaceZ" "demangle.test.interface$"
+
+    test_demangling "_D8demangle4test6__ctorMFZv" "demangle.test.this()"
+    test_demangling "_D8demangle4test6__dtorMFZv" "demangle.test.~this()"
+    test_demangling "_D8demangle4test6__postblitMFZv" "demangle.test.this(this)"
+
+    test_demangling "_D8demangle4testFHAbaZv" "demangle.test(char\[bool\[\]\])"
+    test_demangling "_D8demangle4testFHG42caZv" "demangle.test(char\[creal\[42\]\])"
+    test_demangling "_D8demangle4testFAiXv" "demangle.test(int\[\]...)"
+    test_demangling "_D8demangle4testFLAiXv" "demangle.test(lazy int\[\]...)"
+    test_demangling "_D8demangle4testFAiYv" "demangle.test(int\[\], ...)"
+    test_demangling "_D8demangle4testFLAiYv" "demangle.test(lazy int\[\], ...)"
+    test_demangling "_D8demangle4testFLilZv" "demangle.test(lazy int, long)"
+    test_demangling "_D8demangle4testFLliZv" "demangle.test(lazy long, int)"
+
+    test_demangling "_D8demangle4testPFLAiYi" "demangle.test"
+    test_demangling "_D1a1bi" "a.b"
+    test_demangling "_D1a1bPFiZi" "a.b"
+    test_demangling "_D4test3fooAa" "test.foo"
+    test_demangling "_D4test2dgDFiYd" "test.dg"
+    test_demangling "_D8demangle8demangleFAaZAa" "demangle.demangle(char\[\])"
+    test_demangling "_D6object6Object8opAssignFC6ObjectZi" "object.Object.opAssign(Object)"
+    test_demangling "_D6object6Object8opEqualsFC6ObjectZi" "object.Object.opEquals(Object)"
+    test_demangling "_D8demangle4testFLC6ObjectLDFLiZiZi" "demangle.test(lazy Object, lazy int(lazy int) delegate)"
+    test_demangling "_D6plugin8generateFiiZAya" "plugin.generate(int, int)"
+    test_demangling "_D8demangle3fnAFZv3fnBMFZv" "demangle.fnA().fnB()"
+    test_demangling "_D8demangle4mainFZv1S3fnCFZv" "demangle.main().S.fnC()"
+    test_demangling "_D8demangle4mainFZv1S3fnDMFZv" "demangle.main().S.fnD()"
+    test_demangling "_D8demangle2fnFNgiZNgi" "demangle.fn(inout(int))"
+    test_demangling "_D8demangle4mainFZv5localMFZi" "demangle.main().local()"
+    test_demangling "_D3std5ascii9uppercaseyAa" "std.ascii.uppercase"
+    test_demangling "_D3std6stream9BOMEndianyG5E3std6system6Endian" "std.stream.BOMEndian"
+    test_demangling "_D3std8internal7uni_tab10unicodeNkoyS3std8internal3uni12CodepointSet" "std.internal.uni_tab.unicodeNko"
+    test_demangling "_D2gc2gc2GC6addrOfMFPvZPv" "gc.gc.GC.addrOf(void*)"
+    test_demangling "_D3std7process10setCLOEXECFibZv" "std.process.setCLOEXEC(int, bool)"
+    test_demangling "_D3std6digest2md3MD53putMFNaNbNeMAxhXv" "std.digest.md.MD5.put(scope const(ubyte)\[\]...)"
+    test_demangling "_D3std6mmfile6MmFile13opIndexAssignMFhmZh" "std.mmfile.MmFile.opIndexAssign(ubyte, ulong)"
+    test_demangling "_D3std7process18escapeShellCommandFxAAaXAya" "std.process.escapeShellCommand(const(char\[\]\[\])...)"
+    test_demangling "_D4core4sync5mutex5Mutex6__ctorMFC6ObjectZC4core4sync5mutex5Mutex" "core.sync.mutex.Mutex.this(Object)"
+    test_demangling "_D6object14TypeInfo_Array8argTypesMFNbNfJC8TypeInfoJC8TypeInfoZi" "object.TypeInfo_Array.argTypes(out TypeInfo, out TypeInfo)"
+    test_demangling "_D2rt6dmain211_d_run_mainUiPPaPUAAaZiZi7tryExecMFMDFZvZv" "rt.dmain2._d_run_main(int, char**, extern(C) int(char\[\]\[\]) function*).tryExec(scope void() delegate)"
+    test_demangling "_D6object9Exception6__ctorMFNaNbNfAyaAyamC6object9ThrowableZC9Exception" "object.Exception.this(immutable(char)\[\], immutable(char)\[\], ulong, object.Throwable)"
+    test_demangling "_D3gcc3deh17parse_lsda_headerFPS3gcc6unwind7generic15_Unwind_ContextPhPS3gcc3deh16lsda_header_infoZPh" "gcc.deh.parse_lsda_header(gcc.unwind.generic._Unwind_Context*, ubyte*, gcc.deh.lsda_header_info*)"
+    test_demangling "_D3std6socket23UnknownAddressReference6__ctorMFPS4core3sys5posix3sys6socket8sockaddrkZC3std6socket23UnknownAddressReference" "std.socket.UnknownAddressReference.this(core.sys.posix.sys.socket.sockaddr*, uint)"
+}
+
+proc catch_demangling_errors {command} {
+    if {[catch $command result]} {
+	puts "ERROR: demangle.exp: while running $command: $result"
+    }
+}
+
+# Test support for different demangling styles.  Note that this does
+# not depend upon running the test program and does not depend upon
+# gdb being able to lookup any D symbols.  It simply calls the
+# internal demangler with synthesized strings and tests the results.
+
+# Start with a fresh gdb.
+
+gdb_exit
+gdb_start
+
+if [set_lang_d] {
+    gdb_test_no_output "set width 0"
+
+    # Using catch_demangling_errors this way ensures that, if one of
+    # the functions raises a Tcl error, then it'll get reported, and
+    # the rest of the functions will still run.
+    catch_demangling_errors test_d_demangling
+
+} else {
+    warning "D demangling tests suppressed."
+}

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 5/5] Fix for D demangling in GDB
  2014-01-09 13:13 [PATCH 5/5] Fix for D demangling in GDB Iain Buclaw
@ 2014-01-09 21:54 ` Tom Tromey
  2014-01-10 13:24   ` Iain Buclaw
  0 siblings, 1 reply; 13+ messages in thread
From: Tom Tromey @ 2014-01-09 21:54 UTC (permalink / raw)
  To: Iain Buclaw; +Cc: gdb-patches

>>>>> "Iain" == Iain Buclaw <ibuclaw@gdcproject.org> writes:

Iain>         * d-lang.h (d_parse_symbol): Add declaration.
Iain>         * d-lang.c (extract_identifiers)
Iain>         (extract_type_info): Remove functions.
Iain>         (d_demangle): Use d_parse_symbol implemented in d-support.c to
Iain>         demangle D symbols.
Iain>         * d-support.c: New file.

The usual approach in cases like this is to do a "pure move" patch to
move the functions to another file, followed by a second patch to
implement the fixes.

It's also worth noting that with a bit more work you could push the D
demangler into libiberty (see ada_demangle there) and then get
demangling from "nm" and the other binutils.

Iain> +proc catch_demangling_errors {command} {
Iain> +    if {[catch $command result]} {
Iain> +	puts "ERROR: demangle.exp: while running $command: $result"
Iain> +    }
Iain> +}

Iain> +    # Using catch_demangling_errors this way ensures that, if one of
Iain> +    # the functions raises a Tcl error, then it'll get reported, and
Iain> +    # the rest of the functions will still run.
Iain> +    catch_demangling_errors test_d_demangling

I don't think this stuff is needed.  Usually we just let Tcl errors keep
going, since ordinarily they represent bugs in the test case.  Is there
a particular failure you were seeing?

Iain> +
Iain> +} else {
Iain> +    warning "D demangling tests suppressed."

I think "unsupported" instead of "warning" here.

Tom

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 5/5] Fix for D demangling in GDB
  2014-01-09 21:54 ` Tom Tromey
@ 2014-01-10 13:24   ` Iain Buclaw
  2014-01-10 14:51     ` Iain Buclaw
                       ` (2 more replies)
  0 siblings, 3 replies; 13+ messages in thread
From: Iain Buclaw @ 2014-01-10 13:24 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On 9 January 2014 21:54, Tom Tromey <tromey@redhat.com> wrote:
>>>>>> "Iain" == Iain Buclaw <ibuclaw@gdcproject.org> writes:
>
> Iain>         * d-lang.h (d_parse_symbol): Add declaration.
> Iain>         * d-lang.c (extract_identifiers)
> Iain>         (extract_type_info): Remove functions.
> Iain>         (d_demangle): Use d_parse_symbol implemented in d-support.c to
> Iain>         demangle D symbols.
> Iain>         * d-support.c: New file.
>
> The usual approach in cases like this is to do a "pure move" patch to
> move the functions to another file, followed by a second patch to
> implement the fixes.
>

OK, I'll split it into two separate patches.

> It's also worth noting that with a bit more work you could push the D
> demangler into libiberty (see ada_demangle there) and then get
> demangling from "nm" and the other binutils.
>

That sounds like a good plan.  I'll keep a note to get round to do that.


> Iain> +proc catch_demangling_errors {command} {
> Iain> +    if {[catch $command result]} {
> Iain> + puts "ERROR: demangle.exp: while running $command: $result"
> Iain> +    }
> Iain> +}
>
> Iain> +    # Using catch_demangling_errors this way ensures that, if one of
> Iain> +    # the functions raises a Tcl error, then it'll get reported, and
> Iain> +    # the rest of the functions will still run.
> Iain> +    catch_demangling_errors test_d_demangling
>
> I don't think this stuff is needed.  Usually we just let Tcl errors keep
> going, since ordinarily they represent bugs in the test case.  Is there
> a particular failure you were seeing?
>

This was copied from cp-demangle.exp.  I believe it is written that
way so that all demangle tests are ran, rather than stopping at the
first error?

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 5/5] Fix for D demangling in GDB
  2014-01-10 13:24   ` Iain Buclaw
@ 2014-01-10 14:51     ` Iain Buclaw
  2014-01-10 21:43       ` Tom Tromey
  2014-01-10 15:05     ` Iain Buclaw
  2014-01-10 21:22     ` Tom Tromey
  2 siblings, 1 reply; 13+ messages in thread
From: Iain Buclaw @ 2014-01-10 14:51 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 1026 bytes --]

On 10 January 2014 13:24, Iain Buclaw <ibuclaw@gdcproject.org> wrote:
> On 9 January 2014 21:54, Tom Tromey <tromey@redhat.com> wrote:
>> The usual approach in cases like this is to do a "pure move" patch to
>> move the functions to another file, followed by a second patch to
>> implement the fixes.
>>
>
> OK, I'll split it into two separate patches.
>

Couldn't find an easy way to move, then fix.  So I did fix, then move.

First change, fixes D demangling in GDB as per first post.

2014-01-10  Iain Buclaw  <ibuclaw@gdcproject.org>

    gdb/

        * d-lang.h (d_parse_symbol): Add declaration.
        * d-lang.c (extract_identifiers)
        (extract_type_info): Remove functions.
        (parse_call_convention, parse_attributes)
        (parse_function_types, parse_function_args)
        (parse_type, parse_identifier, call_convention_p)
        (d_parse_symbol): New functions.
        (d_demangle): Use d_parse_symbol to demangle D symbols.

    gdb/testsuite/

        * gdb.dlang/demangle.exp: New file.

---

[-- Attachment #2: dlang-p5a.patch --]
[-- Type: text/x-patch, Size: 36369 bytes --]

 gdb/ChangeLog                        |   11 +
 gdb/d-lang.c                         |  722 +++++++++++++++++++++++++++-------
 gdb/d-lang.h                         |    2 +
 gdb/testsuite/ChangeLog              |    4 +
 gdb/testsuite/gdb.dlang/demangle.exp |  223 +++++++++++
 5 files changed, 824 insertions(+), 138 deletions(-)

diff --git a/gdb/d-lang.c b/gdb/d-lang.c
index efd0813..f45fae8 100644
--- a/gdb/d-lang.c
+++ b/gdb/d-lang.c
@@ -23,11 +23,13 @@
 #include "varobj.h"
 #include "d-lang.h"
 #include "c-lang.h"
-#include <string.h>
 #include "parser-defs.h"
 #include "gdb_obstack.h"
 
-#include <ctype.h>
+#include "safe-ctype.h"
+
+static const char *parse_function_args (struct obstack *, const char *);
+static const char *parse_type (struct obstack *, const char *);
 
 /* The name of the symbol to use to get the name of the main subprogram.  */
 static const char D_MAIN[] = "D main";
@@ -50,175 +52,619 @@ d_main_name (void)
   return NULL;
 }
 
-/* Extract identifiers from MANGLED_STR and append it to TEMPBUF.
-   Return 1 on success or 0 on failure.  */
-static int
-extract_identifiers (const char *mangled_str, struct obstack *tempbuf)
+/* 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 && *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)
 {
-  long i = 0;
+  if (mangle == NULL || *mangle == '\0')
+    return mangle;
 
-  while (isdigit (*mangled_str))
+  switch (*mangle)
     {
-      char *end_ptr;
-
-      i = strtol (mangled_str, &end_ptr, 10);
-      mangled_str = end_ptr;
-      if (i <= 0 || strlen (mangled_str) < i)
-        return 0;
-      obstack_grow (tempbuf, mangled_str, i);
-      mangled_str += i;
-      obstack_grow_str (tempbuf, ".");
+    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;
     }
-  if (*mangled_str == '\0' || i == 0)
-    return 0;
-  obstack_blank (tempbuf, -1);
-  return 1;
 }
 
-/* Extract and demangle type from MANGLED_STR and append it to TEMPBUF.
-   Return 1 on success or 0 on failure.  */
+/* 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;
+}
+
 static int
-extract_type_info (const char *mangled_str, struct obstack *tempbuf)
+call_convention_p (const char *mangle)
 {
-  if (*mangled_str == '\0')
-    return 0;
-  switch (*mangled_str++)
+  size_t i;
+
+  switch (*mangle)
     {
-      case 'A': /* dynamic array */
-      case 'G': /* static array */
-      case 'H': /* associative array */
-	if (!extract_type_info (mangled_str, tempbuf))
-	  return 0;
-	obstack_grow_str (tempbuf, "[]");
-	return 1;
-      case 'P': /* pointer */
-	if (!extract_type_info (mangled_str, tempbuf))
-	  return 0;
-	obstack_grow_str (tempbuf, "*");
-	return 1;
-      case 'R': /* reference */
-	if (!extract_type_info (mangled_str, tempbuf))
-	  return 0;
-	obstack_grow_str (tempbuf, "&");
-	return 1;
-      case 'Z': /* return value */
-	return extract_type_info (mangled_str, tempbuf);
-      case 'J': /* out */
-	obstack_grow_str (tempbuf, "out ");
-	return extract_type_info (mangled_str, tempbuf);
-      case 'K': /* inout */
-	obstack_grow_str (tempbuf, "inout ");
-	return extract_type_info (mangled_str, tempbuf);
-      case 'E': /* enum */
-      case 'T': /* typedef */
-      case 'D': /* delegate */
-      case 'C': /* class */
-      case 'S': /* struct */
-	return extract_identifiers (mangled_str, tempbuf);
-
-      /* basic types: */
-      case 'n': obstack_grow_str (tempbuf, "none"); return 1;
-      case 'v': obstack_grow_str (tempbuf, "void"); return 1;
-      case 'g': obstack_grow_str (tempbuf, "byte"); return 1;
-      case 'h': obstack_grow_str (tempbuf, "ubyte"); return 1;
-      case 's': obstack_grow_str (tempbuf, "short"); return 1;
-      case 't': obstack_grow_str (tempbuf, "ushort"); return 1;
-      case 'i': obstack_grow_str (tempbuf, "int"); return 1;
-      case 'k': obstack_grow_str (tempbuf, "uint"); return 1;
-      case 'l': obstack_grow_str (tempbuf, "long"); return 1;
-      case 'm': obstack_grow_str (tempbuf, "ulong"); return 1;
-      case 'f': obstack_grow_str (tempbuf, "float"); return 1;
-      case 'd': obstack_grow_str (tempbuf, "double"); return 1;
-      case 'e': obstack_grow_str (tempbuf, "real"); return 1;
-
-      /* imaginary and complex: */
-      case 'o': obstack_grow_str (tempbuf, "ifloat"); return 1;
-      case 'p': obstack_grow_str (tempbuf, "idouble"); return 1;
-      case 'j': obstack_grow_str (tempbuf, "ireal"); return 1;
-      case 'q': obstack_grow_str (tempbuf, "cfloat"); return 1;
-      case 'r': obstack_grow_str (tempbuf, "cdouble"); return 1;
-      case 'c': obstack_grow_str (tempbuf, "creal"); return 1;
-
-      /* other types: */
-      case 'b': obstack_grow_str (tempbuf, "bit"); return 1;
-      case 'a': obstack_grow_str (tempbuf, "char"); return 1;
-      case 'u': obstack_grow_str (tempbuf, "wchar"); return 1;
-      case 'w': obstack_grow_str (tempbuf, "dchar"); return 1;
-
-      default:
-	obstack_grow_str (tempbuf, "unknown");
-	return 1;
+    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 (mangle && 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 && !ISDIGIT (*mangle))
+	    {
+	      saved = obstack_next_free (tempbuf);
+	      mangle = parse_type (tempbuf, mangle);
+	      obstack_next_free (tempbuf) = saved;
+	    }
+	}
+    }
+  while (mangle && ISDIGIT (*mangle));
+
+  return mangle;
+}
+
 /* Implements the la_demangle language_defn routine for language D.  */
 char *
 d_demangle (const char *symbol, int options)
 {
   struct obstack tempbuf;
-  char *out_str;
-  unsigned char is_func = 0;
+  char *result;
 
-  if (symbol == NULL)
+  if (symbol == NULL || *symbol == '\0')
     return NULL;
   else if (strcmp (symbol, "_Dmain") == 0)
     return xstrdup ("D main");
 
   obstack_init (&tempbuf);
-  
-  if (symbol[0] == '_' && symbol[1] == 'D')
-    {
-      symbol += 2;
-      is_func = 1;
-    }
-  else if (strncmp (symbol, "__Class_", 8) == 0)
-    symbol += 8;
-  else if (strncmp (symbol, "__init_", 7) == 0)
-    symbol += 7;
-  else if (strncmp (symbol, "__vtbl_", 7) == 0)
-    symbol += 7;
-  else if (strncmp (symbol, "__modctor_", 10) == 0)
-    symbol += 10;
-  else if (strncmp (symbol, "__moddtor_", 10) == 0)
-    symbol += 10;
-  else if (strncmp (symbol, "__ModuleInfo_", 13) == 0)
-    symbol += 13;
+
+  if (strncmp (symbol, "_D", 2) == 0)
+    symbol += 2;
   else
     {
       obstack_free (&tempbuf, NULL);
       return NULL;
     }
-  
-  if (!extract_identifiers (symbol, &tempbuf))
+
+  if (d_parse_symbol (&tempbuf, symbol) != NULL)
     {
+      obstack_grow_str0 (&tempbuf, "");
+      result = xstrdup (obstack_finish (&tempbuf));
       obstack_free (&tempbuf, NULL);
-      return NULL;
     }
-
-  obstack_grow_str (&tempbuf, "(");
-  if (is_func == 1 && *symbol == 'F')
+  else
     {
-      symbol++;
-      while (*symbol != '\0' && *symbol != 'Z')
-	{
-	  if (is_func == 1)
-	    is_func++;
-	  else
-	    obstack_grow_str (&tempbuf, ", ");
-	  if (!extract_type_info (symbol, &tempbuf))
-	    {
-	      obstack_free (&tempbuf, NULL);
-	      return NULL;
-	   }
-	}
-     }
-  obstack_grow_str0 (&tempbuf, ")");
-
-  /* Doesn't display the return type, but wouldn't be too hard to do.  */
+      obstack_free (&tempbuf, NULL);
+      return NULL;
+    }
 
-  out_str = xstrdup (obstack_finish (&tempbuf));
-  obstack_free (&tempbuf, NULL);
-  return out_str;
+  return result;
 }
 
 /* Table mapping opcodes into strings for printing operators
diff --git a/gdb/d-lang.h b/gdb/d-lang.h
index 455c808..a1b4456 100644
--- a/gdb/d-lang.h
+++ b/gdb/d-lang.h
@@ -62,6 +62,8 @@ extern char *d_demangle (const char *mangled, int options);
 
 extern const struct builtin_d_type *builtin_d_type (struct gdbarch *);
 
+extern const char *d_parse_symbol (struct obstack *, const char *);
+
 /* Defined in d-valprint.c  */
 
 extern void d_val_print (struct type *type, const gdb_byte *valaddr,
diff --git a/gdb/testsuite/gdb.dlang/demangle.exp b/gdb/testsuite/gdb.dlang/demangle.exp
new file mode 100644
index 0000000..4c237c5
--- /dev/null
+++ b/gdb/testsuite/gdb.dlang/demangle.exp
@@ -0,0 +1,223 @@
+# Copyright (C) 2014 Free Software Foundation, Inc.
+
+# 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/>.
+
+# Test D demangling.
+# The tests here intentionally do not require a D compiler.
+
+load_lib "d-support.exp"
+
+if { [skip_d_tests] } { continue }
+
+### Utility function for test_demangling and test_demangling_exact.
+proc test_demangling {test result} {
+    gdb_test_exact "maintenance demangle $test" $result $test
+}
+
+#
+#  Test D demangler
+#
+
+proc test_d_demangling {} {
+
+    test_demangling "_Dmain" "D main"
+
+    test_demangling "_D8demangle4testFaZv" "demangle.test(char)"
+    test_demangling "_D8demangle4testFbZv" "demangle.test(bool)"
+    test_demangling "_D8demangle4testFcZv" "demangle.test(creal)"
+    test_demangling "_D8demangle4testFdZv" "demangle.test(double)"
+    test_demangling "_D8demangle4testFeZv" "demangle.test(real)"
+    test_demangling "_D8demangle4testFfZv" "demangle.test(float)"
+    test_demangling "_D8demangle4testFgZv" "demangle.test(byte)"
+    test_demangling "_D8demangle4testFhZv" "demangle.test(ubyte)"
+    test_demangling "_D8demangle4testFiZv" "demangle.test(int)"
+    test_demangling "_D8demangle4testFjZv" "demangle.test(ireal)"
+    test_demangling "_D8demangle4testFkZv" "demangle.test(uint)"
+    test_demangling "_D8demangle4testFlZv" "demangle.test(long)"
+    test_demangling "_D8demangle4testFmZv" "demangle.test(ulong)"
+    test_demangling "_D8demangle4testFnZv" "demangle.test(none)"
+    test_demangling "_D8demangle4testFoZv" "demangle.test(ifloat)"
+    test_demangling "_D8demangle4testFpZv" "demangle.test(idouble)"
+    test_demangling "_D8demangle4testFqZv" "demangle.test(cfloat)"
+    test_demangling "_D8demangle4testFrZv" "demangle.test(cdouble)"
+    test_demangling "_D8demangle4testFsZv" "demangle.test(short)"
+    test_demangling "_D8demangle4testFtZv" "demangle.test(ushort)"
+    test_demangling "_D8demangle4testFuZv" "demangle.test(wchar)"
+    test_demangling "_D8demangle4testFvZv" "demangle.test(void)"
+    test_demangling "_D8demangle4testFwZv" "demangle.test(dchar)"
+
+    test_demangling "_D8demangle4testFOaZv" "demangle.test(shared(char))"
+    test_demangling "_D8demangle4testFxaZv" "demangle.test(const(char))"
+    test_demangling "_D8demangle4testFyaZv" "demangle.test(immutable(char))"
+    test_demangling "_D8demangle4testFNgaZv" "demangle.test(inout(char))"
+    test_demangling "_D8demangle4testFOxaZv" "demangle.test(shared(const(char)))"
+    test_demangling "_D8demangle4testFONgaZv" "demangle.test(shared(inout(char)))"
+    test_demangling "_D8demangle4testFAaZv" "demangle.test(char\[\])"
+    test_demangling "_D8demangle4testFAAaZv" "demangle.test(char\[\]\[\])"
+    test_demangling "_D8demangle4testFAAAaZv" "demangle.test(char\[\]\[\]\[\])"
+    test_demangling "_D8demangle4testFG42aZv" "demangle.test(char\[42\])"
+    test_demangling "_D8demangle4testFG42G42aZv" "demangle.test(char\[42\]\[42\])"
+    test_demangling "_D8demangle4testFG42G42G42aZv" "demangle.test(char\[42\]\[42\]\[42\])"
+    test_demangling "_D8demangle4testFG1234567890aZv" "demangle.test(char\[1234567890\])"
+    test_demangling "_D8demangle4testFHaaZv" "demangle.test(char\[char\])"
+    test_demangling "_D8demangle4testFHHaaaZv" "demangle.test(char\[char\[char\]\])"
+    test_demangling "_D8demangle4testFPaZv" "demangle.test(char*)"
+    test_demangling "_D8demangle4testFPPaZv" "demangle.test(char**)"
+    test_demangling "_D8demangle4testFPPPaZv" "demangle.test(char***)"
+
+    test_demangling "_D8demangle4testFNhG8gZv" "demangle.test(__vector(byte\[8\]))"
+    test_demangling "_D8demangle4testFNhG16gZv" "demangle.test(__vector(byte\[16\]))"
+    test_demangling "_D8demangle4testFNhG32gZv" "demangle.test(__vector(byte\[32\]))"
+    test_demangling "_D8demangle4testFNhG4sZv" "demangle.test(__vector(short\[4\]))"
+    test_demangling "_D8demangle4testFNhG8sZv" "demangle.test(__vector(short\[8\]))"
+    test_demangling "_D8demangle4testFNhG16sZv" "demangle.test(__vector(short\[16\]))"
+    test_demangling "_D8demangle4testFNhG2iZv" "demangle.test(__vector(int\[2\]))"
+    test_demangling "_D8demangle4testFNhG4iZv" "demangle.test(__vector(int\[4\]))"
+    test_demangling "_D8demangle4testFNhG8iZv" "demangle.test(__vector(int\[8\]))"
+    test_demangling "_D8demangle4testFNhG1lZv" "demangle.test(__vector(long\[1\]))"
+    test_demangling "_D8demangle4testFNhG2lZv" "demangle.test(__vector(long\[2\]))"
+    test_demangling "_D8demangle4testFNhG4lZv" "demangle.test(__vector(long\[4\]))"
+    test_demangling "_D8demangle4testFNhG2fZv" "demangle.test(__vector(float\[2\]))"
+    test_demangling "_D8demangle4testFNhG4fZv" "demangle.test(__vector(float\[4\]))"
+    test_demangling "_D8demangle4testFNhG8fZv" "demangle.test(__vector(float\[8\]))"
+    test_demangling "_D8demangle4testFNhG1dZv" "demangle.test(__vector(double\[1\]))"
+    test_demangling "_D8demangle4testFNhG2dZv" "demangle.test(__vector(double\[2\]))"
+    test_demangling "_D8demangle4testFNhG4dZv" "demangle.test(__vector(double\[4\]))"
+
+    test_demangling "_D8demangle4testFI5identZv" "demangle.test(ident)"
+    test_demangling "_D8demangle4testFI5ident4testZv" "demangle.test(ident.test)"
+    test_demangling "_D8demangle4testFC5classZv" "demangle.test(class)"
+    test_demangling "_D8demangle4testFC5class4testZv" "demangle.test(class.test)"
+    test_demangling "_D8demangle4testFS6structZv" "demangle.test(struct)"
+    test_demangling "_D8demangle4testFS6struct4testZv" "demangle.test(struct.test)"
+    test_demangling "_D8demangle4testFE4enumZv" "demangle.test(enum)"
+    test_demangling "_D8demangle4testFE4enum4testZv" "demangle.test(enum.test)"
+    test_demangling "_D8demangle4testFT7typedefZv" "demangle.test(typedef)"
+    test_demangling "_D8demangle4testFT7typedef4testZv" "demangle.test(typedef.test)"
+
+    test_demangling "_D8demangle4testFJaZv" "demangle.test(out char)"
+    test_demangling "_D8demangle4testFKaZv" "demangle.test(ref char)"
+    test_demangling "_D8demangle4testFLaZv" "demangle.test(lazy char)"
+    test_demangling "_D8demangle4testFMaZv" "demangle.test(scope char)"
+    test_demangling "_D8demangle4testFaXv" "demangle.test(char...)"
+    test_demangling "_D8demangle4testFaYv" "demangle.test(char, ...)"
+    test_demangling "_D8demangle4testFaaYv" "demangle.test(char, char, ...)"
+    test_demangling "_D8demangle4testFaaZv" "demangle.test(char, char)"
+
+    test_demangling "_D8demangle4testFDFZaZv" "demangle.test(char() delegate)"
+    test_demangling "_D8demangle4testFDUZaZv" "demangle.test(extern(C) char() delegate)"
+    test_demangling "_D8demangle4testFDWZaZv" "demangle.test(extern(Windows) char() delegate)"
+    test_demangling "_D8demangle4testFDVZaZv" "demangle.test(extern(Pascal) char() delegate)"
+    test_demangling "_D8demangle4testFDRZaZv" "demangle.test(extern(C++) char() delegate)"
+
+    test_demangling "_D8demangle4testFFZaZv" "demangle.test(char() function)"
+    test_demangling "_D8demangle4testFUZaZv" "demangle.test(extern(C) char() function)"
+    test_demangling "_D8demangle4testFWZaZv" "demangle.test(extern(Windows) char() function)"
+    test_demangling "_D8demangle4testFVZaZv" "demangle.test(extern(Pascal) char() function)"
+    test_demangling "_D8demangle4testFRZaZv" "demangle.test(extern(C++) char() function)"
+
+    test_demangling "_D8demangle4testFDFNaZaZv" "demangle.test(char() pure delegate)"
+    test_demangling "_D8demangle4testFDFNbZaZv" "demangle.test(char() nothrow delegate)"
+    test_demangling "_D8demangle4testFDFNcZaZv" "demangle.test(char() ref delegate)"
+    test_demangling "_D8demangle4testFDFNdZaZv" "demangle.test(char() @property delegate)"
+    test_demangling "_D8demangle4testFDFNeZaZv" "demangle.test(char() @trusted delegate)"
+    test_demangling "_D8demangle4testFDFNfZaZv" "demangle.test(char() @safe delegate)"
+
+    test_demangling "_D8demangle4testFFNaZaZv" "demangle.test(char() pure function)"
+    test_demangling "_D8demangle4testFFNbZaZv" "demangle.test(char() nothrow function)"
+    test_demangling "_D8demangle4testFFNcZaZv" "demangle.test(char() ref function)"
+    test_demangling "_D8demangle4testFFNdZaZv" "demangle.test(char() @property function)"
+    test_demangling "_D8demangle4testFFNeZaZv" "demangle.test(char() @trusted function)"
+    test_demangling "_D8demangle4testFFNfZaZv" "demangle.test(char() @safe function)"
+    test_demangling "_D8demangle4testFFNaNbZaZv" "demangle.test(char() pure nothrow function)"
+    test_demangling "_D8demangle4testFFNbNaZaZv" "demangle.test(char() nothrow pure function)"
+    test_demangling "_D8demangle4testFFNdNfNaZaZv" "demangle.test(char() @property @safe pure function)"
+
+    test_demangling "_D8demangle4test6__vtblZ" "demangle.test.vtbl$"
+    test_demangling "_D8demangle4test6__initZ" "demangle.test.init$"
+    test_demangling "_D8demangle4test12__ModuleInfoZ" "demangle.test.moduleinfo$"
+    test_demangling "_D8demangle4test7__ClassZ" "demangle.test.classinfo$"
+    test_demangling "_D8demangle4test11__InterfaceZ" "demangle.test.interface$"
+
+    test_demangling "_D8demangle4test6__ctorMFZv" "demangle.test.this()"
+    test_demangling "_D8demangle4test6__dtorMFZv" "demangle.test.~this()"
+    test_demangling "_D8demangle4test6__postblitMFZv" "demangle.test.this(this)"
+
+    test_demangling "_D8demangle4testFHAbaZv" "demangle.test(char\[bool\[\]\])"
+    test_demangling "_D8demangle4testFHG42caZv" "demangle.test(char\[creal\[42\]\])"
+    test_demangling "_D8demangle4testFAiXv" "demangle.test(int\[\]...)"
+    test_demangling "_D8demangle4testFLAiXv" "demangle.test(lazy int\[\]...)"
+    test_demangling "_D8demangle4testFAiYv" "demangle.test(int\[\], ...)"
+    test_demangling "_D8demangle4testFLAiYv" "demangle.test(lazy int\[\], ...)"
+    test_demangling "_D8demangle4testFLilZv" "demangle.test(lazy int, long)"
+    test_demangling "_D8demangle4testFLliZv" "demangle.test(lazy long, int)"
+
+    test_demangling "_D8demangle4testPFLAiYi" "demangle.test"
+    test_demangling "_D1a1bi" "a.b"
+    test_demangling "_D1a1bPFiZi" "a.b"
+    test_demangling "_D4test3fooAa" "test.foo"
+    test_demangling "_D4test2dgDFiYd" "test.dg"
+    test_demangling "_D8demangle8demangleFAaZAa" "demangle.demangle(char\[\])"
+    test_demangling "_D6object6Object8opAssignFC6ObjectZi" "object.Object.opAssign(Object)"
+    test_demangling "_D6object6Object8opEqualsFC6ObjectZi" "object.Object.opEquals(Object)"
+    test_demangling "_D8demangle4testFLC6ObjectLDFLiZiZi" "demangle.test(lazy Object, lazy int(lazy int) delegate)"
+    test_demangling "_D6plugin8generateFiiZAya" "plugin.generate(int, int)"
+    test_demangling "_D8demangle3fnAFZv3fnBMFZv" "demangle.fnA().fnB()"
+    test_demangling "_D8demangle4mainFZv1S3fnCFZv" "demangle.main().S.fnC()"
+    test_demangling "_D8demangle4mainFZv1S3fnDMFZv" "demangle.main().S.fnD()"
+    test_demangling "_D8demangle2fnFNgiZNgi" "demangle.fn(inout(int))"
+    test_demangling "_D8demangle4mainFZv5localMFZi" "demangle.main().local()"
+    test_demangling "_D3std5ascii9uppercaseyAa" "std.ascii.uppercase"
+    test_demangling "_D3std6stream9BOMEndianyG5E3std6system6Endian" "std.stream.BOMEndian"
+    test_demangling "_D3std8internal7uni_tab10unicodeNkoyS3std8internal3uni12CodepointSet" "std.internal.uni_tab.unicodeNko"
+    test_demangling "_D2gc2gc2GC6addrOfMFPvZPv" "gc.gc.GC.addrOf(void*)"
+    test_demangling "_D3std7process10setCLOEXECFibZv" "std.process.setCLOEXEC(int, bool)"
+    test_demangling "_D3std6digest2md3MD53putMFNaNbNeMAxhXv" "std.digest.md.MD5.put(scope const(ubyte)\[\]...)"
+    test_demangling "_D3std6mmfile6MmFile13opIndexAssignMFhmZh" "std.mmfile.MmFile.opIndexAssign(ubyte, ulong)"
+    test_demangling "_D3std7process18escapeShellCommandFxAAaXAya" "std.process.escapeShellCommand(const(char\[\]\[\])...)"
+    test_demangling "_D4core4sync5mutex5Mutex6__ctorMFC6ObjectZC4core4sync5mutex5Mutex" "core.sync.mutex.Mutex.this(Object)"
+    test_demangling "_D6object14TypeInfo_Array8argTypesMFNbNfJC8TypeInfoJC8TypeInfoZi" "object.TypeInfo_Array.argTypes(out TypeInfo, out TypeInfo)"
+    test_demangling "_D2rt6dmain211_d_run_mainUiPPaPUAAaZiZi7tryExecMFMDFZvZv" "rt.dmain2._d_run_main(int, char**, extern(C) int(char\[\]\[\]) function*).tryExec(scope void() delegate)"
+    test_demangling "_D6object9Exception6__ctorMFNaNbNfAyaAyamC6object9ThrowableZC9Exception" "object.Exception.this(immutable(char)\[\], immutable(char)\[\], ulong, object.Throwable)"
+    test_demangling "_D3gcc3deh17parse_lsda_headerFPS3gcc6unwind7generic15_Unwind_ContextPhPS3gcc3deh16lsda_header_infoZPh" "gcc.deh.parse_lsda_header(gcc.unwind.generic._Unwind_Context*, ubyte*, gcc.deh.lsda_header_info*)"
+    test_demangling "_D3std6socket23UnknownAddressReference6__ctorMFPS4core3sys5posix3sys6socket8sockaddrkZC3std6socket23UnknownAddressReference" "std.socket.UnknownAddressReference.this(core.sys.posix.sys.socket.sockaddr*, uint)"
+}
+
+proc catch_demangling_errors {command} {
+    if {[catch $command result]} {
+	puts "ERROR: demangle.exp: while running $command: $result"
+    }
+}
+
+# Test support for different demangling styles.  Note that this does
+# not depend upon running the test program and does not depend upon
+# gdb being able to lookup any D symbols.  It simply calls the
+# internal demangler with synthesized strings and tests the results.
+
+# Start with a fresh gdb.
+
+gdb_exit
+gdb_start
+
+if [set_lang_d] {
+    gdb_test_no_output "set width 0"
+
+    # Using catch_demangling_errors this way ensures that, if one of
+    # the functions raises a Tcl error, then it'll get reported, and
+    # the rest of the functions will still run.
+    catch_demangling_errors test_d_demangling
+
+} else {
+    warning "D demangling tests suppressed."
+}

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 5/5] Fix for D demangling in GDB
  2014-01-10 13:24   ` Iain Buclaw
  2014-01-10 14:51     ` Iain Buclaw
@ 2014-01-10 15:05     ` Iain Buclaw
  2014-01-10 21:45       ` Tom Tromey
  2014-01-10 21:22     ` Tom Tromey
  2 siblings, 1 reply; 13+ messages in thread
From: Iain Buclaw @ 2014-01-10 15:05 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 857 bytes --]

On 10 January 2014 13:24, Iain Buclaw <ibuclaw@gdcproject.org> wrote:
> On 9 January 2014 21:54, Tom Tromey <tromey@redhat.com> wrote:
>> The usual approach in cases like this is to do a "pure move" patch to
>> move the functions to another file, followed by a second patch to
>> implement the fixes.
>>
>
> OK, I'll split it into two separate patches.
>

Second change, move D demangling routines out of d-lang.c into
d-support.c, which is intended to house other language support
functions that don't really have a home elsewhere.


2014-01-10  Iain Buclaw  <ibuclaw@gdcproject.org>

    gdb/

        * d-lang.c (parse_call_convention)
        (parse_attributes, parse_function_types)
        (parse_function_args, parse_type, parse_identifier)
        (call_convention_p, d_parse_symbol): Move functions to ...
        * d-support.c: ... New file.

---

[-- Attachment #2: dlang-p5b.patch --]
[-- Type: text/x-patch, Size: 32895 bytes --]

 gdb/ChangeLog   |    8 +
 gdb/Makefile.in |    3 +-
 gdb/d-lang.c    |  583 ----------------------------------------------------
 gdb/d-lang.h    |    2 +
 gdb/d-support.c |  606 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 618 insertions(+), 584 deletions(-)

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 824b26b..f557bfe 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -727,7 +727,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
 	charset.c cleanups.c cli-out.c coffread.c coff-pe-read.c \
 	complaints.c completer.c continuations.c corefile.c corelow.c \
 	cp-abi.c cp-support.c cp-namespace.c cp-valprint.c \
-	d-lang.c d-valprint.c \
+	d-lang.c d-support.c d-valprint.c \
 	cp-name-parser.y \
 	dbxread.c demangle.c dictionary.c disasm.c doublest.c dummy-frame.c \
 	dwarf2expr.c dwarf2loc.c dwarf2read.c dwarf2-frame.c \
@@ -947,6 +947,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
 	inline-frame.o \
 	gnu-v2-abi.o gnu-v3-abi.o cp-abi.o cp-support.o \
 	cp-namespace.o \
+	d-support.o \
 	reggroups.o regset.o \
 	trad-frame.o \
 	tramp-frame.o \
diff --git a/gdb/d-lang.c b/gdb/d-lang.c
index f45fae8..9fdd7bb 100644
--- a/gdb/d-lang.c
+++ b/gdb/d-lang.c
@@ -26,11 +26,6 @@
 #include "parser-defs.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 *);
-
 /* The name of the symbol to use to get the name of the main subprogram.  */
 static const char D_MAIN[] = "D main";
 
@@ -52,584 +47,6 @@ d_main_name (void)
   return NULL;
 }
 
-/* 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 && *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;
-}
-
-static int
-call_convention_p (const char *mangle)
-{
-  size_t i;
-
-  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 (mangle && 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 && !ISDIGIT (*mangle))
-	    {
-	      saved = obstack_next_free (tempbuf);
-	      mangle = parse_type (tempbuf, mangle);
-	      obstack_next_free (tempbuf) = saved;
-	    }
-	}
-    }
-  while (mangle && ISDIGIT (*mangle));
-
-  return mangle;
-}
-
 /* Implements the la_demangle language_defn routine for language D.  */
 char *
 d_demangle (const char *symbol, int options)
diff --git a/gdb/d-lang.h b/gdb/d-lang.h
index a1b4456..ca9126b 100644
--- a/gdb/d-lang.h
+++ b/gdb/d-lang.h
@@ -62,6 +62,8 @@ extern char *d_demangle (const char *mangled, int options);
 
 extern const struct builtin_d_type *builtin_d_type (struct gdbarch *);
 
+/* Defined in d-support.c  */
+
 extern const char *d_parse_symbol (struct obstack *, const char *);
 
 /* Defined in d-valprint.c  */
diff --git a/gdb/d-support.c b/gdb/d-support.c
new file mode 100644
index 0000000..318acf8
--- /dev/null
+++ b/gdb/d-support.c
@@ -0,0 +1,606 @@
+/* D language support routines for GDB, the GNU debugger.
+
+   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 && *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;
+}
+
+static int
+call_convention_p (const char *mangle)
+{
+  size_t i;
+
+  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 (mangle && 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 && !ISDIGIT (*mangle))
+	    {
+	      saved = obstack_next_free (tempbuf);
+	      mangle = parse_type (tempbuf, mangle);
+	      obstack_next_free (tempbuf) = saved;
+	    }
+	}
+    }
+  while (mangle && ISDIGIT (*mangle));
+
+  return mangle;
+}
+

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 5/5] Fix for D demangling in GDB
  2014-01-10 13:24   ` Iain Buclaw
  2014-01-10 14:51     ` Iain Buclaw
  2014-01-10 15:05     ` Iain Buclaw
@ 2014-01-10 21:22     ` Tom Tromey
  2014-01-10 23:09       ` Iain Buclaw
  2 siblings, 1 reply; 13+ messages in thread
From: Tom Tromey @ 2014-01-10 21:22 UTC (permalink / raw)
  To: Iain Buclaw; +Cc: gdb-patches

>>>>> "Iain" == Iain Buclaw <ibuclaw@gdcproject.org> writes:

Tom> It's also worth noting that with a bit more work you could push the D
Tom> demangler into libiberty (see ada_demangle there) and then get
Tom> demangling from "nm" and the other binutils.

Iain> That sounds like a good plan.  I'll keep a note to get round to do that.

Just FYI - I'm not sure if you know this or not, but libiberty is
canonically maintained in the GCC tree, so if you do this, it has to be
submitted there first.  Then it will be merged (either by me, or by
whoever else seems to be doing (semi-)automated merges) into
binutils-gdb.git.  So, it's a little bit of a pain.

Iain> This was copied from cp-demangle.exp.  I believe it is written that
Iain> way so that all demangle tests are ran, rather than stopping at the
Iain> first error?

The C++ one only works proc-by-proc.  If a test fails with a Tcl error
-- which btw isn't the same as just an ordinary failure, those don't
cause particular problems -- then it will run the subsequent procs.
Your test file only has a single proc; and anyway I'm guessing that code
in the C++ test is not useful anyway.  I think dropping it from your
patch is safe.

Tom

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 5/5] Fix for D demangling in GDB
  2014-01-10 14:51     ` Iain Buclaw
@ 2014-01-10 21:43       ` Tom Tromey
  2014-01-11 20:08         ` Iain Buclaw
  0 siblings, 1 reply; 13+ messages in thread
From: Tom Tromey @ 2014-01-10 21:43 UTC (permalink / raw)
  To: Iain Buclaw; +Cc: gdb-patches

>>>>> "Iain" == Iain Buclaw <ibuclaw@gdcproject.org> writes:

Iain> Couldn't find an easy way to move, then fix.  So I did fix, then move.

Thank you.

Iain> +/* Demangle the calling convention from MANGLE and append it to TEMPBUF.
Iain> +   Return the remaining string on success or NULL on failure.  */
Iain> +static const char *
Iain> +parse_call_convention (struct obstack *tempbuf, const char *mangle)

Blank line between comment and start of function; here and elsewhere.

Iain> +}
Iain> +
Iain>  static int
Iain> -extract_type_info (const char *mangled_str, struct obstack *tempbuf)
Iain> +call_convention_p (const char *mangle)
Iain>  {

Needs an intro comment.

Iain> +      if (mangle && call_convention_p (mangle))

We're trying to use the explicit checking form these days, so
"if (mangle != NULL && ..."

I didn't check the rest of the patch for this nit -- could you take a
look?


Other than the nits and the remaining test suite thing, this looks good.
Thanks again for splitting the patch.

Tom

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 5/5] Fix for D demangling in GDB
  2014-01-10 15:05     ` Iain Buclaw
@ 2014-01-10 21:45       ` Tom Tromey
  2014-01-11 20:18         ` Iain Buclaw
  0 siblings, 1 reply; 13+ messages in thread
From: Tom Tromey @ 2014-01-10 21:45 UTC (permalink / raw)
  To: Iain Buclaw; +Cc: gdb-patches

>>>>> "Iain" == Iain Buclaw <ibuclaw@gdcproject.org> writes:

Iain> Second change, move D demangling routines out of d-lang.c into
Iain> d-support.c, which is intended to house other language support
Iain> functions that don't really have a home elsewhere.

Thanks.

Iain>         * d-lang.c (parse_call_convention)
Iain>         (parse_attributes, parse_function_types)
Iain>         (parse_function_args, parse_type, parse_identifier)
Iain>         (call_convention_p, d_parse_symbol): Move functions to ...
Iain>         * d-support.c: ... New file.

This should mention the Makefile.in change, like:

	* Makefile.in (SFILES): Add d-support.c.
        (COMMON_OBS): Add d-support.o.

... and the d-lang.h change, even though it is trivial.

Otherwise the patch is fine.

Tom

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 5/5] Fix for D demangling in GDB
  2014-01-10 21:22     ` Tom Tromey
@ 2014-01-10 23:09       ` Iain Buclaw
  0 siblings, 0 replies; 13+ messages in thread
From: Iain Buclaw @ 2014-01-10 23:09 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On 10 January 2014 21:22, Tom Tromey <tromey@redhat.com> wrote:
>>>>>> "Iain" == Iain Buclaw <ibuclaw@gdcproject.org> writes:
>
> Tom> It's also worth noting that with a bit more work you could push the D
> Tom> demangler into libiberty (see ada_demangle there) and then get
> Tom> demangling from "nm" and the other binutils.
>
> Iain> That sounds like a good plan.  I'll keep a note to get round to do that.
>
> Just FYI - I'm not sure if you know this or not, but libiberty is
> canonically maintained in the GCC tree, so if you do this, it has to be
> submitted there first.  Then it will be merged (either by me, or by
> whoever else seems to be doing (semi-)automated merges) into
> binutils-gdb.git.  So, it's a little bit of a pain.
>

Incidentally, I have a assignment form for GCC that has been sitting
in my inbox waiting to be signed and sent off.  This will probably
give me an excuse to get round to do that.

I've ported this demangler over to libiberty, changing obstack ->
string where appropriate.  Whilst happy at the result, I think I'll
wait until I've finished off demangling D templates. Which is the only
part of demangling where things get a bit hairy (and also why I chose
to move demangling in a separate file).


> Iain> This was copied from cp-demangle.exp.  I believe it is written that
> Iain> way so that all demangle tests are ran, rather than stopping at the
> Iain> first error?
>
> The C++ one only works proc-by-proc.  If a test fails with a Tcl error
> -- which btw isn't the same as just an ordinary failure, those don't
> cause particular problems -- then it will run the subsequent procs.
> Your test file only has a single proc; and anyway I'm guessing that code
> in the C++ test is not useful anyway.  I think dropping it from your
> patch is safe.
>


OK, thanks for the explaination.

Regards
Iain.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 5/5] Fix for D demangling in GDB
  2014-01-10 21:43       ` Tom Tromey
@ 2014-01-11 20:08         ` Iain Buclaw
  0 siblings, 0 replies; 13+ messages in thread
From: Iain Buclaw @ 2014-01-11 20:08 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 775 bytes --]

On 10 January 2014 21:43, Tom Tromey <tromey@redhat.com> wrote:
>
> Other than the nits and the remaining test suite thing, this looks good.
> Thanks again for splitting the patch.
>
> Tom

Updated with nits, testsuite, and explicit null pointer checks.

Changelog entry same as before:

    gdb/

        * d-lang.h (d_parse_symbol): Add declaration.
        * d-lang.c (extract_identifiers)
        (extract_type_info): Remove functions.
        (parse_call_convention, parse_attributes)
        (parse_function_types, parse_function_args)
        (parse_type, parse_identifier, call_convention_p)
        (d_parse_symbol): New functions.
        (d_demangle): Use d_parse_symbol to demangle D symbols.

    gdb/testsuite/

        * gdb.dlang/demangle.exp: New file.

---

[-- Attachment #2: dlang-p5a.patch --]
[-- Type: text/x-diff, Size: 36143 bytes --]

 gdb/ChangeLog                        |  11 +
 gdb/d-lang.c                         | 735 ++++++++++++++++++++++++++++-------
 gdb/d-lang.h                         |   2 +
 gdb/testsuite/ChangeLog              |   4 +
 gdb/testsuite/gdb.dlang/demangle.exp | 206 ++++++++++
 5 files changed, 821 insertions(+), 137 deletions(-)

diff --git a/gdb/d-lang.c b/gdb/d-lang.c
index 71a2524..9c5eead 100644
--- a/gdb/d-lang.c
+++ b/gdb/d-lang.c
@@ -23,11 +23,13 @@
 #include "varobj.h"
 #include "d-lang.h"
 #include "c-lang.h"
-#include <string.h>
 #include "parser-defs.h"
 #include "gdb_obstack.h"
 
-#include <ctype.h>
+#include "safe-ctype.h"
+
+static const char *parse_function_args (struct obstack *, const char *);
+static const char *parse_type (struct obstack *, const char *);
 
 /* The name of the symbol to use to get the name of the main subprogram.  */
 static const char D_MAIN[] = "D main";
@@ -50,175 +52,634 @@ d_main_name (void)
   return NULL;
 }
 
-/* Extract identifiers from MANGLED_STR and append it to TEMPBUF.
-   Return 1 on success or 0 on failure.  */
-static int
-extract_identifiers (const char *mangled_str, struct obstack *tempbuf)
+/* 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)
 {
-  long i = 0;
+  if ((mangle == NULL) || (*mangle == '\0'))
+    return mangle;
 
-  while (isdigit (*mangled_str))
+  switch (*mangle)
     {
-      char *end_ptr;
-
-      i = strtol (mangled_str, &end_ptr, 10);
-      mangled_str = end_ptr;
-      if (i <= 0 || strlen (mangled_str) < i)
-        return 0;
-      obstack_grow (tempbuf, mangled_str, i);
-      mangled_str += i;
-      obstack_grow_str (tempbuf, ".");
+    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;
     }
-  if (*mangled_str == '\0' || i == 0)
-    return 0;
-  obstack_blank (tempbuf, -1);
-  return 1;
+
+  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;
 }
 
-/* Extract and demangle type from MANGLED_STR and append it to TEMPBUF.
-   Return 1 on success or 0 on failure.  */
+/* 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
-extract_type_info (const char *mangled_str, struct obstack *tempbuf)
+call_convention_p (const char *mangle)
 {
-  if (*mangled_str == '\0')
+  size_t i;
+
+  if ((mangle == NULL) || (*mangle == '\0'))
     return 0;
-  switch (*mangled_str++)
+
+  switch (*mangle)
     {
-      case 'A': /* dynamic array */
-      case 'G': /* static array */
-      case 'H': /* associative array */
-	if (!extract_type_info (mangled_str, tempbuf))
-	  return 0;
-	obstack_grow_str (tempbuf, "[]");
-	return 1;
-      case 'P': /* pointer */
-	if (!extract_type_info (mangled_str, tempbuf))
-	  return 0;
-	obstack_grow_str (tempbuf, "*");
-	return 1;
-      case 'R': /* reference */
-	if (!extract_type_info (mangled_str, tempbuf))
-	  return 0;
-	obstack_grow_str (tempbuf, "&");
-	return 1;
-      case 'Z': /* return value */
-	return extract_type_info (mangled_str, tempbuf);
-      case 'J': /* out */
-	obstack_grow_str (tempbuf, "out ");
-	return extract_type_info (mangled_str, tempbuf);
-      case 'K': /* inout */
-	obstack_grow_str (tempbuf, "inout ");
-	return extract_type_info (mangled_str, tempbuf);
-      case 'E': /* enum */
-      case 'T': /* typedef */
-      case 'D': /* delegate */
-      case 'C': /* class */
-      case 'S': /* struct */
-	return extract_identifiers (mangled_str, tempbuf);
-
-      /* basic types: */
-      case 'n': obstack_grow_str (tempbuf, "none"); return 1;
-      case 'v': obstack_grow_str (tempbuf, "void"); return 1;
-      case 'g': obstack_grow_str (tempbuf, "byte"); return 1;
-      case 'h': obstack_grow_str (tempbuf, "ubyte"); return 1;
-      case 's': obstack_grow_str (tempbuf, "short"); return 1;
-      case 't': obstack_grow_str (tempbuf, "ushort"); return 1;
-      case 'i': obstack_grow_str (tempbuf, "int"); return 1;
-      case 'k': obstack_grow_str (tempbuf, "uint"); return 1;
-      case 'l': obstack_grow_str (tempbuf, "long"); return 1;
-      case 'm': obstack_grow_str (tempbuf, "ulong"); return 1;
-      case 'f': obstack_grow_str (tempbuf, "float"); return 1;
-      case 'd': obstack_grow_str (tempbuf, "double"); return 1;
-      case 'e': obstack_grow_str (tempbuf, "real"); return 1;
-
-      /* imaginary and complex: */
-      case 'o': obstack_grow_str (tempbuf, "ifloat"); return 1;
-      case 'p': obstack_grow_str (tempbuf, "idouble"); return 1;
-      case 'j': obstack_grow_str (tempbuf, "ireal"); return 1;
-      case 'q': obstack_grow_str (tempbuf, "cfloat"); return 1;
-      case 'r': obstack_grow_str (tempbuf, "cdouble"); return 1;
-      case 'c': obstack_grow_str (tempbuf, "creal"); return 1;
-
-      /* other types: */
-      case 'b': obstack_grow_str (tempbuf, "bit"); return 1;
-      case 'a': obstack_grow_str (tempbuf, "char"); return 1;
-      case 'u': obstack_grow_str (tempbuf, "wchar"); return 1;
-      case 'w': obstack_grow_str (tempbuf, "dchar"); return 1;
-
-      default:
-	obstack_grow_str (tempbuf, "unknown");
-	return 1;
+    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;
+}
+
 /* Implements the la_demangle language_defn routine for language D.  */
+
 char *
 d_demangle (const char *symbol, int options)
 {
   struct obstack tempbuf;
-  char *out_str;
-  unsigned char is_func = 0;
+  char *result;
 
-  if (symbol == NULL)
+  if ((symbol == NULL) || (*symbol == '\0'))
     return NULL;
   else if (strcmp (symbol, "_Dmain") == 0)
     return xstrdup ("D main");
 
   obstack_init (&tempbuf);
-  
-  if (symbol[0] == '_' && symbol[1] == 'D')
-    {
-      symbol += 2;
-      is_func = 1;
-    }
-  else if (strncmp (symbol, "__Class_", 8) == 0)
-    symbol += 8;
-  else if (strncmp (symbol, "__init_", 7) == 0)
-    symbol += 7;
-  else if (strncmp (symbol, "__vtbl_", 7) == 0)
-    symbol += 7;
-  else if (strncmp (symbol, "__modctor_", 10) == 0)
-    symbol += 10;
-  else if (strncmp (symbol, "__moddtor_", 10) == 0)
-    symbol += 10;
-  else if (strncmp (symbol, "__ModuleInfo_", 13) == 0)
-    symbol += 13;
+
+  if (strncmp (symbol, "_D", 2) == 0)
+    symbol += 2;
   else
     {
       obstack_free (&tempbuf, NULL);
       return NULL;
     }
-  
-  if (!extract_identifiers (symbol, &tempbuf))
+
+  if (d_parse_symbol (&tempbuf, symbol) != NULL)
     {
+      obstack_grow_str0 (&tempbuf, "");
+      result = xstrdup (obstack_finish (&tempbuf));
       obstack_free (&tempbuf, NULL);
-      return NULL;
     }
-
-  obstack_grow_str (&tempbuf, "(");
-  if (is_func == 1 && *symbol == 'F')
+  else
     {
-      symbol++;
-      while (*symbol != '\0' && *symbol != 'Z')
-	{
-	  if (is_func == 1)
-	    is_func++;
-	  else
-	    obstack_grow_str (&tempbuf, ", ");
-	  if (!extract_type_info (symbol, &tempbuf))
-	    {
-	      obstack_free (&tempbuf, NULL);
-	      return NULL;
-	   }
-	}
-     }
-  obstack_grow_str0 (&tempbuf, ")");
-
-  /* Doesn't display the return type, but wouldn't be too hard to do.  */
+      obstack_free (&tempbuf, NULL);
+      return NULL;
+    }
 
-  out_str = xstrdup (obstack_finish (&tempbuf));
-  obstack_free (&tempbuf, NULL);
-  return out_str;
+  return result;
 }
 
 /* Table mapping opcodes into strings for printing operators
diff --git a/gdb/d-lang.h b/gdb/d-lang.h
index 455c808..a1b4456 100644
--- a/gdb/d-lang.h
+++ b/gdb/d-lang.h
@@ -62,6 +62,8 @@ extern char *d_demangle (const char *mangled, int options);
 
 extern const struct builtin_d_type *builtin_d_type (struct gdbarch *);
 
+extern const char *d_parse_symbol (struct obstack *, const char *);
+
 /* Defined in d-valprint.c  */
 
 extern void d_val_print (struct type *type, const gdb_byte *valaddr,
diff --git a/gdb/testsuite/gdb.dlang/demangle.exp b/gdb/testsuite/gdb.dlang/demangle.exp
new file mode 100644
index 0000000..4a2ca35
--- /dev/null
+++ b/gdb/testsuite/gdb.dlang/demangle.exp
@@ -0,0 +1,206 @@
+# Copyright (C) 2014 Free Software Foundation, Inc.
+
+# 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/>.
+
+# Test support for D demangling.  The tests here intentionally do not
+# require a D compiler, it simply calls the internal demangler with
+# synthesized strings and tests the results.
+
+load_lib "d-support.exp"
+
+if { [skip_d_tests] } { continue }
+
+### Utility function for test_demangling and test_demangling_exact.
+proc test_demangling {test result} {
+    gdb_test_exact "maintenance demangle $test" $result $test
+}
+
+proc test_d_demangling {} {
+
+    #  Test D demangler
+    test_demangling "_Dmain" "D main"
+
+    test_demangling "_D8demangle4testFaZv" "demangle.test(char)"
+    test_demangling "_D8demangle4testFbZv" "demangle.test(bool)"
+    test_demangling "_D8demangle4testFcZv" "demangle.test(creal)"
+    test_demangling "_D8demangle4testFdZv" "demangle.test(double)"
+    test_demangling "_D8demangle4testFeZv" "demangle.test(real)"
+    test_demangling "_D8demangle4testFfZv" "demangle.test(float)"
+    test_demangling "_D8demangle4testFgZv" "demangle.test(byte)"
+    test_demangling "_D8demangle4testFhZv" "demangle.test(ubyte)"
+    test_demangling "_D8demangle4testFiZv" "demangle.test(int)"
+    test_demangling "_D8demangle4testFjZv" "demangle.test(ireal)"
+    test_demangling "_D8demangle4testFkZv" "demangle.test(uint)"
+    test_demangling "_D8demangle4testFlZv" "demangle.test(long)"
+    test_demangling "_D8demangle4testFmZv" "demangle.test(ulong)"
+    test_demangling "_D8demangle4testFnZv" "demangle.test(none)"
+    test_demangling "_D8demangle4testFoZv" "demangle.test(ifloat)"
+    test_demangling "_D8demangle4testFpZv" "demangle.test(idouble)"
+    test_demangling "_D8demangle4testFqZv" "demangle.test(cfloat)"
+    test_demangling "_D8demangle4testFrZv" "demangle.test(cdouble)"
+    test_demangling "_D8demangle4testFsZv" "demangle.test(short)"
+    test_demangling "_D8demangle4testFtZv" "demangle.test(ushort)"
+    test_demangling "_D8demangle4testFuZv" "demangle.test(wchar)"
+    test_demangling "_D8demangle4testFvZv" "demangle.test(void)"
+    test_demangling "_D8demangle4testFwZv" "demangle.test(dchar)"
+
+    test_demangling "_D8demangle4testFOaZv" "demangle.test(shared(char))"
+    test_demangling "_D8demangle4testFxaZv" "demangle.test(const(char))"
+    test_demangling "_D8demangle4testFyaZv" "demangle.test(immutable(char))"
+    test_demangling "_D8demangle4testFNgaZv" "demangle.test(inout(char))"
+    test_demangling "_D8demangle4testFOxaZv" "demangle.test(shared(const(char)))"
+    test_demangling "_D8demangle4testFONgaZv" "demangle.test(shared(inout(char)))"
+    test_demangling "_D8demangle4testFAaZv" "demangle.test(char\[\])"
+    test_demangling "_D8demangle4testFAAaZv" "demangle.test(char\[\]\[\])"
+    test_demangling "_D8demangle4testFAAAaZv" "demangle.test(char\[\]\[\]\[\])"
+    test_demangling "_D8demangle4testFG42aZv" "demangle.test(char\[42\])"
+    test_demangling "_D8demangle4testFG42G42aZv" "demangle.test(char\[42\]\[42\])"
+    test_demangling "_D8demangle4testFG42G42G42aZv" "demangle.test(char\[42\]\[42\]\[42\])"
+    test_demangling "_D8demangle4testFG1234567890aZv" "demangle.test(char\[1234567890\])"
+    test_demangling "_D8demangle4testFHaaZv" "demangle.test(char\[char\])"
+    test_demangling "_D8demangle4testFHHaaaZv" "demangle.test(char\[char\[char\]\])"
+    test_demangling "_D8demangle4testFPaZv" "demangle.test(char*)"
+    test_demangling "_D8demangle4testFPPaZv" "demangle.test(char**)"
+    test_demangling "_D8demangle4testFPPPaZv" "demangle.test(char***)"
+
+    test_demangling "_D8demangle4testFNhG8gZv" "demangle.test(__vector(byte\[8\]))"
+    test_demangling "_D8demangle4testFNhG16gZv" "demangle.test(__vector(byte\[16\]))"
+    test_demangling "_D8demangle4testFNhG32gZv" "demangle.test(__vector(byte\[32\]))"
+    test_demangling "_D8demangle4testFNhG4sZv" "demangle.test(__vector(short\[4\]))"
+    test_demangling "_D8demangle4testFNhG8sZv" "demangle.test(__vector(short\[8\]))"
+    test_demangling "_D8demangle4testFNhG16sZv" "demangle.test(__vector(short\[16\]))"
+    test_demangling "_D8demangle4testFNhG2iZv" "demangle.test(__vector(int\[2\]))"
+    test_demangling "_D8demangle4testFNhG4iZv" "demangle.test(__vector(int\[4\]))"
+    test_demangling "_D8demangle4testFNhG8iZv" "demangle.test(__vector(int\[8\]))"
+    test_demangling "_D8demangle4testFNhG1lZv" "demangle.test(__vector(long\[1\]))"
+    test_demangling "_D8demangle4testFNhG2lZv" "demangle.test(__vector(long\[2\]))"
+    test_demangling "_D8demangle4testFNhG4lZv" "demangle.test(__vector(long\[4\]))"
+    test_demangling "_D8demangle4testFNhG2fZv" "demangle.test(__vector(float\[2\]))"
+    test_demangling "_D8demangle4testFNhG4fZv" "demangle.test(__vector(float\[4\]))"
+    test_demangling "_D8demangle4testFNhG8fZv" "demangle.test(__vector(float\[8\]))"
+    test_demangling "_D8demangle4testFNhG1dZv" "demangle.test(__vector(double\[1\]))"
+    test_demangling "_D8demangle4testFNhG2dZv" "demangle.test(__vector(double\[2\]))"
+    test_demangling "_D8demangle4testFNhG4dZv" "demangle.test(__vector(double\[4\]))"
+
+    test_demangling "_D8demangle4testFI5identZv" "demangle.test(ident)"
+    test_demangling "_D8demangle4testFI5ident4testZv" "demangle.test(ident.test)"
+    test_demangling "_D8demangle4testFC5classZv" "demangle.test(class)"
+    test_demangling "_D8demangle4testFC5class4testZv" "demangle.test(class.test)"
+    test_demangling "_D8demangle4testFS6structZv" "demangle.test(struct)"
+    test_demangling "_D8demangle4testFS6struct4testZv" "demangle.test(struct.test)"
+    test_demangling "_D8demangle4testFE4enumZv" "demangle.test(enum)"
+    test_demangling "_D8demangle4testFE4enum4testZv" "demangle.test(enum.test)"
+    test_demangling "_D8demangle4testFT7typedefZv" "demangle.test(typedef)"
+    test_demangling "_D8demangle4testFT7typedef4testZv" "demangle.test(typedef.test)"
+
+    test_demangling "_D8demangle4testFJaZv" "demangle.test(out char)"
+    test_demangling "_D8demangle4testFKaZv" "demangle.test(ref char)"
+    test_demangling "_D8demangle4testFLaZv" "demangle.test(lazy char)"
+    test_demangling "_D8demangle4testFMaZv" "demangle.test(scope char)"
+    test_demangling "_D8demangle4testFaXv" "demangle.test(char...)"
+    test_demangling "_D8demangle4testFaYv" "demangle.test(char, ...)"
+    test_demangling "_D8demangle4testFaaYv" "demangle.test(char, char, ...)"
+    test_demangling "_D8demangle4testFaaZv" "demangle.test(char, char)"
+
+    test_demangling "_D8demangle4testFDFZaZv" "demangle.test(char() delegate)"
+    test_demangling "_D8demangle4testFDUZaZv" "demangle.test(extern(C) char() delegate)"
+    test_demangling "_D8demangle4testFDWZaZv" "demangle.test(extern(Windows) char() delegate)"
+    test_demangling "_D8demangle4testFDVZaZv" "demangle.test(extern(Pascal) char() delegate)"
+    test_demangling "_D8demangle4testFDRZaZv" "demangle.test(extern(C++) char() delegate)"
+
+    test_demangling "_D8demangle4testFFZaZv" "demangle.test(char() function)"
+    test_demangling "_D8demangle4testFUZaZv" "demangle.test(extern(C) char() function)"
+    test_demangling "_D8demangle4testFWZaZv" "demangle.test(extern(Windows) char() function)"
+    test_demangling "_D8demangle4testFVZaZv" "demangle.test(extern(Pascal) char() function)"
+    test_demangling "_D8demangle4testFRZaZv" "demangle.test(extern(C++) char() function)"
+
+    test_demangling "_D8demangle4testFDFNaZaZv" "demangle.test(char() pure delegate)"
+    test_demangling "_D8demangle4testFDFNbZaZv" "demangle.test(char() nothrow delegate)"
+    test_demangling "_D8demangle4testFDFNcZaZv" "demangle.test(char() ref delegate)"
+    test_demangling "_D8demangle4testFDFNdZaZv" "demangle.test(char() @property delegate)"
+    test_demangling "_D8demangle4testFDFNeZaZv" "demangle.test(char() @trusted delegate)"
+    test_demangling "_D8demangle4testFDFNfZaZv" "demangle.test(char() @safe delegate)"
+
+    test_demangling "_D8demangle4testFFNaZaZv" "demangle.test(char() pure function)"
+    test_demangling "_D8demangle4testFFNbZaZv" "demangle.test(char() nothrow function)"
+    test_demangling "_D8demangle4testFFNcZaZv" "demangle.test(char() ref function)"
+    test_demangling "_D8demangle4testFFNdZaZv" "demangle.test(char() @property function)"
+    test_demangling "_D8demangle4testFFNeZaZv" "demangle.test(char() @trusted function)"
+    test_demangling "_D8demangle4testFFNfZaZv" "demangle.test(char() @safe function)"
+    test_demangling "_D8demangle4testFFNaNbZaZv" "demangle.test(char() pure nothrow function)"
+    test_demangling "_D8demangle4testFFNbNaZaZv" "demangle.test(char() nothrow pure function)"
+    test_demangling "_D8demangle4testFFNdNfNaZaZv" "demangle.test(char() @property @safe pure function)"
+
+    test_demangling "_D8demangle4test6__vtblZ" "demangle.test.vtbl$"
+    test_demangling "_D8demangle4test6__initZ" "demangle.test.init$"
+    test_demangling "_D8demangle4test12__ModuleInfoZ" "demangle.test.moduleinfo$"
+    test_demangling "_D8demangle4test7__ClassZ" "demangle.test.classinfo$"
+    test_demangling "_D8demangle4test11__InterfaceZ" "demangle.test.interface$"
+
+    test_demangling "_D8demangle4test6__ctorMFZv" "demangle.test.this()"
+    test_demangling "_D8demangle4test6__dtorMFZv" "demangle.test.~this()"
+    test_demangling "_D8demangle4test6__postblitMFZv" "demangle.test.this(this)"
+
+    test_demangling "_D8demangle4testFHAbaZv" "demangle.test(char\[bool\[\]\])"
+    test_demangling "_D8demangle4testFHG42caZv" "demangle.test(char\[creal\[42\]\])"
+    test_demangling "_D8demangle4testFAiXv" "demangle.test(int\[\]...)"
+    test_demangling "_D8demangle4testFLAiXv" "demangle.test(lazy int\[\]...)"
+    test_demangling "_D8demangle4testFAiYv" "demangle.test(int\[\], ...)"
+    test_demangling "_D8demangle4testFLAiYv" "demangle.test(lazy int\[\], ...)"
+    test_demangling "_D8demangle4testFLilZv" "demangle.test(lazy int, long)"
+    test_demangling "_D8demangle4testFLliZv" "demangle.test(lazy long, int)"
+
+    test_demangling "_D8demangle4testPFLAiYi" "demangle.test"
+    test_demangling "_D1a1bi" "a.b"
+    test_demangling "_D1a1bPFiZi" "a.b"
+    test_demangling "_D4test3fooAa" "test.foo"
+    test_demangling "_D4test2dgDFiYd" "test.dg"
+    test_demangling "_D8demangle8demangleFAaZAa" "demangle.demangle(char\[\])"
+    test_demangling "_D6object6Object8opAssignFC6ObjectZi" "object.Object.opAssign(Object)"
+    test_demangling "_D6object6Object8opEqualsFC6ObjectZi" "object.Object.opEquals(Object)"
+    test_demangling "_D8demangle4testFLC6ObjectLDFLiZiZi" "demangle.test(lazy Object, lazy int(lazy int) delegate)"
+    test_demangling "_D6plugin8generateFiiZAya" "plugin.generate(int, int)"
+    test_demangling "_D8demangle3fnAFZv3fnBMFZv" "demangle.fnA().fnB()"
+    test_demangling "_D8demangle4mainFZv1S3fnCFZv" "demangle.main().S.fnC()"
+    test_demangling "_D8demangle4mainFZv1S3fnDMFZv" "demangle.main().S.fnD()"
+    test_demangling "_D8demangle2fnFNgiZNgi" "demangle.fn(inout(int))"
+    test_demangling "_D8demangle4mainFZv5localMFZi" "demangle.main().local()"
+    test_demangling "_D3std5ascii9uppercaseyAa" "std.ascii.uppercase"
+    test_demangling "_D3std6stream9BOMEndianyG5E3std6system6Endian" "std.stream.BOMEndian"
+    test_demangling "_D3std8internal7uni_tab10unicodeNkoyS3std8internal3uni12CodepointSet" "std.internal.uni_tab.unicodeNko"
+    test_demangling "_D2gc2gc2GC6addrOfMFPvZPv" "gc.gc.GC.addrOf(void*)"
+    test_demangling "_D3std7process10setCLOEXECFibZv" "std.process.setCLOEXEC(int, bool)"
+    test_demangling "_D3std6digest2md3MD53putMFNaNbNeMAxhXv" "std.digest.md.MD5.put(scope const(ubyte)\[\]...)"
+    test_demangling "_D3std6mmfile6MmFile13opIndexAssignMFhmZh" "std.mmfile.MmFile.opIndexAssign(ubyte, ulong)"
+    test_demangling "_D3std7process18escapeShellCommandFxAAaXAya" "std.process.escapeShellCommand(const(char\[\]\[\])...)"
+    test_demangling "_D4core4sync5mutex5Mutex6__ctorMFC6ObjectZC4core4sync5mutex5Mutex" "core.sync.mutex.Mutex.this(Object)"
+    test_demangling "_D6object14TypeInfo_Array8argTypesMFNbNfJC8TypeInfoJC8TypeInfoZi" "object.TypeInfo_Array.argTypes(out TypeInfo, out TypeInfo)"
+    test_demangling "_D2rt6dmain211_d_run_mainUiPPaPUAAaZiZi7tryExecMFMDFZvZv" "rt.dmain2._d_run_main(int, char**, extern(C) int(char\[\]\[\]) function*).tryExec(scope void() delegate)"
+    test_demangling "_D6object9Exception6__ctorMFNaNbNfAyaAyamC6object9ThrowableZC9Exception" "object.Exception.this(immutable(char)\[\], immutable(char)\[\], ulong, object.Throwable)"
+    test_demangling "_D3gcc3deh17parse_lsda_headerFPS3gcc6unwind7generic15_Unwind_ContextPhPS3gcc3deh16lsda_header_infoZPh" "gcc.deh.parse_lsda_header(gcc.unwind.generic._Unwind_Context*, ubyte*, gcc.deh.lsda_header_info*)"
+    test_demangling "_D3std6socket23UnknownAddressReference6__ctorMFPS4core3sys5posix3sys6socket8sockaddrkZC3std6socket23UnknownAddressReference" "std.socket.UnknownAddressReference.this(core.sys.posix.sys.socket.sockaddr*, uint)"
+}
+
+# Start with a fresh gdb.
+
+gdb_exit
+gdb_start
+
+if [set_lang_d] {
+    gdb_test_no_output "set width 0"
+
+    test_d_demangling
+} else {
+    warning "D demangling tests suppressed."
+}

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 5/5] Fix for D demangling in GDB
  2014-01-10 21:45       ` Tom Tromey
@ 2014-01-11 20:18         ` Iain Buclaw
  2014-01-13 20:04           ` Tom Tromey
  0 siblings, 1 reply; 13+ messages in thread
From: Iain Buclaw @ 2014-01-11 20:18 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 912 bytes --]

On 10 January 2014 21:45, Tom Tromey <tromey@redhat.com> wrote:
>
> This should mention the Makefile.in change, like:
>
>         * Makefile.in (SFILES): Add d-support.c.
>         (COMMON_OBS): Add d-support.o.
>
> ... and the d-lang.h change, even though it is trivial.
>
> Otherwise the patch is fine.
>
> Tom

And patch to move functions to d-support.c. Updated from previous
patch changes, and ChangeLog entries added.


2014-01-11  Iain Buclaw  <ibuclaw@gdcproject.org>

        * Makefile.in (SFILES): Add d-support.c.
        (COMMON_OBS): Add d-support.o.
        * d-lang.h (d_parse_symbol): Add comment, now defined in
        d-support.c.
        * d-lang.c (parse_call_convention)
        (parse_attributes, parse_function_types)
        (parse_function_args, parse_type, parse_identifier)
        (call_convention_p, d_parse_symbol): Move functions to ...
        * d-support.c: ... New file.

---

[-- Attachment #2: dlang-p5b.patch --]
[-- Type: text/x-diff, Size: 33495 bytes --]

 gdb/ChangeLog   |  11 +
 gdb/Makefile.in |   3 +-
 gdb/d-lang.c    | 597 -----------------------------------------------------
 gdb/d-lang.h    |   2 +
 gdb/d-support.c | 621 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 636 insertions(+), 598 deletions(-)

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 824b26b..f557bfe 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -727,7 +727,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
 	charset.c cleanups.c cli-out.c coffread.c coff-pe-read.c \
 	complaints.c completer.c continuations.c corefile.c corelow.c \
 	cp-abi.c cp-support.c cp-namespace.c cp-valprint.c \
-	d-lang.c d-valprint.c \
+	d-lang.c d-support.c d-valprint.c \
 	cp-name-parser.y \
 	dbxread.c demangle.c dictionary.c disasm.c doublest.c dummy-frame.c \
 	dwarf2expr.c dwarf2loc.c dwarf2read.c dwarf2-frame.c \
@@ -947,6 +947,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
 	inline-frame.o \
 	gnu-v2-abi.o gnu-v3-abi.o cp-abi.o cp-support.o \
 	cp-namespace.o \
+	d-support.o \
 	reggroups.o regset.o \
 	trad-frame.o \
 	tramp-frame.o \
diff --git a/gdb/d-lang.c b/gdb/d-lang.c
index 9c5eead..b724b96 100644
--- a/gdb/d-lang.c
+++ b/gdb/d-lang.c
@@ -26,11 +26,6 @@
 #include "parser-defs.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 *);
-
 /* The name of the symbol to use to get the name of the main subprogram.  */
 static const char D_MAIN[] = "D main";
 
@@ -52,598 +47,6 @@ d_main_name (void)
   return NULL;
 }
 
-/* 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;
-}
-
 /* Implements the la_demangle language_defn routine for language D.  */
 
 char *
diff --git a/gdb/d-lang.h b/gdb/d-lang.h
index a1b4456..ca9126b 100644
--- a/gdb/d-lang.h
+++ b/gdb/d-lang.h
@@ -62,6 +62,8 @@ extern char *d_demangle (const char *mangled, int options);
 
 extern const struct builtin_d_type *builtin_d_type (struct gdbarch *);
 
+/* Defined in d-support.c  */
+
 extern const char *d_parse_symbol (struct obstack *, const char *);
 
 /* Defined in d-valprint.c  */
diff --git a/gdb/d-support.c b/gdb/d-support.c
new file mode 100644
index 0000000..8123981
--- /dev/null
+++ b/gdb/d-support.c
@@ -0,0 +1,621 @@
+/* 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;
+}
+

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 5/5] Fix for D demangling in GDB
  2014-01-11 20:18         ` Iain Buclaw
@ 2014-01-13 20:04           ` Tom Tromey
  2014-01-18 18:24             ` Iain Buclaw
  0 siblings, 1 reply; 13+ messages in thread
From: Tom Tromey @ 2014-01-13 20:04 UTC (permalink / raw)
  To: Iain Buclaw; +Cc: gdb-patches

>>>>> "Iain" == Iain Buclaw <ibuclaw@gdcproject.org> writes:

Iain> 2014-01-11  Iain Buclaw  <ibuclaw@gdcproject.org>
Iain>         * Makefile.in (SFILES): Add d-support.c.
Iain>         (COMMON_OBS): Add d-support.o.
Iain>         * d-lang.h (d_parse_symbol): Add comment, now defined in
Iain>         d-support.c.
Iain>         * d-lang.c (parse_call_convention)
Iain>         (parse_attributes, parse_function_types)
Iain>         (parse_function_args, parse_type, parse_identifier)
Iain>         (call_convention_p, d_parse_symbol): Move functions to ...
Iain>         * d-support.c: ... New file.

Thanks, this is ok.

Tom

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 5/5] Fix for D demangling in GDB
  2014-01-13 20:04           ` Tom Tromey
@ 2014-01-18 18:24             ` Iain Buclaw
  0 siblings, 0 replies; 13+ messages in thread
From: Iain Buclaw @ 2014-01-18 18:24 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On 13 January 2014 20:04, Tom Tromey <tromey@redhat.com> wrote:
>>>>>> "Iain" == Iain Buclaw <ibuclaw@gdcproject.org> writes:
>
> Iain> 2014-01-11  Iain Buclaw  <ibuclaw@gdcproject.org>
> Iain>         * Makefile.in (SFILES): Add d-support.c.
> Iain>         (COMMON_OBS): Add d-support.o.
> Iain>         * d-lang.h (d_parse_symbol): Add comment, now defined in
> Iain>         d-support.c.
> Iain>         * d-lang.c (parse_call_convention)
> Iain>         (parse_attributes, parse_function_types)
> Iain>         (parse_function_args, parse_type, parse_identifier)
> Iain>         (call_convention_p, d_parse_symbol): Move functions to ...
> Iain>         * d-support.c: ... New file.
>
> Thanks, this is ok.
>
> Tom

Thanks, now all done!

Regards
Iain

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2014-01-18 18:24 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-01-09 13:13 [PATCH 5/5] Fix for D demangling in GDB Iain Buclaw
2014-01-09 21:54 ` Tom Tromey
2014-01-10 13:24   ` Iain Buclaw
2014-01-10 14:51     ` Iain Buclaw
2014-01-10 21:43       ` Tom Tromey
2014-01-11 20:08         ` Iain Buclaw
2014-01-10 15:05     ` Iain Buclaw
2014-01-10 21:45       ` Tom Tromey
2014-01-11 20:18         ` Iain Buclaw
2014-01-13 20:04           ` Tom Tromey
2014-01-18 18:24             ` Iain Buclaw
2014-01-10 21:22     ` Tom Tromey
2014-01-10 23:09       ` Iain Buclaw

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).