RFA: Improvements to libiberty v3 demangler

DJ Delorie dj@redhat.com
Wed Nov 19 03:35:00 GMT 2003


> After getting this far, I've realized that the whole approach used
> by this code is fundamentally broken.

Er, that's why I'm writing a new one.  I've mentioned this before.
Current sources (standalone) attached, although I'm thinking of
scrapping the marker tokens thing.

#include <stdio.h>
#include <string.h>

#include "ansidecl.h"
#include "libiberty.h"

#define DEBUG 0

/* Text marker characters...
   \1 - before prefix
   \2 - after prefix
   \3 - before suffix
   \4 - after suffix
   \5 - allocation marker
   \6 - optional parens here
*/

struct dem_s {
  char *pattern;
  char *getp;
  char is_template;
  char is_ctor_dtor;
  char is_conversion;
  int sub_idx;
  char **subs;
  int tsub_idx;
  char **tsubs;
  int tsub_ofs;
};
typedef struct dem_s *dem_t;

struct dem_state_s {
  int is_template;
  char *getp;
  int sub_idx;
  int tsub_idx;
  int tsub_ofs;
};
typedef struct dem_state_s *dem_state_t;

struct cv_s {
  char r;
  char K;
  char V;
  int n_u;
  char **u;
};
typedef struct cv_s *cv_t;

char *funcstack[1000];
int fsi = 0;

static void
cv_init (cv_t cv, int used)
{
  cv->r = 0;
  cv->K = 0;
  cv->V = 0;
  cv->n_u = used ? 0 : -1;
  cv->u = 0;
}

static void
ds_save (dem_t d, dem_state_t dt)
{
  dt->is_template = d->is_template;
  dt->getp = d->getp;
  dt->sub_idx = d->sub_idx;
  dt->tsub_idx = d->tsub_idx;
  dt->tsub_ofs = d->tsub_ofs;
}

static void
ds_restore (dem_t d, dem_state_t dt)
{
  d->is_template = dt->is_template;
  d->getp = dt->getp;
  d->sub_idx = dt->sub_idx;
  d->tsub_idx = dt->tsub_idx;
  d->tsub_ofs = dt->tsub_ofs;
}

static int
d_peek (dem_t d)
{
  return *(d->getp);
}

static int
d_get (dem_t d)
{
  int rv = *d->getp;
  if (rv)
    d->getp++;
  return rv;
}

static void
d_skip (dem_t d, int n)
{
  while (n--)
    d_get (d);
}

static char *
d_concat VPARAMS ((dem_t d, ...))
{
  int len = 1;
  char *newstr, *s, *tp, last;

  /* First compute the size of the result and get sufficient memory.  */
  VA_OPEN (args, d);
  VA_FIXEDARG (args, dem_t, d);
  while ((s = va_arg (args, char *)) != 0)
      len += strlen (s);
  VA_CLOSE (args);

  tp = newstr = (char *) malloc (len);

  /* Now copy the individual pieces to the result string. */
  VA_OPEN (args, d);
  VA_FIXEDARG (args, dem_t, d);
  last = 0;
  while ((s = va_arg (args, char *)) != 0)
    {
      int l = strlen (s);
      memcpy (tp, s, l);
      tp += l;
      last = tp[-1];
    }
  *tp = 0;
  VA_CLOSE (args);

  return newstr;
}

static int
d_prefix_p (dem_t d, const char *pre)
{
  char *p = d->getp;
  int n=0;
  while (*pre)
    {
      n++;
      if (*pre++ != *p++)
	return 0;
    }
  d->getp += n;
  return 1;
}

#if DEBUG
static void
d_trace_pat (const char *pp, const char *pt)
{
  int cols = 0, n;
  char *gr = pt ? "\033[36m" : "\033[32m";
  fprintf(stderr, gr);
  while (1)
    {
      if (pp == pt)
	{
	  fprintf (stderr, "\033[34m\1%s", gr);
	  cols ++;
	}
      if (*pp == 0)
	break;
      switch (*pp)
	{
	case 1:
	  fprintf (stderr, "\033[32m<%s", gr);
	  break;
	case 2:
	  fprintf (stderr, "\033[32m>%s", gr);
	  break;
	case 3:
	  fprintf (stderr, "\033[31m<%s", gr);
	  break;
	case 4:
	  fprintf (stderr, "\033[31m>%s", gr);
	  break;
	case 5:
	  fprintf (stderr, "\033[36mM%s", gr);
	  break;
	case 6:
	  fprintf (stderr, "\033[31m()%s", gr);
	  cols++;
	  break;
	case '\n':
	  fprintf (stderr, "\033[31mn%s", gr);
	  break;
	case ' ':
	  fprintf (stderr, "\033[34m\267%s", gr);
	  break;
	default:
	  fputc (*pp, stderr);
	}
      pp++;
      cols++;
    }
  fprintf(stderr, "\033[0m");
#if 0
  while (cols < 20)
    {
      fputc(' ', stderr);
      cols++;
    }
  n = 0;
  for (cols=fsi-1, n=0; cols>=0 && n<3; cols--, n++)
    fprintf(stderr, " %-25s", funcstack[cols]);
  if (cols)
    fprintf(stderr, " ...");
#endif
  fputc ('\n', stderr);
}

static void
d_trace_state (dem_t d)
{
  fprintf (stderr, "[S%d T%d O%d] ",
	   d->sub_idx, d->tsub_idx, d->tsub_ofs);
}
#endif

static struct dem_state_s
d_trace_enter (char *f, int l, dem_t d)
{
  struct dem_state_s ds;
#if DEBUG
  fprintf(stderr, "%5d: %-25s called   ", l, f);
  d_trace_state (d);
  d_trace_pat (d->pattern, d->getp);
#endif
  ds_save (d, &ds);
#if DEBUG
  funcstack[fsi++] = f;
#endif
  return ds;
}

