Discussion:
RFA: Improvements to libiberty v3 demangler
Ian Lance Taylor
2003-11-19 03:24:22 UTC
Permalink
Here are some patches which improve the libiberty v3 demangler. These
mostly improve the handling of CV qualifiers. Previously these would
tend to appear at the end of the string. This patch attempts to put
them in the right place. This fixes some of the errors caught by the
testsuite, and does not introduce any new errors.

After getting this far, I've realized that the whole approach used by
this code is fundamentally broken. Thie approach taken by this code
can not handle substitutions correctly, because demangling pointers
and references to substitutions, to say nothing of qualifiers,
requires knowing the structure of the substitution source, and this
code does not have that information.

Still, this patch is an improvement over what is there now, and does
fix some bugs.

Ian


2003-11-18 Ian Lance Taylor <***@wasabisystems.com>

* cp-demangle.c (demangle_operator_name): Remove space before
"sizeof".
(demangle_type_ptr): Put qualifiers in the right place. Handle
qualifiers in pointer to member specially.
(demangle_type): Handle qualifiers for pointer or reference
specially. Handle function type.
(demangle_local_name): Save and restore caret around demangling of
initial encoding.


Index: cp-demangle.c
===================================================================
RCS file: /cvs/gcc/gcc/libiberty/cp-demangle.c,v
retrieving revision 1.48
diff -u -p -r1.48 cp-demangle.c
--- cp-demangle.c 12 Aug 2003 06:58:17 -0000 1.48
+++ cp-demangle.c 19 Nov 2003 03:15:21 -0000
@@ -1147,7 +1147,7 @@ demangle_name (dm, encode_return_type)

/* Demangles and emits a <nested-name>.

- <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqulified-name> E */
+ <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name> E */

static status_t
demangle_nested_name (dm, encode_return_type)
@@ -1662,7 +1662,7 @@ demangle_operator_name (dm, short_name,
{ "rS", ">>=" , 2 },
{ "rm", "%" , 2 },
{ "rs", ">>" , 2 },
- { "sz", " sizeof" , 1 }
+ { "sz", "sizeof" , 1 }
};

