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