static char *
d_trace_return (char *f, int l, dem_t d, char *rv)
{
  fsi--;
#if DEBUG
  fprintf(stderr, "%5d: %-25s returns  ", l, f);
  d_trace_state (d);
  if (rv)
    d_trace_pat (rv, 0);
  else
    fprintf (stderr, "\033[33m(null)\033[0m\n");
#endif
  return rv;
}

static char *
d_trace_no (char *f, int l, dem_t d, dem_state_t bt)
{
  fsi--;
  ds_restore (d, bt);
#if DEBUG
  fprintf(stderr, "%5d: %-25s no-match ", l, f);
  d_trace_state (d);
  d_trace_pat (d->pattern, d->getp);
#endif
  return 0;
}

static struct dem_state_s
d_trace_push (dem_t d)
{
  struct dem_state_s ds;
  ds_save (d, &ds);
  return ds;
}

static void
d_trace_string (char *f, int l, dem_t d, char *s)
{
#if DEBUG
  fprintf(stderr, "%5d: %-25s trace    ", l, f);
  d_trace_state (d);
  d_trace_pat (s, 0);
#endif
}

#define ENTER() struct dem_state_s __bt = d_trace_enter (__FUNCTION__, __LINE__, d)
#define TRACE(s) d_trace_string (__FUNCTION__, __LINE__, d, (s));
#define RETURN(r) return d_trace_return (__FUNCTION__, __LINE__, d, (r))
#define RETURN_IF(r) { char *rv = (r); if (rv) return d_trace_return (__FUNCTION__, __LINE__, d, rv); }
#define UNWIND() return d_trace_no (__FUNCTION__, __LINE__, d, &__bt)
#define UNWIND_UNLESS(rv) do { if ((rv) == 0) UNWIND(); } while (0)
#define PUSH() { struct dem_state_s __bt = d_trace_push(d);
#define POP() ds_restore(d, &__bt); } 

static char *
d_insert (dem_t d, char *str, char *snippet, int marker)
{
  char *p, *rv;
  int slen, snlen, plen;
  int eat = 0;
  ENTER();

#if DEBUG
  fprintf (stderr, "d_insert(%s,%s,%d)\n", str, snippet, marker);
#endif

  if (marker != 6)
    if ((p = strchr (str, 6)) != 0)
      str = d_insert (d, str, " (\1\2\3\4) ", 6);

  p = strchr (str, marker ? marker : 2);

  switch (marker)
    {
    case 0:
      if (p)
	p++;
      else
	RETURN (d_concat (d, str, " ", snippet, 0));
      break;
    case 1:
      if (p)
	p++;
      else
	RETURN (d_concat (d, snippet, str, 0));
      break;
    case 2:
      if (!p)
	RETURN (d_concat (d, snippet, str, 0));
      break;
    case 3:
      if (p)
	p++;
      else
	RETURN (d_concat (d, str, snippet, 0));
      break;
    case 4:
      if (!p)
	RETURN (d_concat (d, str, snippet, 0));
      break;
    case 6:
      eat = 1;
      break;
    }
  slen = strlen (str);
  snlen = strlen (snippet);
  plen = p - str;

  rv = (char *) malloc (slen + snlen + 1);

  memcpy (rv, str, plen);
  memcpy (rv+plen, snippet, snlen);
  memcpy (rv+plen+snlen, p+eat, slen-plen+1-eat);
  RETURN (rv);
}

static char *
d_strip (char *str)
{
  char *rv = str;
  char *p;
  while (*str)
    {
      if ((unsigned int)(*str) > 9)
	str++;
      else
	break;
    }
  p = str;
  while (*str)
    {
      if ((unsigned int)(*str) == 9)
	{
	  
	}
      else if ((unsigned int)(*str) > 9)
	*p++ = *str;
      str++;
    }
  if (p != str)
    *p = 0;
  return rv;
}

static char *
sub_save (dem_t d, char *s, int is_t)
{
  static char *last = 0;
  char ***a;
  int *an, i, ofs=0;
  if (!s)
    return 0;
#if DEBUG
  fprintf(stderr, "sub_save(%d, %s)\n", is_t, s);
#endif
  if (is_t)
    {
      a = &(d->tsubs);
      an = &(d->tsub_idx);
      ofs = d->tsub_ofs;
    }
  else
    {
      a = &(d->subs);
      an = &(d->sub_idx);
    }
#if DEBUG
  fprintf (stderr, "%cSUB[%d] = %s\n", is_t?'T':'S', (*an)-ofs, s);
#endif

  if ((*an) && last == s)
    return (*a)[(*an)-1];

  *a = (char **) realloc (*a, ((*an)+1) * sizeof (char *));
  (*a)[*an] = strdup (s);
  (*an)++;
  return s;
}

static char *
show_cv (dem_t d, cv_t cv, int reverse)
{
  int i, j;
  char *rv = "";
  ENTER();
  for (i=0; i<cv->n_u-1; i++)
    for (j=i+1; j<cv->n_u; j++)
      if (strcmp (cv->u[i], cv->u[j]) > 0)
	{
	  char *t = cv->u[i];
	  cv->u[i] = cv->u[j];
	  cv->u[j] = t;
	}
  if (reverse)
    {
      if (cv->K)
	rv = d_concat (d, rv, " const", 0);
      if (cv->V)
	rv = d_concat (d, rv, " volatile", 0);
      if (cv->r)
	rv = d_concat (d, rv, " restrict", 0);
      for (i=0; i<cv->n_u; i++)
	rv = d_concat (d, rv, " ", cv->u[i], 0);
      RETURN (rv);
    }
  if (cv->r)
    rv = d_concat (d, rv, " restrict", 0);
  if (cv->V)
    rv = d_concat (d, rv, " volatile", 0);
  if (cv->K)
    rv = d_concat (d, rv, " const", 0);
  for (i=cv->n_u-1; i>=0; i--)
    rv = d_concat (d, rv, " ", cv->u[i], 0);
  RETURN (rv);
}