const int num_operators =
@@ -2236,6 +2236,9 @@ demangle_type_ptr (dm, insert_pos, subst
{
/* A pointer-to-member. */
dyn_string_t class_type;
+ char peek;
+ int reset_caret = 0;
+ int old_caret_position;

/* Eat the 'M'. */
advance_char (dm);
@@ -2245,14 +2248,43 @@ demangle_type_ptr (dm, insert_pos, subst
RETURN_IF_ERROR (demangle_type (dm));
class_type = (dyn_string_t) result_pop (dm);

- if (peek_char (dm) == 'F')
+ peek = peek_char (dm);
+ old_caret_position = result_get_caret (dm);
+ if (peek == 'r' || peek == 'V' || peek == 'K')
+ {
+ dyn_string_t cv_qualifiers = dyn_string_new (24);
+ status_t status;
+
+ if (cv_qualifiers == NULL)
+ return STATUS_ALLOCATION_FAILED;
+
+ /* Decode all adjacent CV qualifiers. */
+ demangle_CV_qualifiers (dm, cv_qualifiers);
+
+ /* Emit them, and shift the caret left so that the
+ underlying type will be emitted before the
+ qualifiers. */
+ status = result_add_string (dm, cv_qualifiers);
+ result_shift_caret (dm, -dyn_string_length (cv_qualifiers));
+
+ dyn_string_delete (cv_qualifiers);
+ RETURN_IF_ERROR (status);
+ /* Prepend a blank. */
+ RETURN_IF_ERROR (result_add_char (dm, ' '));
+ result_shift_caret (dm, -1);
+
+ peek = peek_char (dm);
+ reset_caret = 1;
+ }
+
+ if (peek == 'F')
/* A pointer-to-member function. We want output along the
lines of `void (C::*) (int, int)'. Demangle the function
type, which would in this case give `void () (int, int)'
and set *insert_pos to the spot between the first
parentheses. */
status = demangle_type_ptr (dm, insert_pos, substitution_start);
- else if (peek_char (dm) == 'A')
+ else if (peek == 'A')
/* A pointer-to-member array variable. We want output that
looks like `int (Klass::*) [10]'. Demangle the array type
as `int () [10]', and set *insert_pos to the spot between
@@ -2286,6 +2318,9 @@ demangle_type_ptr (dm, insert_pos, subst
/* Clean up. */
dyn_string_delete (class_type);

+ if (reset_caret)
+ result_set_caret (dm, old_caret_position);
+
RETURN_IF_ERROR (status);
}
break;
@@ -2317,6 +2352,39 @@ demangle_type_ptr (dm, insert_pos, subst
RETURN_IF_ERROR (demangle_array_type (dm, insert_pos));
break;

+ case 'r':
+ case 'V':
+ case 'K':
+ /* Qualified base type. Pick up the qualifiers. */
+ {
+ dyn_string_t cv_qualifiers = dyn_string_new (24);
+
+ if (cv_qualifiers == NULL)
+ return STATUS_ALLOCATION_FAILED;
+
+ /* Pick up all adjacent CV qualifiers. */
+ demangle_CV_qualifiers (dm, cv_qualifiers);
+
+ /* Demangle the underlying type. */
+ status = demangle_type_ptr (dm, insert_pos, substitution_start);
+
+ /* Insert the qualifiers where we're told to. */
+ if (STATUS_NO_ERROR (status))
+ {
+ status = result_insert_char (dm, *insert_pos, ' ');
+ ++(*insert_pos);
+ if (STATUS_NO_ERROR (status))
+ status = result_insert_string (dm, *insert_pos, cv_qualifiers);
+ }
+
+ /* The next character should go after the qualifiers. */
+ *insert_pos += dyn_string_length (cv_qualifiers);
+
+ dyn_string_delete (cv_qualifiers);
+ RETURN_IF_ERROR (status);
+ }
+ break;
+
default:
/* No more pointer or reference tokens; this is therefore a
pointer to data. Finish up by demangling the underlying
@@ -2404,28 +2472,58 @@ demangle_type (dm)

/* Decode all adjacent CV qualifiers. */
demangle_CV_qualifiers (dm, cv_qualifiers);
- /* Emit them, and shift the caret left so that the
- underlying type will be emitted before the qualifiers. */
- status = result_add_string (dm, cv_qualifiers);
- result_shift_caret (dm, -dyn_string_length (cv_qualifiers));
- /* Clean up. */
- dyn_string_delete (cv_qualifiers);
- RETURN_IF_ERROR (status);
- /* Also prepend a blank, if needed. */
- RETURN_IF_ERROR (result_add_char (dm, ' '));
- result_shift_caret (dm, -1);

- /* Demangle the underlying type. It will be emitted before
- the CV qualifiers, since we moved the caret. */
- RETURN_IF_ERROR (demangle_type (dm));
+ /* If the underlying type is a pointer or reference type, we
+ need to call demangle_type_ptr to find out where to put
+ the qualifiers. */
+ peek = peek_char (dm);
+ if (peek == 'P' || peek == 'R' || peek == 'M')
+ {
+ status = demangle_type_ptr (dm, &insert_pos,
+ substitution_start (dm));
+ if (STATUS_NO_ERROR (status))
+ {
+ status = result_insert_char (dm, insert_pos, ' ');
+ if (STATUS_NO_ERROR (status))
+ status = result_insert_string (dm, insert_pos + 1,
+ cv_qualifiers);
+ }
+ dyn_string_delete (cv_qualifiers);
+ RETURN_IF_ERROR (status);
+ }
+ else
+ {
+ /* Emit the qualifiers, and shift the caret left so that
+ the underlying type will be emitted first. */
+ status = result_add_string (dm, cv_qualifiers);
+ result_shift_caret (dm, -dyn_string_length (cv_qualifiers));
+ /* Clean up. */
+ dyn_string_delete (cv_qualifiers);
+ RETURN_IF_ERROR (status);
+ /* Also prepend a blank, if needed. */
+ RETURN_IF_ERROR (result_add_char (dm, ' '));
+ result_shift_caret (dm, -1);

- /* Put the caret back where it was previously. */
- result_set_caret (dm, old_caret_position);
+ /* Demangle the underlying type. It will be emitted before
+ the CV qualifiers, since we moved the caret. */
+ RETURN_IF_ERROR (demangle_type (dm));
+
+ /* Put the caret back where it was previously. */
+ result_set_caret (dm, old_caret_position);
+ }
}
break;

case 'F':
- return "Non-pointer or -reference function type.";
+ /* The return type should go at the current position. */
+ insert_pos = result_caret_pos (dm);
+ /* Put in parentheses to indicate that this is a function. */
+ RETURN_IF_ERROR (result_add (dm, "()"));
+ /* Demangle the function type. The return type will be
+ inserted before the '()', and the argument list will go
+ after it. */
+ RETURN_IF_ERROR (demangle_function_type (dm, &insert_pos));
+ break;

case 'A':
RETURN_IF_ERROR (demangle_array_type (dm, NULL));
@@ -3472,10 +3570,17 @@ static status_t
demangle_local_name (dm)
demangling_t dm;
{
+ int old_caret_position = result_get_caret (dm);
+
DEMANGLE_TRACE ("local-name", dm);

RETURN_IF_ERROR (demangle_char (dm, 'Z'));
RETURN_IF_ERROR (demangle_encoding (dm));
+
+ /* Restore the caret to avoid being confused by any qualifiers we
+ may have found during the encoding. */
+ result_set_caret (dm, old_caret_position);
+
RETURN_IF_ERROR (demangle_char (dm, 'E'));
RETURN_IF_ERROR (result_add (dm, "::"));
DJ Delorie
2003-11-19 03:28:40 UTC
Permalink
Post by Ian Lance Taylor
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;
}
Ian Lance Taylor
2003-11-19 03:35:26 UTC
Permalink
Post by DJ Delorie
Post by Ian Lance Taylor
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.
Yeah, I know, but, well, it's been months, and I got tired of waiting.
In the last note I saw, back in July, you mentioned a problem with
qualifiers on class method pointers, and I saw how to fix that, or at
least what I thought you were referring to. Digging into it farther,
I saw the real deficiency.

Ian
DJ Delorie
2003-11-19 03:42:27 UTC
Permalink
Post by Ian Lance Taylor
Yeah, I know, but, well, it's been months, and I got tired of waiting.
In the last note I saw, back in July, you mentioned a problem with
qualifiers on class method pointers, and I saw how to fix that, or at
least what I thought you were referring to. Digging into it farther,
I saw the real deficiency.
Well, ok, go ahead and check your change in. I assume you've run it
through the testsuites? Any new passes?
Ian Lance Taylor
2003-11-19 04:08:51 UTC
Permalink
Post by DJ Delorie
Post by Ian Lance Taylor
Yeah, I know, but, well, it's been months, and I got tired of waiting.
In the last note I saw, back in July, you mentioned a problem with
qualifiers on class method pointers, and I saw how to fix that, or at
least what I thought you were referring to. Digging into it farther,
I saw the real deficiency.
Well, ok, go ahead and check your change in.
Checked in. Thanks.
Post by DJ Delorie
I assume you've run it
through the testsuites? Any new passes?
The libiberty testsuite has 8 new passes with these patches. There
are still 19 failures, of which at least 2 are trivial whitespace
differences.

Ian

Daniel Jacobowitz
2003-11-19 03:38:34 UTC
Permalink
Post by Ian Lance Taylor
Here are some patches which improve the libiberty v3 demangler. These
mostly improve the handling of CV qualifiers. Previously these would
tend to appear at the end of the string. This patch attempts to put
them in the right place. This fixes some of the errors caught by the
testsuite, and does not introduce any new errors.
After getting this far, I've realized that the whole approach used by
this code is fundamentally broken. Thie approach taken by this code
can not handle substitutions correctly, because demangling pointers
and references to substitutions, to say nothing of qualifiers,
requires knowing the structure of the substitution source, and this
code does not have that information.
Still, this patch is an improvement over what is there now, and does
fix some bugs.
I don't suppose you've run the GDB testsuite with these changes?
Something which moves CV qualifiers around is likely to break it. Not
badly, but enough to need to tweak the regexes again.
--
Daniel Jacobowitz
MontaVista Software Debian GNU/Linux Developer
Ian Lance Taylor
2003-11-19 04:06:12 UTC
Permalink
Post by Daniel Jacobowitz
Post by Ian Lance Taylor
Here are some patches which improve the libiberty v3 demangler. These
mostly improve the handling of CV qualifiers. Previously these would
tend to appear at the end of the string. This patch attempts to put
them in the right place. This fixes some of the errors caught by the
testsuite, and does not introduce any new errors.
After getting this far, I've realized that the whole approach used by
this code is fundamentally broken. Thie approach taken by this code
can not handle substitutions correctly, because demangling pointers
and references to substitutions, to say nothing of qualifiers,
requires knowing the structure of the substitution source, and this
code does not have that information.
Still, this patch is an improvement over what is there now, and does
fix some bugs.
I don't suppose you've run the GDB testsuite with these changes?
Something which moves CV qualifiers around is likely to break it. Not
badly, but enough to need to tweak the regexes again.
Thanks for the suggestion.

I just checked the gdb testsuite, and it doesn't appear to make any
difference. I'm not sure the gdb testsuite really tests the v3
demangler code at all. At least, I don't see any tests for it in
gdb.cp/demangle.exp.

Ian
Daniel Jacobowitz
2003-11-19 04:07:59 UTC
Permalink
Post by Ian Lance Taylor
Post by Daniel Jacobowitz
Post by Ian Lance Taylor
Here are some patches which improve the libiberty v3 demangler. These
mostly improve the handling of CV qualifiers. Previously these would
tend to appear at the end of the string. This patch attempts to put
them in the right place. This fixes some of the errors caught by the
testsuite, and does not introduce any new errors.
After getting this far, I've realized that the whole approach used by
this code is fundamentally broken. Thie approach taken by this code
can not handle substitutions correctly, because demangling pointers
and references to substitutions, to say nothing of qualifiers,
requires knowing the structure of the substitution source, and this
code does not have that information.
Still, this patch is an improvement over what is there now, and does
fix some bugs.
I don't suppose you've run the GDB testsuite with these changes?
Something which moves CV qualifiers around is likely to break it. Not
badly, but enough to need to tweak the regexes again.
Thanks for the suggestion.
I just checked the gdb testsuite, and it doesn't appear to make any
difference. I'm not sure the gdb testsuite really tests the v3
demangler code at all. At least, I don't see any tests for it in
gdb.cp/demangle.exp.
We don't check the demangler in demangle.exp, because it has its own
testsuite. It does, however, affect output in a number of other C++
tests.
--
Daniel Jacobowitz
MontaVista Software Debian GNU/Linux Developer
Loading...