static char *
freedup (char *ptr, char *newstring)
{
  if (ptr == newstring)
    return newstring;
  if (ptr)
    free (ptr);
  if (newstring)
    newstring = strdup (newstring);
  return newstring;
}

/*----------------------------------------------------------------------*/

static char *d_encoding PARAMS((dem_t d));
static char *d_name PARAMS((dem_t d, cv_t cvr));
static char *d_unscoped_name PARAMS((dem_t d));
static char *d_unscoped_template_name PARAMS((dem_t d));
static char *d_nested_name PARAMS((dem_t d, cv_t cvr));
static char *d_prefix PARAMS((dem_t d));
static char *d_template_prefix PARAMS((dem_t d));
static char *d_unqualified_name PARAMS((dem_t d, char *prev));
static char *d_source_name PARAMS((dem_t d));
static char *d_number PARAMS((dem_t d));
static char *d_operator_name PARAMS((dem_t d, int *n_args));
static char *d_special_name PARAMS((dem_t d));
static char *d_call_offset PARAMS((dem_t d));
static char *d_nv_offset PARAMS((dem_t d));
static char *d_r_offset PARAMS((dem_t d));
static char *d_ctor_dtor_name PARAMS((dem_t d, char *prev));
static char *d_type PARAMS((dem_t d, cv_t cv));
static char *d_cv_qualifiers PARAMS((dem_t d, cv_t cvr));
static char *d_builtin_type PARAMS((dem_t d));
static char *d_function_type PARAMS((dem_t d, char **rtp));
static char *d_bare_function_type PARAMS((dem_t d, char **rtp, int full));
static char *d_class_enum_type PARAMS((dem_t d));
static char *d_array_type PARAMS((dem_t d, int full));
static char *d_pointer_to_member_type PARAMS((dem_t d));
static char *d_template_param PARAMS((dem_t d));
static char *d_template_template_param PARAMS((dem_t d));
static char *d_template_args PARAMS((dem_t d));
static char *d_template_arg PARAMS((dem_t d));
static char *d_expression PARAMS((dem_t d));
static char *d_expr_primary PARAMS((dem_t d));
static char *d_local_name PARAMS((dem_t d));
static char *d_discriminator PARAMS((dem_t d));
static char *d_substitution PARAMS((dem_t d));

/*----------------------------------------------------------------------*/

static char *
d_encoding (dem_t d)
{
  char *name, *type;
  struct cv_s cv;

  ENTER ();
  RETURN_IF (d_special_name (d));

  cv_init (&cv, 1);

  name = d_name (d, &cv);
  if (d_peek (d) && d_peek (d) != 'E')
    {
      char *rt = 0;
      int need_rv = d->is_template && !(d->is_ctor_dtor || d->is_conversion);
      type = d_bare_function_type (d, need_rv ? &rt : 0, 0);
      if (type)
	{
	  RETURN (d_concat (d, rt?rt:"", rt?" ":"", name, "(", type, ")", show_cv(d, &cv, 0), 0));
	}
      UNWIND();
    }
  RETURN (name);
}

static char *
d_name_2 (dem_t d)
{
  char *tn, *tp;
  ENTER();
  tn = d_unscoped_template_name (d);
  if (!tn)
    UNWIND();
  tp = d_template_args (d);
  if (!tp)
    UNWIND();
  RETURN (d_concat (d, tn, tp, 0));
}

static char *
d_name (dem_t d, cv_t cv)
{
  char *tn;
  char *tp;
  ENTER();

  RETURN_IF (d_nested_name (d, cv));
  PUSH();
  if ((tn = d_unscoped_template_name (d)) != 0)
    if ((tp = d_template_args (d)) != 0)
      RETURN (d_concat (d, tn, tp, 0));
  POP();

  RETURN_IF (d_unscoped_name (d));
  RETURN_IF (d_local_name (d));

  UNWIND();
}

static char *
d_unscoped_name (dem_t d)
{
  ENTER();
  if (d_prefix_p (d, "St"))
    {
      char *un = d_unqualified_name (d, "");
      if (un)
	RETURN (d_concat (d, "std::", un, 0));
      UNWIND();
    }
  RETURN (d_unqualified_name (d, ""));
}

static char *
d_unscoped_template_name (dem_t d)
{
  ENTER();
  RETURN_IF (d_substitution (d));
  RETURN (sub_save (d, d_unscoped_name (d), 0));
}

static char *
d_nested_name (dem_t d, cv_t cv)
{
  char *prefix, *name, *args;
  ENTER ();
  if (d_get (d) != 'N')
    UNWIND();

  d_cv_qualifiers (d, cv);

  prefix = d_prefix (d);
  if (!prefix)
    UNWIND();

  if (d_get (d) != 'E')
    UNWIND();

  RETURN (prefix);
}

static char *
d_prefix_1 (dem_t d, char *prev)
{
  ENTER();
  RETURN_IF (d_substitution (d));
  RETURN_IF (d_template_param (d));
  RETURN_IF (d_unqualified_name (d, prev));
  UNWIND();
}
static char *
d_prefix (dem_t d)
{
  char *rv = 0, *p, *prev=0;
  int save_subs = 0;
  ENTER();

  while (1)
    {
      if (d_peek (d) == 'E')
	break;
      if (rv)
	TRACE(rv);
      d->is_template = 0;
      d->is_ctor_dtor = 0;
      d->is_conversion = 0;
      if (save_subs)
	sub_save (d, rv, 0);
      save_subs = 1;
      if (rv == 0 && d_peek (d) == 'S')
	save_subs = 0;
      p = d_prefix_1 (d, prev);
      if (!p)
	break;
      prev = freedup (prev, p);
      if (rv)
	rv = d_concat (d, rv, "::", p, 0);
      else
	rv = p;
      if (d_peek (d) == 'I')
	{
	  char *ta;
	  if (save_subs)
	    sub_save (d, rv, 0);
	  ta = d_template_args (d);
	  if (!ta)
	    {
	      free (prev);
	      UNWIND(); /* An "I" that isn't a t_a is bad.  */
	    }
	  rv = d_concat (d, rv, ta, 0);
	  save_subs = 1;
	}
    }
  if (prev)
    free (prev);

  if (rv)
    RETURN (rv);

  UNWIND();
}

static char *
d_unqualified_name (dem_t d, char *prev)
{
  char *cd, *on;
  ENTER();
  RETURN_IF (d_operator_name (d, 0));
  if (prev)
    if ((cd = d_ctor_dtor_name (d, prev)) != 0)
      {
	d->is_ctor_dtor = 1;
	RETURN (cd);
      }
  RETURN_IF (d_source_name (d));
  UNWIND();
}

static char *
d_source_name (dem_t d)
{
  int len = 0, p, sign=1;
  char *rv, *cp;
  ENTER();


  if (d_peek (d) == '_')
    {
      if (strncmp (d->getp, "_GLOBAL__I__Z", 13) == 0
	  || strncmp (d->getp, "_GLOBAL__D__Z", 13) == 0)
	{
	  char *pre = d->getp[9] == 'I' ? "con" : "de";
	  char *rv;
	  d_skip (d, 11);
	  rv = d_concat (d, "global ", pre, "structors keyed to ",
			 d->getp, 0);
	  while (d_get (d));
	  RETURN (rv);
	}
    }

  cp = d_number (d);
  if (!cp)
    UNWIND ();
  len = atoi (cp);
  free (cp);

  cp = rv = (char *) malloc (len+1);
  while (len--)
    *cp++ = d_get (d);
  *cp = 0;

  if (strncmp (rv, "_GLOBAL__N", 10) == 0)
    RETURN("(anonymous namespace)");

  RETURN(rv);
}

static char *
d_number (dem_t d)
{
  int len = 0, p, sign=1;
  char *endp = d->getp, *rv;
  ENTER();

  if (*endp == 'n')
    endp++;
  while (isdigit (*endp))
    endp++;
  if (endp == d->getp)
    UNWIND ();
  rv = (char *)malloc (endp - d->getp + 1);
  memcpy (rv, d->getp, endp - d->getp);
  rv[endp - d->getp] = 0;
  if (rv[0] == 'n')
    rv[0] = '-';
  d->getp = endp;

  RETURN(rv);
}

static const struct {
  char *prefix;
  char *text;
  int n_args;
}
operators[] = 
  {
    { "aN", "&="       , 2 },
    { "aS", "="        , 2 },
    { "aa", "&&"       , 2 },
    { "ad", "&"        , 1 },
    { "an", "&"        , 2 },
    { "cl", "()"       , 0 },
    { "cm", ","        , 2 },
    { "co", "~"        , 1 },
    { "dV", "/="       , 2 },
    { "da", " delete[]", 1 },
    { "de", "*"        , 1 },
    { "dl", " delete"  , 1 },
    { "dv", "/"        , 2 },
    { "eO", "^="       , 2 },
    { "eo", "^"        , 2 },
    { "eq", "=="       , 2 },
    { "ge", ">="       , 2 },
    { "gt", ">"        , 2 },
    { "ix", "[]"       , 1 },
    { "lS", "<<="      , 2 },
    { "le", "<="       , 2 },
    { "ls", "<<"       , 2 },
    { "lt", "<"        , 2 },
    { "mI", "-="       , 2 },
    { "mL", "*="       , 2 },
    { "mi", "-"        , 2 },
    { "ml", "*"        , 2 },
    { "mm", "--"       , 1 },
    { "na", " new[]"   , 1 },
    { "ne", "!="       , 2 },
    { "ng", "-"        , 1 },
    { "nt", "!"        , 1 },
    { "nw", " new"     , 1 },
    { "oR", "|="       , 2 },
    { "oo", "||"       , 2 },
    { "or", "|"        , 2 },
    { "pL", "+="       , 2 },
    { "pl", "+"        , 2 },
    { "pm", "->*"      , 2 },
    { "pp", "++"       , 1 },
    { "ps", "+"        , 1 },
    { "pt", "->"       , 2 },
    { "qu", "?"        , 2 },
    { "rM", "%="       , 2 },
    { "rS", ">>="      , 2 },
    { "rm", "%"        , 2 },
    { "rs", ">>"       , 2 },
    { "sz", "sizeof"  , 1 },
    { 0, 0, 0 }
  };

static char *
d_operator_name (dem_t d, int *n_args)
{
  char *rv = 0;
  int i;
  ENTER ();
  for (i=0; operators[i].prefix; i++)
    if (d_prefix_p (d, operators[i].prefix))
      {
	if (n_args)
	  {
	    *n_args = operators[i].n_args;
	    RETURN (operators[i].text);
	  }
	else
	  RETURN (d_concat (d, "operator", operators[i].text, 0));
      }
  if (d_prefix_p (d, "cv"))
    {
      char *t;

      if (d_prefix_p (d, "T_"))
	{
	  char *save_getp = d->getp;
	  d->getp += 1;
	  t = d_type (d, 0);
	  d->getp = save_getp;
	}
      else
	t = d_type (d, 0);
      if (!t)
	UNWIND();
      d->is_conversion = 1;
      if (n_args)
	{
	  *n_args = 1;
	  RETURN (t);
	}
      else
	  RETURN (d_concat (d, "operator ", t, 0));
    }
  if (d_peek (d) == 'v')
    {
      int na;
      d_get (d);
      na = d_get (d) - '0';
      RETURN (d_source_name (d));
    }
  UNWIND();
}

static char *
d_special_name (dem_t d)
{
  struct cv_s cv;
  ENTER();
  cv_init (&cv, 0);
  if (d_prefix_p (d, "TV"))
    RETURN (d_concat (d, "vtable for ", d_type (d, &cv), 0));
  if (d_prefix_p (d, "TT"))
    RETURN (d_concat (d, "VTT for ", d_type (d, &cv), 0));
  if (d_prefix_p (d, "TI"))
    RETURN (d_concat (d, "typeinfo for ", d_type (d, &cv), 0));
  if (d_prefix_p (d, "TS"))
    RETURN (d_concat (d, "typeinfo name for ", d_type (d, &cv), 0));
  if (d_prefix_p (d, "GV"))
    RETURN (d_concat (d, "guard variable for ", d_name (d, &cv), 0));
  if (d_peek (d) == 'T')
    {
      char *co1, *co2, *be;
      PUSH();
      d_get (d);
      if ((co1 = d_call_offset (d)) != 0)
	if ((be = d_encoding (d)) != 0)
	  RETURN (d_concat (d, co1, be, 0));
      POP();
      PUSH();
      d_get (d);
      if (d_get (d) == 'c')
	if ((co1 = d_call_offset (d)) != 0)
	  if ((co2 = d_call_offset (d)) != 0)
	    if ((be = d_encoding (d)) != 0)
	      RETURN (d_concat (d, "covariant return thunk to ", be, 0));
      POP();
    }
  UNWIND();
}

static char *
d_call_offset (dem_t d)
{
  char *o1, *o2;
  ENTER();
  switch (d_get (d))
    {
    case 'v':
      UNWIND_UNLESS (o1 = d_number (d));
      if (d_get (d) != '_')
	UNWIND();
      UNWIND_UNLESS (o2 = d_number (d));
      if (d_get (d) != '_')
	UNWIND();
      RETURN ("virtual thunk to ");

    case 'h':
      UNWIND_UNLESS (o2 = d_number (d));
      if (d_get (d) != '_')
	UNWIND();
      RETURN ("non-virtual thunk to ");
    }
  UNWIND();
}

static char *
d_ctor_dtor_name (dem_t d, char *prev)
{
  ENTER();
  if (d_prefix_p (d, "C1")) RETURN (prev);
  if (d_prefix_p (d, "C2")) RETURN (prev);
  if (d_prefix_p (d, "C3")) RETURN (prev);
  if (d_prefix_p (d, "D0")) RETURN (d_concat (d, "~", prev, 0));
  if (d_prefix_p (d, "D1")) RETURN (d_concat (d, "~", prev, 0));
  if (d_prefix_p (d, "D2")) RETURN (d_concat (d, "~", prev, 0));
  RETURN (0);
}

static char *
d_type_1 (dem_t d, cv_t cv)
{
  char *rt, *ttp, *ta;
  /* These are the substitutable ones.  */
  ENTER();
  RETURN_IF (d_function_type (d, &rt));
  RETURN_IF (d_array_type (d, 0));
  RETURN_IF (d_pointer_to_member_type (d));

  PUSH();
  if ((ttp = d_template_template_param (d)) != 0)
    if ((ta = d_template_args (d)) != 0)
      RETURN (d_concat (d, ttp, ta, 0));
  POP();

  RETURN_IF (d_template_param (d));
  RETURN_IF (d_class_enum_type (d));

  UNWIND();
}

static char *
d_type_0 (dem_t d, cv_t cv)
{
  char *rv;
  struct cv_s cv_tmp;
  char *t;
  ENTER();

  if (cv == 0)
    {
      cv = &cv_tmp;
      cv_init (cv, 0);
    }

  RETURN_IF (d_builtin_type (d));
  RETURN_IF (sub_save (d, d_type_1 (d, cv), 0));
  RETURN_IF (d_substitution (d));
  switch (d_peek (d))
    {
    case 'r':
    case 'V':
    case 'K':
      {
        struct cv_s cv;
	cv_init (&cv, 1);
	UNWIND_UNLESS (d_cv_qualifiers (d, &cv));
	UNWIND_UNLESS (t = d_type (d, &cv));
	if (t)
	  {
	    RETURN (sub_save (d, d_insert (d, t, show_cv (d, &cv, 0), 4), 0));
	  }
	break;
      }
    case 'U':
      {
	char *sn;
	d_get (d);
	UNWIND_UNLESS (sn = d_source_name (d));
	UNWIND_UNLESS (t = d_type (d, cv));
	if (t)
	  {
	    RETURN (sub_save (d, d_insert (d, t, d_concat (d, " ", sn, 0), 4), 0));
	  }
	break;
      }
    case 'P':
      {
	d_get (d);
	UNWIND_UNLESS (t = d_type (d, cv));
	if (t)
	  RETURN (sub_save (d, d_insert (d, t, "*", 4), 0));
	break;
      }
    case 'R':
      {
	d_get (d);
	UNWIND_UNLESS (t = d_type (d, 0));
	if (t)
	  RETURN (sub_save (d, d_insert (d, t, "&", 4), 0));
	break;
      }
    case 'C':
      {
	d_get (d);
	UNWIND_UNLESS (t = d_type (d, 0));
	if (t)
	  RETURN (sub_save (d, d_insert (d, t, "complex ", 2), 0));
	break;
      }
    case 'G':
      {
	d_get (d);
	UNWIND_UNLESS (t = d_type (d, 0));
	if (t)
	  RETURN (sub_save (d, d_insert (d, t, "imaginary ", 2), 0));
	break;
      }
    }
  UNWIND();
}

static char *
d_type (dem_t d, cv_t cv)
{
  int save_idx = d->tsub_idx;
  int save_ofs = d->tsub_ofs;
  char *rv = d_type_0 (d, cv);
  d->tsub_idx = save_idx;
  d->tsub_ofs = save_ofs;
  return rv;
}

static char *
d_cv_qualifiers (dem_t d, cv_t cv)
{
  char *sn, *t;
  ENTER();
  while (1)
    switch (d_peek (d))
      {
      case 'r':
	d_get (d);
	cv->r = 1;
	break;
      case 'K':
	d_get (d);
	cv->K = 1;
	break;
      case 'V':
	d_get (d);
	cv->V = 1;
	break;
#if 0
      case 'U':
	PUSH();
	d_get (d);
	if ((sn = d_source_name (d)) != 0)
	  {
	    if (cv->n_u >= 0)
	      {
		cv->n_u ++;
		cv->u = (char **) realloc (cv->u, cv->n_u * sizeof (char *));
		cv->u[cv->n_u-1] = strdup (sn);
	      }
	    break;
	  }
	POP();
#endif
      default:
	RETURN ("");
      }
}

static char *
d_builtin_type (dem_t d)
{
  char *rv = 0;
  ENTER ();

  switch (d_peek (d))
    {
    case 'v': rv = "void"; break;
    case 'w': rv = "wchar_t"; break;
    case 'b': rv = "bool"; break;
    case 'c': rv = "char"; break;
    case 'a': rv = "signed char"; break;
    case 'h': rv = "unsigned char"; break;
    case 's': rv = "short"; break;
    case 't': rv = "unsigned short"; break;
    case 'i': rv = "int"; break;
    case 'j': rv = "unsigned int"; break;
    case 'l': rv = "long"; break;
    case 'm': rv = "unsigned long"; break;
    case 'x': rv = "long long"; break;
    case 'y': rv = "unsigned long long"; break;
    case 'n': rv = "__int128"; break;
    case 'o': rv = "unsigned __int128"; break;
    case 'f': rv = "float"; break;
    case 'd': rv = "double"; break;
    case 'e': rv = "long double"; break;
    case 'g': rv = "__float128"; break;
    case 'z': rv = "..."; break;
    case 'u':
      d_get(d);
      RETURN (d_source_name (d));
      break;
    }

  if (rv)
    {
      d_get (d);
      RETURN (rv);
    }
  UNWIND();
}

static char *
d_function_type (dem_t d, char **rtp)
{
  char *rv;
  ENTER();
  if (d_get (d) != 'F')
    UNWIND();
  if (d_peek(d) == 'Y')
    d_get(d);
  rv = d_bare_function_type (d, rtp, 1);
  if (d_get (d) != 'E')
    UNWIND();
  RETURN (rv);
}

static char *
d_bare_function_type (dem_t d, char **rtp, int full)
{
  char *rv = "unset";
  int argc;

  ENTER();

  argc = rtp ? -1 : 0;
  if (rtp)
    *rtp = 0;

  while (d_peek (d) && d_peek (d) != 'E') {
    char *t;

    if (d_peek (d) == 'v' && argc >= 0)
      {
	d_get (d);
	t = "";
      }
    else
      UNWIND_UNLESS (t = d_type (d, 0));
    switch (argc++)
    {
    case -1:
      *rtp = t;
      break;
    case 0:
      rv = t;
      break;
    default:
      rv = d_concat (d, rv, ", ", t, 0);
      break;
    }
  }
  d_strip (rv);
  if (full && rtp && *rtp && strchr (*rtp, 4))
    {
      RETURN (d_insert (d, *rtp, d_concat (d, "(\1\2\3\4)(", rv, ")", 0), 4));
      *rtp = d_insert (d, *rtp, d_concat (d, " ()(", rv, ")", 0), 4);
      d_strip (*rtp);
      RETURN (*rtp);
    }
  else if (full && *rtp)
    RETURN (d_concat (d, *rtp?*rtp:"", " (\1\2\3\4)(", rv, ")", 0) );
  else
    RETURN (rv);
}

static char *
d_class_enum_type (dem_t d)
{
  struct cv_s cv;
  ENTER();
  cv_init (&cv, 0);
  RETURN_IF (d_name (d, &cv));
  UNWIND();
}

static char *
d_array_type (dem_t d, int full)
{
  char *dim, *t, *rv = "";
  ENTER();
  if (d_peek (d) != 'A')
    UNWIND();
  while (d_peek (d) == 'A')
    {
      d_get (d);
      if ((dim = d_number (d)) == 0)
	dim = d_expression (d);
      if (d_get (d) != '_')
	UNWIND ();
      rv = d_concat (d, rv, "[", dim?dim:"", "]", 0);
    }
  UNWIND_UNLESS (t = d_type (d, 0));
  RETURN (d_concat (d, t, "\6", rv, 0));
}

static char *
d_pointer_to_member_type (dem_t d)
{
  char *ct, *mt, *rv, c;
  struct cv_s cv1, cv2;
  ENTER();

  cv_init (&cv1, 1);
  cv_init (&cv2, 1);

  UNWIND_UNLESS (d_cv_qualifiers (d, &cv1));

  if (d_get (d) != 'M')
    UNWIND();

  UNWIND_UNLESS (ct = d_type (d, 0));
  c = d_peek (d);
  if (c != 'F' && c != 'K' && c != 'V' && c != 'r')
    ct = d_concat (d, " ", ct, 0);
  UNWIND_UNLESS (d_cv_qualifiers (d, &cv2));
  UNWIND_UNLESS (mt = d_type (d, 0));

  TRACE (mt);

  mt = d_insert (d, mt, d_concat (d, ct, "::*", 0), 4);
  mt = d_insert (d, mt, show_cv (d, &cv1, 0), 4);
  mt = d_concat (d, mt, show_cv (d, &cv2, 0), 0);
  RETURN (mt);
}

static int
snarf_seq_id (dem_t d)
{
  int rv = 0;
  char *rp = d->getp;

  if (*rp == '_')
    {
      d_get (d);
      return 0;
    }
  while (1)
    {
      int q = 0;
      switch (*rp++)
	{
	  /* Done this way for non-ascii host character sets.  */
	case '0': q = 0; break;
	case '1': q = 1; break;
	case '2': q = 2; break;
	case '3': q = 3; break;
	case '4': q = 4; break;
	case '5': q = 5; break;
	case '6': q = 6; break;
	case '7': q = 7; break;
	case '8': q = 8; break;
	case '9': q = 9; break;
	case 'A': q = 10; break;
	case 'B': q = 11; break;
	case 'C': q = 12; break;
	case 'D': q = 13; break;
	case 'E': q = 14; break;
	case 'F': q = 15; break;
	case 'G': q = 16; break;
	case 'H': q = 17; break;
	case 'I': q = 18; break;
	case 'J': q = 19; break;
	case 'K': q = 20; break;
	case 'L': q = 21; break;
	case 'M': q = 22; break;
	case 'N': q = 23; break;
	case 'O': q = 24; break;
	case 'P': q = 25; break;
	case 'Q': q = 26; break;
	case 'R': q = 27; break;
	case 'S': q = 28; break;
	case 'T': q = 29; break;
	case 'U': q = 30; break;
	case 'V': q = 31; break;
	case 'W': q = 32; break;
	case 'X': q = 33; break;
	case 'Y': q = 34; break;
	case 'Z': q = 35; break;
	case '_':
	  d->getp = rp;
	  return rv + 1;
	default:
	  return -1;
	}
      rv = rv * 36 + q;
    }
}

static char *
d_substitution (dem_t d)
{
  ENTER();
  switch (d_get (d))
    {
    case 'S':
      {
	int seq = snarf_seq_id (d);
	if (seq != -1)
	  {
	    if (seq < d->sub_idx)
	      RETURN (d->subs[seq]);
	    RETURN ("nosub");
	  }
	switch (d_get (d))
	  {
	  case 'a': RETURN ("std::allocator");
	  case 'b': RETURN ("std::basic_string");
	  case 'd': RETURN ("std::iostream");
	  case 'i': RETURN ("std::istream");
	  case 'o': RETURN ("std::ostream");
	  case 's': RETURN ("std::string");
	  case 't':
	    {
	      char *un = d_unqualified_name (d, 0);
	      if (un)
		RETURN (sub_save (d, d_concat (d, "std::", un, 0), 0));
	      RETURN ("std");
	    }
	  }
      }
      break;
    }
  UNWIND();
}

static char *
d_template_param (dem_t d)
{
  int seq;
  ENTER();
  if (d_get (d) != 'T')
    UNWIND();
  seq = snarf_seq_id (d);
  if (seq == -1)
    UNWIND();

  RETURN (d->tsubs[seq+d->tsub_ofs]);
}

static char *
d_template_template_param (dem_t d)
{
  ENTER();
  RETURN (sub_save (d, d_template_param (d), 0));
}

static char *
d_template_args (dem_t d)
{
  char *rv = 0;
  int idx_save = d->tsub_idx;
  ENTER();
  if (d_get (d) != 'I')
    UNWIND();
  while (1)
    {
      char *ta = d_template_arg (d);
      if (!ta)
	break;
      sub_save (d, ta, 1);
      if (!rv)
	rv = d_concat (d, "<", ta, 0);
      else
	rv = d_concat (d, rv, ", ", ta, 0);
    }
  if (!rv)
    UNWIND();
  if (d_get (d) != 'E')
    UNWIND();
  d->tsub_ofs = idx_save;
  d->is_template = 1;
  if (rv[strlen(rv)-1] == '>')
    RETURN (d_concat (d, rv, " >", 0));
  RETURN (d_concat (d, rv, ">", 0));
}

static char *
d_template_arg (dem_t d)
{
  struct cv_s cv;
  ENTER();

  cv_init (&cv, 0);

  RETURN_IF (d_type(d, &cv));
  if (d_peek (d) == 'X')
    {
      char *rv;
      PUSH();
      d_get (d);
      if ((rv = d_expression (d)) != 0)
	if (d_get (d) == 'E')
	  RETURN (rv);
      POP();
    }
  RETURN_IF (d_expr_primary (d));

  UNWIND();
}

static char *
d_expression (dem_t d)
{
  char *opname;
  int n_args;
  ENTER();

  if (d_peek (d) == 'L')
    RETURN (d_expr_primary (d));

  if ((opname = d_operator_name (d, &n_args)) != 0)
    {
      char *operands[2];
      int o;

      for (o=0; o<n_args; o++)
	UNWIND_UNLESS (operands[o] = d_expression (d));

      if (n_args == 1)
	RETURN (d_concat (d,  opname, "(", operands[0], ")", 0));
      else
	RETURN (d_concat (d, "(", operands[0], ") ", opname, " (", operands[1], ")", 0));
    }

  RETURN_IF (d_template_param (d));

  /* FIXME: nobody supports these, so I don't know how to print them.  */
  if (d_prefix_p (d, "st"))
    {
      char *t;

      UNWIND_UNLESS (t = d_type (d, 0));
      RETURN (d_concat (d, "st(", t, ")", 0));
    }
  if (d_prefix_p (d, "sr"))
    {
      char *t, *un, *ta;
      UNWIND_UNLESS (t = d_type (d, 0));
      UNWIND_UNLESS (un = d_unqualified_name (d, 0));
      ta = d_template_args (d);
      if (ta)
	RETURN (d_concat (d, "sr(", t, ",", un, ",", ta, ")", 0));
      else
	RETURN (d_concat (d, "sr(", t, ",", un, ")", 0));
    }

  RETURN_IF (d_template_param (d));
  RETURN_IF (d_expr_primary (d));

  UNWIND();
}

static char *
d_expr_primary_1 (dem_t d)
{
  char *type, *val;
  struct cv_s cv;
  ENTER();

  cv_init (&cv, 0);

  if (d_peek (d) == '_')
    {
      d_get(d);
      UNWIND_UNLESS (d_get (d) == 'Z');
      UNWIND_UNLESS (val = d_encoding (d));
      RETURN (val);
    }
  if (isdigit (d_peek (d)))
    {
      UNWIND_UNLESS (val = d_source_name (d));
      RETURN (val);
    }
  if (d_peek (d) == 'b')
    {
      d_get (d);
      switch (d_get (d))
	{
	case '0': RETURN ("false");
	case '1': RETURN ("true");
	default:  UNWIND();
	}
    }
  if (d_peek (d) == 'i')
    {
      d_get (d);
      RETURN_IF (d_number (d));
      UNWIND();
    }

  UNWIND_UNLESS (type = d_type (d, &cv));
  UNWIND_UNLESS (val = d_number (d));
  RETURN (d_concat (d, "(", type, ")", val, 0));
}

static char *
d_expr_primary (dem_t d)
{
  char *type, *val;
  struct cv_s cv;
  ENTER();
  if (d_get (d) != 'L')
    UNWIND();
  UNWIND_UNLESS (val = d_expr_primary_1 (d));
  UNWIND_UNLESS (d_get (d) == 'E');
  RETURN (val);
}

static char *
d_local_name (dem_t d)
{
  char *enc, *name, *disc;
  struct cv_s cv;
  ENTER();

  cv_init (&cv, 0);

  if (d_get (d) != 'Z')
    UNWIND();
  UNWIND_UNLESS (enc = d_encoding (d));
  if (d_get (d) != 'E')
    UNWIND();
  if (d_peek (d) == 's')
    {
      d_get (d);
      name = "string literal";
    }
  else
    UNWIND_UNLESS (name = d_name (d, &cv));

  disc = d_discriminator (d);

  RETURN (d_concat (d, enc, "::", name, 0));
}

static char *
d_discriminator (dem_t d)
{
  ENTER();
  if (d_get (d) != '_')
    UNWIND();
  RETURN_IF (d_number (d));
  UNWIND();
}

/*----------------------------------------------------------------------*/


static dem_t d_test;

static int exit_on_failure = 0;

static char sch[] = "_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

void
dump_subs (dem_t d)
{
  int i;
  for (i=0; i<d->sub_idx; i++)
    printf(" S%c = %s\n", sch[i], d->subs[i]);
  for (i=0; i<d->tsub_idx; i++)
    printf(" T%c = %s\n", sch[i], d->tsubs[i]);
}

char *
demangle(char *pattern)
{
  dem_t d;
  char *name;
  char *type, *rv, *rt, *rp;

  d = (dem_t) calloc (1, sizeof(struct dem_s));

  if (strncmp (pattern, "_GLOBAL_", 8) == 0)
    {
      d->pattern = d->getp = pattern;
      rv = d_encoding (d);
    }
  else if (pattern[0] != '_' || pattern[1] != 'Z')
    {
      d->pattern = d->getp = pattern;
      rv = d_type (d, 0);
    }
  else
    {
      d->pattern = d->getp = pattern+2;
      rv = d_encoding (d);
    }
  d_test = d;
#if DEBUG
  if (exit_on_failure)
    dump_subs (d);
#endif
  if (rv)
    d_strip (rv);
  else
    return pattern;
  return rv;
}

static int
getline (FILE *f, char *buf)
{
  while (1)
    {
      if (fgets (buf, 1000, f) == 0)
	return 1;
      if (buf[0] && buf[0] != '#')
	{
	  char *eol = buf + strlen(buf);
	  while (eol != buf && eol[-1] == '\r' || eol[-1] == '\n')
	    *--eol = 0;
	  return 0;
	}
    }
}

void
do_test (char *file)
{
  int n_fails = 0;
  int n_tests = 0;
  FILE *f;
  char fmt[1000], mangled[1000], exp[1000], *ts;
  f = fopen(file, "r");
  while (1)
    {
      if (getline (f, fmt))
	break;
      if (getline (f, mangled))
	break;
      if (getline (f, exp))
	break;
      if (strcmp (fmt, "--format=gnu-v3"))
	continue;
      n_tests ++;

      fprintf(stderr, "\ntesting %s\n", mangled);
      ts = demangle (mangled);
      if (!ts || strcmp (ts, exp))
	{
	  fprintf(stderr, "\nMAN: %s\nEXP: %s\nGOT: %s\n", mangled, exp, ts);
	  n_fails ++;
	  if (exit_on_failure)
	    {
	      int i;
	      fprintf(stderr, "stopped at entry %d\n", n_tests);
	      exit (1);
	    }
	}
    }
  fclose (f);
  if (n_fails)
    fprintf(stderr, "%d failures, out of %d tests\n", n_fails, n_tests);
  exit (n_fails);
}

int
main(int argc, char **argv)
{
  int i;
  if (strcmp(argv[1], "-x") == 0)
    {
      exit_on_failure = 1;
      argc--;
      argv++;
    }
  if (strcmp(argv[1], "-t") == 0)
    {
      do_test (argv[2]);
    }
  for (i=1; i<argc; i++)
    printf("%s -> %s\n", argv[i], demangle(argv[i]));
  return 0;
}



More information about the Gcc-patches mailing list