-/* Demangler for IA64 / g++ standard C++ ABI.
- Copyright (C) 2000 CodeSourcery LLC.
+/* Demangler for IA64 / g++ V3 ABI.
+ Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc.
Written by Alex Samuel <samuel@codesourcery.com>.
+ This file is part of GNU CC.
+
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
+ In addition to the permissions in the GNU General Public License, the
+ Free Software Foundation gives you unlimited permission to link the
+ compiled version of this file into combinations with other programs,
+ and to distribute those combinations without any restriction coming
+ from the use of this file. (The General Public License restrictions
+ do apply in other respects; for example, they cover modification of
+ the file, and distribution when not linked into a combined
+ executable.)
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*/
/* This file implements demangling of C++ names mangled according to
- the IA64 / g++ standard C++ ABI. Use the cp_demangle function to
+ the IA64 / g++ V3 ABI. Use the cp_demangle function to
demangle a mangled name, or compile with the preprocessor macro
STANDALONE_DEMANGLER defined to create a demangling filter
- executable. */
+ executable (functionally similar to c++filt, but includes this
+ demangler only). */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
-#include <ctype.h>
+#include <sys/types.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#include <string.h>
#endif
+#include <ctype.h>
+
#include "ansidecl.h"
#include "libiberty.h"
#include "dyn-string.h"
/* If CP_DEMANGLE_DEBUG is defined, a trace of the grammar evaluation,
and other debugging output, will be generated. */
#ifdef CP_DEMANGLE_DEBUG
-#define DEMANGLE_TRACE(PRODUCTION, DM) \
- fprintf (stderr, " -> %-24s at position %3d\n", \
+#define DEMANGLE_TRACE(PRODUCTION, DM) \
+ fprintf (stderr, " -> %-24s at position %3d\n", \
(PRODUCTION), current_position (DM));
#else
#define DEMANGLE_TRACE(PRODUCTION, DM)
#endif
+/* Don't include <ctype.h>, to prevent additional unresolved symbols
+ from being dragged into the C++ runtime library. */
+#define IS_DIGIT(CHAR) ((CHAR) >= '0' && (CHAR) <= '9')
+#define IS_ALPHA(CHAR) \
+ (((CHAR) >= 'a' && (CHAR) <= 'z') \
+ || ((CHAR) >= 'A' && (CHAR) <= 'Z'))
+
+/* The prefix prepended by GCC to an identifier represnting the
+ anonymous namespace. */
+#define ANONYMOUS_NAMESPACE_PREFIX "_GLOBAL_"
+
+/* Character(s) to use for namespace separation in demangled output */
+#define NAMESPACE_SEPARATOR (dm->style == DMGL_JAVA ? "." : "::")
+
/* If flag_verbose is zero, some simplifications will be made to the
output to make it easier to read and supress details that are
generally not of interest to the average C++ programmer.
specification -- don't demangle special g++ manglings. */
static int flag_strict;
-/* String_list_t is an extended form of dyn_string_t which provides a link
- field. A string_list_t may safely be cast to and used as a
- dyn_string_t. */
+/* String_list_t is an extended form of dyn_string_t which provides a
+ link field and a caret position for additions to the string. A
+ string_list_t may safely be cast to and used as a dyn_string_t. */
struct string_list_def
{
+ /* The dyn_string; must be first. */
struct dyn_string string;
+
+ /* The position at which additional text is added to this string
+ (using the result_add* macros). This value is an offset from the
+ end of the string, not the beginning (and should be
+ non-positive). */
+ int caret_position;
+
+ /* The next string in the list. */
struct string_list_def *next;
};
/* The demangled text of the substitution. */
dyn_string_t text;
- /* The template parameter that this represents, indexed from zero.
- If this is not a template paramter number, the value is
- NOT_TEMPLATE_PARM. */
- int template_parm_number;
-
/* Whether this substitution represents a template item. */
int template_p : 1;
};
-#define NOT_TEMPLATE_PARM (-1)
-
/* Data structure representing a template argument list. */
struct template_arg_list_def
struct demangling_def
{
/* The full mangled name being mangled. */
- char *name;
+ const char *name;
/* Pointer into name at the current position. */
- char *next;
+ const char *next;
/* Stack for strings containing demangled result generated so far.
Text is emitted to the topmost (first) string. */
/* The most recently demangled source-name. */
dyn_string_t last_source_name;
+
+ /* Language style to use for demangled output. */
+ int style;
+
+ /* Set to non-zero iff this name is a constructor. The actual value
+ indicates what sort of constructor this is; see demangle.h. */
+ enum gnu_v3_ctor_kinds is_constructor;
+
+ /* Set to non-zero iff this name is a destructor. The actual value
+ indicates what sort of destructor this is; see demangle.h. */
+ enum gnu_v3_dtor_kinds is_destructor;
+
};
typedef struct demangling_def *demangling_t;
typedef const char *status_t;
/* Special values that can be used as a status_t. */
-#define STATUS_OK NULL
-#define STATUS_ERROR "Error."
-#define STATUS_UNIMPLEMENTED "Unimplemented."
-#define STATUS_INTERNAL_ERROR "Internal error."
+#define STATUS_OK NULL
+#define STATUS_ERROR "Error."
+#define STATUS_UNIMPLEMENTED "Unimplemented."
+#define STATUS_INTERNAL_ERROR "Internal error."
+
+/* This status code indicates a failure in malloc or realloc. */
+static const char *const status_allocation_failed = "Allocation failed.";
+#define STATUS_ALLOCATION_FAILED status_allocation_failed
+
+/* Non-zero if STATUS indicates that no error has occurred. */
+#define STATUS_NO_ERROR(STATUS) ((STATUS) == STATUS_OK)
+
+/* Evaluate EXPR, which must produce a status_t. If the status code
+ indicates an error, return from the current function with that
+ status code. */
+#define RETURN_IF_ERROR(EXPR) \
+ do \
+ { \
+ status_t s = EXPR; \
+ if (!STATUS_NO_ERROR (s)) \
+ return s; \
+ } \
+ while (0)
-static void int_to_dyn_string
+static status_t int_to_dyn_string
PARAMS ((int, dyn_string_t));
static string_list_t string_list_new
PARAMS ((int));
static void string_list_delete
PARAMS ((string_list_t));
-static void result_close_template_list
- PARAMS ((demangling_t));
-static void result_push
+static status_t result_add_separated_char
+ PARAMS ((demangling_t, int));
+static status_t result_push
PARAMS ((demangling_t));
static string_list_t result_pop
PARAMS ((demangling_t));
static int substitution_start
PARAMS ((demangling_t));
-static void substitution_add
- PARAMS ((demangling_t, int, int, int));
+static status_t substitution_add
+ PARAMS ((demangling_t, int, int));
static dyn_string_t substitution_get
PARAMS ((demangling_t, int, int *));
#ifdef CP_DEMANGLE_DEBUG
static template_arg_list_t current_template_arg_list
PARAMS ((demangling_t));
static demangling_t demangling_new
- PARAMS ((char *));
+ PARAMS ((const char *, int));
static void demangling_delete
PARAMS ((demangling_t));
(dyn_string_buf (DS)[dyn_string_length (DS) - 1])
/* Append a space character (` ') to DS if it does not already end
- with one. */
+ with one. Evaluates to 1 on success, or 0 on allocation failure. */
#define dyn_string_append_space(DS) \
- do \
- { \
- if (dyn_string_length (DS) > 0 \
- && dyn_string_last_char (DS) != ' ') \
- dyn_string_append_char ((DS), ' '); \
- } \
- while (0)
+ ((dyn_string_length (DS) > 0 \
+ && dyn_string_last_char (DS) != ' ') \
+ ? dyn_string_append_char ((DS), ' ') \
+ : 1)
/* Returns the index of the current position in the mangled name. */
#define current_position(DM) ((DM)->next - (DM)->name)
/* Returns the string containing the current demangled result. */
#define result_string(DM) (&(DM)->result->string)
-/* Appends a dyn_string_t to the demangled result. */
-#define result_append_string(DM, STRING) \
- dyn_string_append (&(DM)->result->string, (STRING))
-
-/* Appends NUL-terminated string CSTR to the demangled result. */
-#define result_append(DM, CSTR) \
- dyn_string_append_cstr (&(DM)->result->string, (CSTR))
-
-/* Appends character CHAR to the demangled result. */
-#define result_append_char(DM, CHAR) \
- dyn_string_append_char (&(DM)->result->string, (CHAR))
+/* Returns the position at which new text is inserted into the
+ demangled result. */
+#define result_caret_pos(DM) \
+ (result_length (DM) + \
+ ((string_list_t) result_string (DM))->caret_position)
+
+/* Adds a dyn_string_t to the demangled result. */
+#define result_add_string(DM, STRING) \
+ (dyn_string_insert (&(DM)->result->string, \
+ result_caret_pos (DM), (STRING)) \
+ ? STATUS_OK : STATUS_ALLOCATION_FAILED)
+
+/* Adds NUL-terminated string CSTR to the demangled result. */
+#define result_add(DM, CSTR) \
+ (dyn_string_insert_cstr (&(DM)->result->string, \
+ result_caret_pos (DM), (CSTR)) \
+ ? STATUS_OK : STATUS_ALLOCATION_FAILED)
+
+/* Adds character CHAR to the demangled result. */
+#define result_add_char(DM, CHAR) \
+ (dyn_string_insert_char (&(DM)->result->string, \
+ result_caret_pos (DM), (CHAR)) \
+ ? STATUS_OK : STATUS_ALLOCATION_FAILED)
+
+/* Inserts a dyn_string_t to the demangled result at position POS. */
+#define result_insert_string(DM, POS, STRING) \
+ (dyn_string_insert (&(DM)->result->string, (POS), (STRING)) \
+ ? STATUS_OK : STATUS_ALLOCATION_FAILED)
+
+/* Inserts NUL-terminated string CSTR to the demangled result at
+ position POS. */
+#define result_insert(DM, POS, CSTR) \
+ (dyn_string_insert_cstr (&(DM)->result->string, (POS), (CSTR)) \
+ ? STATUS_OK : STATUS_ALLOCATION_FAILED)
+
+/* Inserts character CHAR to the demangled result at position POS. */
+#define result_insert_char(DM, POS, CHAR) \
+ (dyn_string_insert_char (&(DM)->result->string, (POS), (CHAR)) \
+ ? STATUS_OK : STATUS_ALLOCATION_FAILED)
/* The length of the current demangled result. */
#define result_length(DM) \
dyn_string_length (&(DM)->result->string)
-/* Appends a space to the demangled result if the last character is
- not a space. */
-#define result_append_space(DM) \
- dyn_string_append_space (&(DM)->result->string)
-
-/* Evaluate EXPR, which must produce a status_t. If the status code
- indicates an error, return from the current function with that
- status code. */
-#define RETURN_IF_ERROR(EXPR) \
- do \
- { \
- status_t s = EXPR; \
- if (s != STATUS_OK) \
- return s; \
- } \
- while (0)
+/* Appends a (less-than, greater-than) character to the result in DM
+ to (open, close) a template argument or parameter list. Appends a
+ space first if necessary to prevent spurious elision of angle
+ brackets with the previous character. */
+#define result_open_template_list(DM) result_add_separated_char(DM, '<')
+#define result_close_template_list(DM) result_add_separated_char(DM, '>')
-/* Appends a base 10 representation of VALUE to DS. */
+/* Appends a base 10 representation of VALUE to DS. STATUS_OK on
+ success. On failure, deletes DS and returns an error code. */
-static void
+static status_t
int_to_dyn_string (value, ds)
int value;
dyn_string_t ds;
/* Handle zero up front. */
if (value == 0)
{
- dyn_string_append_char (ds, '0');
- return;
+ if (!dyn_string_append_char (ds, '0'))
+ return STATUS_ALLOCATION_FAILED;
+ return STATUS_OK;
}
/* For negative numbers, emit a minus sign. */
if (value < 0)
{
- dyn_string_append_char (ds, '-');
+ if (!dyn_string_append_char (ds, '-'))
+ return STATUS_ALLOCATION_FAILED;
value = -value;
}
while (mask > 0)
{
int digit = value / mask;
- dyn_string_append_char (ds, '0' + digit);
+
+ if (!dyn_string_append_char (ds, '0' + digit))
+ return STATUS_ALLOCATION_FAILED;
+
value -= digit * mask;
mask /= 10;
}
+
+ return STATUS_OK;
}
/* Creates a new string list node. The contents of the string are
empty, but the initial buffer allocation is LENGTH. The string
- list node should be deleted with string_list_delete. */
+ list node should be deleted with string_list_delete. Returns NULL
+ if allocation fails. */
static string_list_t
string_list_new (length)
int length;
{
- string_list_t s =
- (string_list_t) xmalloc (sizeof (struct string_list_def));
- dyn_string_init ((dyn_string_t) s, length);
+ string_list_t s = (string_list_t) malloc (sizeof (struct string_list_def));
+ s->caret_position = 0;
+ if (s == NULL)
+ return NULL;
+ if (!dyn_string_init ((dyn_string_t) s, length))
+ return NULL;
return s;
}
while (node != NULL)
{
string_list_t next = node->next;
- free (node);
+ dyn_string_delete ((dyn_string_t) node);
node = next;
}
}
-/* Appends a greater-than character to the demangled result. If the
- last character is a greater-than character, a space is inserted
- first, so that the two greater-than characters don't look like a
- right shift token. */
+/* Appends CHARACTER to the demangled result. If the current trailing
+ character of the result is CHARACTER, a space is inserted first. */
-static void
-result_close_template_list (dm)
+static status_t
+result_add_separated_char (dm, character)
demangling_t dm;
+ int character;
{
- dyn_string_t s = &dm->result->string;
- if (dyn_string_last_char (s) == '>')
- dyn_string_append_char (s, ' ');
- dyn_string_append_char (s, '>');
+ char *result = dyn_string_buf (result_string (dm));
+ int caret_pos = result_caret_pos (dm);
+
+ /* Add a space if the last character is already the character we
+ want to add. */
+ if (caret_pos > 0 && result[caret_pos - 1] == character)
+ RETURN_IF_ERROR (result_add_char (dm, ' '));
+ /* Add the character. */
+ RETURN_IF_ERROR (result_add_char (dm, character));
+
+ return STATUS_OK;
}
/* Allocates and pushes a new string onto the demangled results stack
- for DM. Subsequent demangling with DM will emit to the new string. */
+ for DM. Subsequent demangling with DM will emit to the new string.
+ Returns STATUS_OK on success, STATUS_ALLOCATION_FAILED on
+ allocation failure. */
-static void
+static status_t
result_push (dm)
demangling_t dm;
{
string_list_t new_string = string_list_new (0);
+ if (new_string == NULL)
+ /* Allocation failed. */
+ return STATUS_ALLOCATION_FAILED;
+
+ /* Link the new string to the front of the list of result strings. */
new_string->next = (string_list_t) dm->result;
dm->result = new_string;
+ return STATUS_OK;
}
/* Removes and returns the topmost element on the demangled results
return top;
}
+/* Returns the current value of the caret for the result string. The
+ value is an offet from the end of the result string. */
+
+static int
+result_get_caret (dm)
+ demangling_t dm;
+{
+ return ((string_list_t) result_string (dm))->caret_position;
+}
+
+/* Sets the value of the caret for the result string, counted as an
+ offet from the end of the result string. */
+
+static void
+result_set_caret (dm, position)
+ demangling_t dm;
+ int position;
+{
+ ((string_list_t) result_string (dm))->caret_position = position;
+}
+
+/* Shifts the position of the next addition to the result by
+ POSITION_OFFSET. A negative value shifts the caret to the left. */
+
+static void
+result_shift_caret (dm, position_offset)
+ demangling_t dm;
+ int position_offset;
+{
+ ((string_list_t) result_string (dm))->caret_position += position_offset;
+}
+
+/* Returns non-zero if the character that comes right before the place
+ where text will be added to the result is a space. In this case,
+ the caller should supress adding another space. */
+
+static int
+result_previous_char_is_space (dm)
+ demangling_t dm;
+{
+ char *result = dyn_string_buf (result_string (dm));
+ int pos = result_caret_pos (dm);
+ return pos > 0 && result[pos - 1] == ' ';
+}
+
/* Returns the start position of a fragment of the demangled result
that will be a substitution candidate. Should be called at the
start of productions that can add substitutions. */
substitution_start (dm)
demangling_t dm;
{
- return result_length (dm);
+ return result_caret_pos (dm);
}
/* Adds the suffix of the current demangled result of DM starting at
START_POSITION as a potential substitution. If TEMPLATE_P is
- non-zero, this potential substitution is a template-id.
-
- If TEMPLATE_PARM_NUMBER is not NOT_TEMPLATE_PARM, the substitution
- is for that particular <template-param>, and is distinct from other
- otherwise-identical types and other <template-param>s with
- different indices. */
+ non-zero, this potential substitution is a template-id. */
-static void
-substitution_add (dm, start_position, template_p, template_parm_number)
+static status_t
+substitution_add (dm, start_position, template_p)
demangling_t dm;
int start_position;
int template_p;
- int template_parm_number;
{
dyn_string_t result = result_string (dm);
dyn_string_t substitution = dyn_string_new (0);
int i;
- dyn_string_substring (substitution,
- result, start_position, result_length (dm));
+ if (substitution == NULL)
+ return STATUS_ALLOCATION_FAILED;
- /* Check whether SUBSTITUTION already occurs. */
- for (i = 0; i < dm->num_substitutions; ++i)
- if (dyn_string_eq (dm->substitutions[i].text, substitution)
- && dm->substitutions[i].template_parm_number == template_parm_number)
- /* Found SUBSTITUTION already present. */
- {
- /* Callers expect this function to take ownership of
- SUBSTITUTION, so delete it. */
- dyn_string_delete (substitution);
- return;
- }
+ /* Extract the substring of the current demangling result that
+ represents the subsitution candidate. */
+ if (!dyn_string_substring (substitution,
+ result, start_position, result_caret_pos (dm)))
+ {
+ dyn_string_delete (substitution);
+ return STATUS_ALLOCATION_FAILED;
+ }
/* If there's no room for the new entry, grow the array. */
if (dm->substitutions_allocated == dm->num_substitutions)
{
- dm->substitutions_allocated *= 2;
- dm->substitutions = (struct substitution_def *)
- xrealloc (dm->substitutions,
- sizeof (struct substitution_def)
- * dm->substitutions_allocated);
+ size_t new_array_size;
+ if (dm->substitutions_allocated > 0)
+ dm->substitutions_allocated *= 2;
+ else
+ dm->substitutions_allocated = 2;
+ new_array_size =
+ sizeof (struct substitution_def) * dm->substitutions_allocated;
+
+ dm->substitutions = (struct substitution_def *)
+ realloc (dm->substitutions, new_array_size);
+ if (dm->substitutions == NULL)
+ /* Realloc failed. */
+ {
+ dyn_string_delete (substitution);
+ return STATUS_ALLOCATION_FAILED;
+ }
}
/* Add the substitution to the array. */
+ i = dm->num_substitutions++;
dm->substitutions[i].text = substitution;
dm->substitutions[i].template_p = template_p;
- dm->substitutions[i].template_parm_number = template_parm_number;
- ++dm->num_substitutions;
#ifdef CP_DEMANGLE_DEBUG
substitutions_print (dm, stderr);
#endif
+
+ return STATUS_OK;
}
/* Returns the Nth-most-recent substitution. Sets *TEMPLATE_P to
#endif /* CP_DEMANGLE_DEBUG */
-/* Creates a new template argument list. */
+/* Creates a new template argument list. Returns NULL if allocation
+ fails. */
static template_arg_list_t
template_arg_list_new ()
{
- template_arg_list_t new_list
- = (template_arg_list_t) xmalloc (sizeof (struct template_arg_list_def));
+ template_arg_list_t new_list =
+ (template_arg_list_t) malloc (sizeof (struct template_arg_list_def));
+ if (new_list == NULL)
+ return NULL;
/* Initialize the new list to have no arguments. */
new_list->first_argument = NULL;
new_list->last_argument = NULL;
}
/* Allocates a demangling_t object for demangling mangled NAME. A new
- result must be pushed before the returned object can be used. */
+ result must be pushed before the returned object can be used.
+ Returns NULL if allocation fails. */
static demangling_t
-demangling_new (name)
- char *name;
+demangling_new (name, style)
+ const char *name;
+ int style;
{
- demangling_t dm = (demangling_t)
- xmalloc (sizeof (struct demangling_def));
+ demangling_t dm;
+ dm = (demangling_t) malloc (sizeof (struct demangling_def));
+ if (dm == NULL)
+ return NULL;
dm->name = name;
dm->next = name;
dm->result = NULL;
- dm->last_source_name = dyn_string_new (0);
dm->num_substitutions = 0;
dm->substitutions_allocated = 10;
- dm->substitutions = (struct substitution_def *)
- xmalloc (dm->substitutions_allocated * sizeof (struct substitution_def));
dm->template_arg_lists = NULL;
+ dm->last_source_name = dyn_string_new (0);
+ if (dm->last_source_name == NULL)
+ return NULL;
+ dm->substitutions = (struct substitution_def *)
+ malloc (dm->substitutions_allocated * sizeof (struct substitution_def));
+ if (dm->substitutions == NULL)
+ {
+ dyn_string_delete (dm->last_source_name);
+ return NULL;
+ }
+ dm->style = style;
+ dm->is_constructor = (enum gnu_v3_ctor_kinds) 0;
+ dm->is_destructor = (enum gnu_v3_dtor_kinds) 0;
return dm;
}
static status_t demangle_prefix
PARAMS ((demangling_t, int *));
static status_t demangle_unqualified_name
- PARAMS ((demangling_t));
+ PARAMS ((demangling_t, int *));
static status_t demangle_source_name
PARAMS ((demangling_t));
static status_t demangle_number
static status_t demangle_identifier
PARAMS ((demangling_t, int, dyn_string_t));
static status_t demangle_operator_name
- PARAMS ((demangling_t, int, int *));
+ PARAMS ((demangling_t, int, int *, int *));
+static status_t demangle_nv_offset
+ PARAMS ((demangling_t));
+static status_t demangle_v_offset
+ PARAMS ((demangling_t));
+static status_t demangle_call_offset
+ PARAMS ((demangling_t));
static status_t demangle_special_name
PARAMS ((demangling_t));
static status_t demangle_ctor_dtor_name
PARAMS ((demangling_t));
static status_t demangle_type_ptr
- PARAMS ((demangling_t));
+ PARAMS ((demangling_t, int *, int));
static status_t demangle_type
PARAMS ((demangling_t));
static status_t demangle_CV_qualifiers
static status_t demangle_builtin_type
PARAMS ((demangling_t));
static status_t demangle_function_type
- PARAMS ((demangling_t, int));
+ PARAMS ((demangling_t, int *));
static status_t demangle_bare_function_type
- PARAMS ((demangling_t, int));
+ PARAMS ((demangling_t, int *));
static status_t demangle_class_enum_type
PARAMS ((demangling_t, int *));
static status_t demangle_array_type
- PARAMS ((demangling_t));
-static status_t demangle_template_param
PARAMS ((demangling_t, int *));
+static status_t demangle_template_param
+ PARAMS ((demangling_t));
static status_t demangle_template_args
PARAMS ((demangling_t));
static status_t demangle_literal
static status_t demangle_expr_primary
PARAMS ((demangling_t));
static status_t demangle_substitution
- PARAMS ((demangling_t, int *, int *));
+ PARAMS ((demangling_t, int *));
static status_t demangle_local_name
PARAMS ((demangling_t));
static status_t demangle_discriminator
PARAMS ((demangling_t, int));
static status_t cp_demangle
- PARAMS ((char *, dyn_string_t));
+ PARAMS ((const char *, dyn_string_t, int));
+static status_t cp_demangle_type
+ PARAMS ((const char*, dyn_string_t));
/* When passed to demangle_bare_function_type, indicates that the
function's return type is not encoded before its parameter types. */
-#define BFT_NO_RETURN_TYPE -1
+#define BFT_NO_RETURN_TYPE NULL
/* Check that the next character is C. If so, consume it. If not,
return an error. */
else
{
if (error_message == NULL)
- error_message = strdup ("Expected ?");
+ error_message = (char *) strdup ("Expected ?");
error_message[9] = c;
return error_message;
}
<encoding> ::= <function name> <bare-function-type>
::= <data name>
- ::= <substitution> */
+ ::= <special-name> */
static status_t
demangle_encoding (dm)
demangling_t dm;
{
- int template_p;
- int special_std_substitution;
+ int encode_return_type;
int start_position;
- int start = substitution_start (dm);
template_arg_list_t old_arg_list = current_template_arg_list (dm);
char peek = peek_char (dm);
/* Remember where the name starts. If it turns out to be a template
function, we'll have to insert the return type here. */
- start_position = result_length (dm);
+ start_position = result_caret_pos (dm);
- if (peek == 'S')
- {
- RETURN_IF_ERROR (demangle_substitution (dm, &template_p,
- &special_std_substitution));
- if (special_std_substitution)
- {
- /* This was the magic `std::' substitution. */
- result_append (dm, "::");
- RETURN_IF_ERROR (demangle_encoding (dm));
- }
- }
- else if (peek == 'G' || peek == 'T')
+ if (peek == 'G' || peek == 'T')
RETURN_IF_ERROR (demangle_special_name (dm));
else
{
/* Now demangle the name. */
- RETURN_IF_ERROR (demangle_name (dm, &template_p));
+ RETURN_IF_ERROR (demangle_name (dm, &encode_return_type));
/* If there's anything left, the name was a function name, with
- maybe its return type, and its parameters types, following. */
+ maybe its return type, and its parameter types, following. */
if (!end_of_name_p (dm)
&& peek_char (dm) != 'E')
{
- if (template_p)
+ if (encode_return_type)
/* Template functions have their return type encoded. The
return type should be inserted at start_position. */
RETURN_IF_ERROR
- (demangle_bare_function_type (dm, start_position));
+ (demangle_bare_function_type (dm, &start_position));
else
/* Non-template functions don't have their return type
encoded. */
RETURN_IF_ERROR
(demangle_bare_function_type (dm, BFT_NO_RETURN_TYPE));
}
-
- substitution_add (dm, start, template_p, NOT_TEMPLATE_PARM);
}
/* Pop off template argument lists that were built during the
::= <substitution> */
static status_t
-demangle_name (dm, template_p)
+demangle_name (dm, encode_return_type)
demangling_t dm;
- int *template_p;
+ int *encode_return_type;
{
- int special_std_substitution;
int start = substitution_start (dm);
+ char peek = peek_char (dm);
+ int is_std_substitution = 0;
+
+ /* Generally, the return type is encoded if the function is a
+ template-id, and suppressed otherwise. There are a few cases,
+ though, in which the return type is not encoded even for a
+ templated function. In these cases, this flag is set. */
+ int suppress_return_type = 0;
DEMANGLE_TRACE ("name", dm);
- switch (peek_char (dm))
+ switch (peek)
{
case 'N':
/* This is a <nested-name>. */
- RETURN_IF_ERROR (demangle_nested_name (dm, template_p));
+ RETURN_IF_ERROR (demangle_nested_name (dm, encode_return_type));
break;
case 'Z':
RETURN_IF_ERROR (demangle_local_name (dm));
+ *encode_return_type = 0;
break;
case 'S':
/* The `St' substitution allows a name nested in std:: to appear
- without being enclosed in a nested name.
- <name> ::= St <unqualified-name> # ::std:: */
+ without being enclosed in a nested name. */
if (peek_char_next (dm) == 't')
{
(void) next_char (dm);
(void) next_char (dm);
- result_append (dm, "std::");
- RETURN_IF_ERROR (demangle_unqualified_name (dm));
+ RETURN_IF_ERROR (result_add (dm, "std::"));
+ RETURN_IF_ERROR
+ (demangle_unqualified_name (dm, &suppress_return_type));
+ is_std_substitution = 1;
}
else
+ RETURN_IF_ERROR (demangle_substitution (dm, encode_return_type));
+ /* Check if a template argument list immediately follows.
+ If so, then we just demangled an <unqualified-template-name>. */
+ if (peek_char (dm) == 'I')
{
- RETURN_IF_ERROR (demangle_substitution (dm, template_p,
- &special_std_substitution));
- if (special_std_substitution)
- {
- /* This was the magic `std::' substitution. We can have
- a <nested-name> or one of the unscoped names
- following. */
- result_append (dm, "::");
- RETURN_IF_ERROR (demangle_name (dm, template_p));
- }
+ /* A template name of the form std::<unqualified-name> is a
+ substitution candidate. */
+ if (is_std_substitution)
+ RETURN_IF_ERROR (substitution_add (dm, start, 0));
+ /* Demangle the <template-args> here. */
+ RETURN_IF_ERROR (demangle_template_args (dm));
+ *encode_return_type = !suppress_return_type;
}
+ else
+ *encode_return_type = 0;
+
break;
default:
/* This is an <unscoped-name> or <unscoped-template-name>. */
- RETURN_IF_ERROR (demangle_unqualified_name (dm));
+ RETURN_IF_ERROR (demangle_unqualified_name (dm, &suppress_return_type));
/* If the <unqualified-name> is followed by template args, this
is an <unscoped-template-name>. */
if (peek_char (dm) == 'I')
{
/* Add a substitution for the unqualified template name. */
- substitution_add (dm, start, 0, NOT_TEMPLATE_PARM);
+ RETURN_IF_ERROR (substitution_add (dm, start, 0));
RETURN_IF_ERROR (demangle_template_args (dm));
- *template_p = 1;
+ *encode_return_type = !suppress_return_type;
}
else
- *template_p = 0;
+ *encode_return_type = 0;
break;
}
/* Demangles and emits a <nested-name>.
- <nested-name> ::= N [<CV-qualifiers>] <prefix> <component> E */
+ <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqulified-name> E */
static status_t
-demangle_nested_name (dm, template_p)
+demangle_nested_name (dm, encode_return_type)
demangling_t dm;
- int *template_p;
+ int *encode_return_type;
{
char peek;
peek = peek_char (dm);
if (peek == 'r' || peek == 'V' || peek == 'K')
{
- /* Snarf up and emit CV qualifiers. */
- dyn_string_t cv_qualifiers = dyn_string_new (24);
+ dyn_string_t cv_qualifiers;
+ status_t status;
+
+ /* Snarf up CV qualifiers. */
+ cv_qualifiers = dyn_string_new (24);
+ if (cv_qualifiers == NULL)
+ return STATUS_ALLOCATION_FAILED;
demangle_CV_qualifiers (dm, cv_qualifiers);
- result_append_string (dm, cv_qualifiers);
+
+ /* Emit them, preceded by a space. */
+ status = result_add_char (dm, ' ');
+ if (STATUS_NO_ERROR (status))
+ status = result_add_string (dm, cv_qualifiers);
+ /* The CV qualifiers that occur in a <nested-name> will be
+ qualifiers for member functions. These are placed at the end
+ of the function. Therefore, shift the caret to the left by
+ the length of the qualifiers, so other text is inserted
+ before them and they stay at the end. */
+ result_shift_caret (dm, -dyn_string_length (cv_qualifiers) - 1);
+ /* Clean up. */
dyn_string_delete (cv_qualifiers);
- result_append_space (dm);
+ RETURN_IF_ERROR (status);
}
-
- RETURN_IF_ERROR (demangle_prefix (dm, template_p));
- /* No need to demangle the final <component>; demangle_prefix will
- handle it. */
+
+ RETURN_IF_ERROR (demangle_prefix (dm, encode_return_type));
+ /* No need to demangle the final <unqualified-name>; demangle_prefix
+ will handle it. */
RETURN_IF_ERROR (demangle_char (dm, 'E'));
return STATUS_OK;
/* Demangles and emits a <prefix>.
- <prefix> ::= <prefix> <component>
+ <prefix> ::= <prefix> <unqualified-name>
::= <template-prefix> <template-args>
::= # empty
::= <substitution>
<template-prefix> ::= <prefix>
- ::= <substitution>
-
- <component> ::= <unqualified-name>
- ::= <local-name> */
+ ::= <substitution> */
static status_t
-demangle_prefix (dm, template_p)
+demangle_prefix (dm, encode_return_type)
demangling_t dm;
- int *template_p;
+ int *encode_return_type;
{
int start = substitution_start (dm);
int nested = 0;
- /* TEMPLATE_P is updated as we decend the nesting chain. After
- <template-args>, it is set to non-zero; after everything else it
- is set to zero. */
+ /* ENCODE_RETURN_TYPE is updated as we decend the nesting chain.
+ After <template-args>, it is set to non-zero; after everything
+ else it is set to zero. */
+
+ /* Generally, the return type is encoded if the function is a
+ template-id, and suppressed otherwise. There are a few cases,
+ though, in which the return type is not encoded even for a
+ templated function. In these cases, this flag is set. */
+ int suppress_return_type = 0;
DEMANGLE_TRACE ("prefix", dm);
while (1)
{
char peek;
- int unused;
if (end_of_name_p (dm))
return "Unexpected end of name in <compound-name>.";
peek = peek_char (dm);
- if (isdigit ((unsigned char) peek)
+ /* We'll initialize suppress_return_type to false, and set it to true
+ if we end up demangling a constructor name. However, make
+ sure we're not actually about to demangle template arguments
+ -- if so, this is the <template-args> following a
+ <template-prefix>, so we'll want the previous flag value
+ around. */
+ if (peek != 'I')
+ suppress_return_type = 0;
+
+ if (IS_DIGIT ((unsigned char) peek)
|| (peek >= 'a' && peek <= 'z')
|| peek == 'C' || peek == 'D'
|| peek == 'S')
{
/* We have another level of scope qualification. */
if (nested)
- result_append (dm, "::");
+ RETURN_IF_ERROR (result_add (dm, NAMESPACE_SEPARATOR));
else
nested = 1;
if (peek == 'S')
/* The substitution determines whether this is a
- template-id. */
- RETURN_IF_ERROR (demangle_substitution (dm, template_p,
- &unused));
+ template-id. */
+ RETURN_IF_ERROR (demangle_substitution (dm, encode_return_type));
else
{
- RETURN_IF_ERROR (demangle_unqualified_name (dm));
- *template_p = 0;
+ /* It's just a name. */
+ RETURN_IF_ERROR
+ (demangle_unqualified_name (dm, &suppress_return_type));
+ *encode_return_type = 0;
}
}
else if (peek == 'Z')
RETURN_IF_ERROR (demangle_local_name (dm));
else if (peek == 'I')
{
- if (*template_p)
- return STATUS_INTERNAL_ERROR;
- /* The template name is a substitution candidate. */
- substitution_add (dm, start, 0, NOT_TEMPLATE_PARM);
RETURN_IF_ERROR (demangle_template_args (dm));
- *template_p = 1;
+
+ /* Now we want to indicate to the caller that we've
+ demangled template arguments, thus the prefix was a
+ <template-prefix>. That's so that the caller knows to
+ demangle the function's return type, if this turns out to
+ be a function name. But, if it's a member template
+ constructor or a templated conversion operator, report it
+ as untemplated. Those never get encoded return types. */
+ *encode_return_type = !suppress_return_type;
}
else if (peek == 'E')
/* All done. */
else
return "Unexpected character in <compound-name>.";
- /* Add a new substitution for the prefix thus far. */
- substitution_add (dm, start, *template_p, NOT_TEMPLATE_PARM);
+ if (peek != 'S'
+ && peek_char (dm) != 'E')
+ /* Add a new substitution for the prefix thus far. */
+ RETURN_IF_ERROR (substitution_add (dm, start, *encode_return_type));
}
}
-/* Demangles and emits an <unqualified-name>. If the
- <unqualified-name> is a function and the first element in the
- argument list should be taken to be its return type,
- ENCODE_RETURN_TYPE is non-zero.
+/* Demangles and emits an <unqualified-name>. If this
+ <unqualified-name> is for a special function type that should never
+ have its return type encoded (particularly, a constructor or
+ conversion operator), *SUPPRESS_RETURN_TYPE is set to 1; otherwise,
+ it is set to zero.
<unqualified-name> ::= <operator-name>
::= <special-name>
::= <source-name> */
static status_t
-demangle_unqualified_name (dm)
+demangle_unqualified_name (dm, suppress_return_type)
demangling_t dm;
+ int *suppress_return_type;
{
char peek = peek_char (dm);
DEMANGLE_TRACE ("unqualified-name", dm);
- if (isdigit ((unsigned char) peek))
+ /* By default, don't force suppression of the return type (though
+ non-template functions still don't get a return type encoded). */
+ *suppress_return_type = 0;
+
+ if (IS_DIGIT ((unsigned char) peek))
RETURN_IF_ERROR (demangle_source_name (dm));
else if (peek >= 'a' && peek <= 'z')
{
int num_args;
- RETURN_IF_ERROR (demangle_operator_name (dm, 0, &num_args));
+
+ /* Conversion operators never have a return type encoded. */
+ if (peek == 'c' && peek_char_next (dm) == 'v')
+ *suppress_return_type = 1;
+
+ RETURN_IF_ERROR (demangle_operator_name (dm, 0, &num_args, NULL));
}
else if (peek == 'C' || peek == 'D')
- RETURN_IF_ERROR (demangle_ctor_dtor_name (dm));
+ {
+ /* Constructors never have a return type encoded. */
+ if (peek == 'C')
+ *suppress_return_type = 1;
+
+ RETURN_IF_ERROR (demangle_ctor_dtor_name (dm));
+ }
else
return "Unexpected character in <unqualified-name>.";
dm->last_source_name));
/* Emit it. */
- result_append_string (dm, dm->last_source_name);
+ RETURN_IF_ERROR (result_add_string (dm, dm->last_source_name));
return STATUS_OK;
}
DEMANGLE_TRACE ("number", dm);
+ if (number == NULL)
+ return STATUS_ALLOCATION_FAILED;
+
demangle_number_literally (dm, number, base, is_signed);
*value = strtol (dyn_string_buf (number), NULL, base);
dyn_string_delete (number);
advance_char (dm);
/* The normal way to write a negative number is with a minus
sign. */
- dyn_string_append_char (str, '-');
+ if (!dyn_string_append_char (str, '-'))
+ return STATUS_ALLOCATION_FAILED;
}
/* Loop until we hit a non-digit. */
while (1)
{
char peek = peek_char (dm);
- if (isdigit ((unsigned char) peek)
+ if (IS_DIGIT ((unsigned char) peek)
|| (base == 36 && peek >= 'A' && peek <= 'Z'))
- /* Accumulate digits. */
- dyn_string_append_char (str, next_char (dm));
+ {
+ /* Accumulate digits. */
+ if (!dyn_string_append_char (str, next_char (dm)))
+ return STATUS_ALLOCATION_FAILED;
+ }
else
/* Not a digit? All done. */
break;
DEMANGLE_TRACE ("identifier", dm);
dyn_string_clear (identifier);
- dyn_string_resize (identifier, length);
+ if (!dyn_string_resize (identifier, length))
+ return STATUS_ALLOCATION_FAILED;
+
while (length-- > 0)
{
+ int ch;
if (end_of_name_p (dm))
return "Unexpected end of name in <identifier>.";
- dyn_string_append_char (identifier, next_char (dm));
+ ch = next_char (dm);
+
+ /* Handle extended Unicode characters. We encode them as __U{hex}_,
+ where {hex} omits leading 0's. For instance, '$' is encoded as
+ "__U24_". */
+ if (ch == '_'
+ && peek_char (dm) == '_'
+ && peek_char_next (dm) == 'U')
+ {
+ char buf[10];
+ int pos = 0;
+ advance_char (dm); advance_char (dm); length -= 2;
+ while (length-- > 0)
+ {
+ ch = next_char (dm);
+ if (!isxdigit (ch))
+ break;
+ buf[pos++] = ch;
+ }
+ if (ch != '_' || length < 0)
+ return STATUS_ERROR;
+ if (pos == 0)
+ {
+ /* __U_ just means __U. */
+ if (!dyn_string_append_cstr (identifier, "__U"))
+ return STATUS_ALLOCATION_FAILED;
+ continue;
+ }
+ else
+ {
+ buf[pos] = '\0';
+ ch = strtol (buf, 0, 16);
+ }
+ }
+
+ if (!dyn_string_append_char (identifier, ch))
+ return STATUS_ALLOCATION_FAILED;
+ }
+
+ /* GCC encodes anonymous namespaces using a `_GLOBAL_[_.$]N.'
+ followed by the source file name and some random characters.
+ Unless we're in strict mode, decipher these names appropriately. */
+ if (!flag_strict)
+ {
+ char *name = dyn_string_buf (identifier);
+ int prefix_length = strlen (ANONYMOUS_NAMESPACE_PREFIX);
+
+ /* Compare the first, fixed part. */
+ if (strncmp (name, ANONYMOUS_NAMESPACE_PREFIX, prefix_length) == 0)
+ {
+ name += prefix_length;
+ /* The next character might be a period, an underscore, or
+ dollar sign, depending on the target architecture's
+ assembler's capabilities. After that comes an `N'. */
+ if ((*name == '.' || *name == '_' || *name == '$')
+ && *(name + 1) == 'N')
+ /* This looks like the anonymous namespace identifier.
+ Replace it with something comprehensible. */
+ dyn_string_copy_cstr (identifier, "(anonymous namespace)");
+ }
}
return STATUS_OK;
/* Demangles and emits an <operator-name>. If SHORT_NAME is non-zero,
the short form is emitted; otherwise the full source form
(`operator +' etc.) is emitted. *NUM_ARGS is set to the number of
- operands that the operator takes.
+ operands that the operator takes. If TYPE_ARG is non-NULL,
+ *TYPE_ARG is set to 1 if the first argument is a type and 0
+ otherwise.
<operator-name>
::= nw # new
::= cl # ()
::= ix # []
::= qu # ?
- ::= sz # sizeof
+ ::= st # sizeof (a type)
+ ::= sz # sizeof (an expression)
::= cv <type> # cast
- ::= vx <source-name> # vendor extended operator */
+ ::= v [0-9] <source-name> # vendor extended operator */
static status_t
-demangle_operator_name (dm, short_name, num_args)
+demangle_operator_name (dm, short_name, num_args, type_arg)
demangling_t dm;
int short_name;
int *num_args;
+ int *type_arg;
{
struct operator_code
{
/* The mangled code for this operator. */
- const char *code;
+ const char *const code;
/* The source name of this operator. */
- const char *name;
+ const char *const name;
/* The number of arguments this operator takes. */
- int num_args;
+ const int num_args;
};
static const struct operator_code operators[] =
{ "pm", "->*" , 2 },
{ "pp", "++" , 1 },
{ "ps", "+" , 1 },
+ { "pt", "->" , 2 },
{ "qu", "?" , 3 },
{ "rM", "%=" , 2 },
{ "rS", ">>=" , 2 },
DEMANGLE_TRACE ("operator-name", dm);
- /* Is this a vendor extended operator? */
- if (c0 == 'v' && c1 == 'x')
+ /* Assume the first argument is not a type. */
+ if (type_arg)
+ *type_arg = 0;
+
+ /* Is this a vendor-extended operator? */
+ if (c0 == 'v' && IS_DIGIT (c1))
{
- result_append (dm, "operator");
+ RETURN_IF_ERROR (result_add (dm, "operator "));
RETURN_IF_ERROR (demangle_source_name (dm));
*num_args = 0;
return STATUS_OK;
/* Is this a conversion operator? */
if (c0 == 'c' && c1 == 'v')
{
- result_append (dm, "operator ");
+ RETURN_IF_ERROR (result_add (dm, "operator "));
/* Demangle the converted-to type. */
RETURN_IF_ERROR (demangle_type (dm));
*num_args = 0;
return STATUS_OK;
}
+ /* Is it the sizeof variant that takes a type? */
+ if (c0 == 's' && c1 == 't')
+ {
+ RETURN_IF_ERROR (result_add (dm, " sizeof"));
+ *num_args = 1;
+ if (type_arg)
+ *type_arg = 1;
+ return STATUS_OK;
+ }
+
/* Perform a binary search for the operator code. */
while (1)
{
/* Found it. */
{
if (!short_name)
- result_append (dm, "operator");
- result_append (dm, p->name);
+ RETURN_IF_ERROR (result_add (dm, "operator"));
+ RETURN_IF_ERROR (result_add (dm, p->name));
*num_args = p->num_args;
return STATUS_OK;
}
}
+/* Demangles and omits an <nv-offset>.
+
+ <nv-offset> ::= <offset number> # non-virtual base override */
+
+static status_t
+demangle_nv_offset (dm)
+ demangling_t dm;
+{
+ dyn_string_t number;
+ status_t status = STATUS_OK;
+
+ DEMANGLE_TRACE ("h-offset", dm);
+
+ /* Demangle the offset. */
+ number = dyn_string_new (4);
+ if (number == NULL)
+ return STATUS_ALLOCATION_FAILED;
+ demangle_number_literally (dm, number, 10, 1);
+
+ /* Don't display the offset unless in verbose mode. */
+ if (flag_verbose)
+ {
+ status = result_add (dm, " [nv:");
+ if (STATUS_NO_ERROR (status))
+ status = result_add_string (dm, number);
+ if (STATUS_NO_ERROR (status))
+ status = result_add_char (dm, ']');
+ }
+
+ /* Clean up. */
+ dyn_string_delete (number);
+ RETURN_IF_ERROR (status);
+ return STATUS_OK;
+}
+
+/* Demangles and emits a <v-offset>.
+
+ <v-offset> ::= <offset number> _ <virtual offset number>
+ # virtual base override, with vcall offset */
+
+static status_t
+demangle_v_offset (dm)
+ demangling_t dm;
+{
+ dyn_string_t number;
+ status_t status = STATUS_OK;
+
+ DEMANGLE_TRACE ("v-offset", dm);
+
+ /* Demangle the offset. */
+ number = dyn_string_new (4);
+ if (number == NULL)
+ return STATUS_ALLOCATION_FAILED;
+ demangle_number_literally (dm, number, 10, 1);
+
+ /* Don't display the offset unless in verbose mode. */
+ if (flag_verbose)
+ {
+ status = result_add (dm, " [v:");
+ if (STATUS_NO_ERROR (status))
+ status = result_add_string (dm, number);
+ if (STATUS_NO_ERROR (status))
+ result_add_char (dm, ',');
+ }
+ dyn_string_delete (number);
+ RETURN_IF_ERROR (status);
+
+ /* Demangle the separator. */
+ RETURN_IF_ERROR (demangle_char (dm, '_'));
+
+ /* Demangle the vcall offset. */
+ number = dyn_string_new (4);
+ if (number == NULL)
+ return STATUS_ALLOCATION_FAILED;
+ demangle_number_literally (dm, number, 10, 1);
+
+ /* Don't display the vcall offset unless in verbose mode. */
+ if (flag_verbose)
+ {
+ status = result_add_string (dm, number);
+ if (STATUS_NO_ERROR (status))
+ status = result_add_char (dm, ']');
+ }
+ dyn_string_delete (number);
+ RETURN_IF_ERROR (status);
+
+ return STATUS_OK;
+}
+
+/* Demangles and emits a <call-offset>.
+
+ <call-offset> ::= h <nv-offset> _
+ ::= v <v-offset> _ */
+
+static status_t
+demangle_call_offset (dm)
+ demangling_t dm;
+{
+ DEMANGLE_TRACE ("call-offset", dm);
+
+ switch (peek_char (dm))
+ {
+ case 'h':
+ advance_char (dm);
+ /* Demangle the offset. */
+ RETURN_IF_ERROR (demangle_nv_offset (dm));
+ /* Demangle the separator. */
+ RETURN_IF_ERROR (demangle_char (dm, '_'));
+ break;
+
+ case 'v':
+ advance_char (dm);
+ /* Demangle the offset. */
+ RETURN_IF_ERROR (demangle_v_offset (dm));
+ /* Demangle the separator. */
+ RETURN_IF_ERROR (demangle_char (dm, '_'));
+ break;
+
+ default:
+ return "Unrecognized <call-offset>.";
+ }
+
+ return STATUS_OK;
+}
+
/* Demangles and emits a <special-name>.
<special-name> ::= GV <object name> # Guard variable
- ::= Th[n] <offset number> _ <base name> <base encoding>
- # non-virtual base override thunk
- ::= Tv[n] <offset number> _ <vcall offset number>
- _ <base encoding>
- # virtual base override thunk
::= TV <type> # virtual table
::= TT <type> # VTT
::= TI <type> # typeinfo structure
::= TS <type> # typeinfo name
+ Other relevant productions include thunks:
+
+ <special-name> ::= T <call-offset> <base encoding>
+ # base is the nominal target function of thunk
+
+ <special-name> ::= Tc <call-offset> <call-offset> <base encoding>
+ # base is the nominal target function of thunk
+ # first call-offset is 'this' adjustment
+ # second call-offset is result adjustment
+
+ where
+
+ <call-offset> ::= h <nv-offset> _
+ ::= v <v-offset> _
+
Also demangles the special g++ manglings,
- <special-name> ::= CT <type> <offset number> _ <base type>
+ <special-name> ::= TC <type> <offset number> _ <base type>
# construction vtable
::= TF <type> # typeinfo function (old ABI only)
::= TJ <type> # java Class structure */
if (peek == 'G')
{
- /* A guard variable name. Consume the G. */
+ /* Consume the G. */
advance_char (dm);
- RETURN_IF_ERROR (demangle_char (dm, 'V'));
- result_append (dm, "guard variable for ");
- RETURN_IF_ERROR (demangle_name (dm, &unused));
- }
- else if (peek == 'T')
+ switch (peek_char (dm))
+ {
+ case 'V':
+ /* A guard variable name. */
+ advance_char (dm);
+ RETURN_IF_ERROR (result_add (dm, "guard variable for "));
+ RETURN_IF_ERROR (demangle_name (dm, &unused));
+ break;
+
+ case 'R':
+ /* A reference temporary. */
+ advance_char (dm);
+ RETURN_IF_ERROR (result_add (dm, "reference temporary for "));
+ RETURN_IF_ERROR (demangle_name (dm, &unused));
+ break;
+
+ default:
+ return "Unrecognized <special-name>.";
+ }
+ }
+ else if (peek == 'T')
{
+ status_t status = STATUS_OK;
+
/* Other C++ implementation miscellania. Consume the T. */
advance_char (dm);
case 'V':
/* Virtual table. */
advance_char (dm);
- result_append (dm, "vtable for ");
+ RETURN_IF_ERROR (result_add (dm, "vtable for "));
RETURN_IF_ERROR (demangle_type (dm));
break;
case 'T':
/* VTT structure. */
advance_char (dm);
- result_append (dm, "VTT for ");
+ RETURN_IF_ERROR (result_add (dm, "VTT for "));
RETURN_IF_ERROR (demangle_type (dm));
break;
case 'I':
/* Typeinfo structure. */
advance_char (dm);
- result_append (dm, "typeinfo for ");
+ RETURN_IF_ERROR (result_add (dm, "typeinfo for "));
RETURN_IF_ERROR (demangle_type (dm));
break;
case 'F':
/* Typeinfo function. Used only in old ABI with new mangling. */
advance_char (dm);
- result_append (dm, "typeinfo fn for ");
+ RETURN_IF_ERROR (result_add (dm, "typeinfo fn for "));
RETURN_IF_ERROR (demangle_type (dm));
break;
case 'S':
/* Character string containing type name, used in typeinfo. */
advance_char (dm);
- result_append (dm, "typeinfo name for ");
+ RETURN_IF_ERROR (result_add (dm, "typeinfo name for "));
RETURN_IF_ERROR (demangle_type (dm));
break;
case 'J':
/* The java Class variable corresponding to a C++ class. */
advance_char (dm);
- result_append (dm, "java Class for ");
+ RETURN_IF_ERROR (result_add (dm, "java Class for "));
RETURN_IF_ERROR (demangle_type (dm));
break;
case 'h':
/* Non-virtual thunk. */
advance_char (dm);
- result_append (dm, "non-virtual thunk");
- /* Demangle and emit the offset. */
- number = dyn_string_new (4);
- demangle_number_literally (dm, number, 10, 1);
- /* Don't display the offset unless in verbose mode. */
- if (flag_verbose)
- {
- result_append_char (dm, ' ');
- result_append_string (dm, number);
- }
- dyn_string_delete (number);
+ RETURN_IF_ERROR (result_add (dm, "non-virtual thunk"));
+ RETURN_IF_ERROR (demangle_nv_offset (dm));
/* Demangle the separator. */
RETURN_IF_ERROR (demangle_char (dm, '_'));
/* Demangle and emit the target name and function type. */
- result_append (dm, " to ");
+ RETURN_IF_ERROR (result_add (dm, " to "));
RETURN_IF_ERROR (demangle_encoding (dm));
break;
case 'v':
/* Virtual thunk. */
advance_char (dm);
- result_append (dm, "virtual thunk ");
- /* Demangle and emit the offset. */
- number = dyn_string_new (4);
- demangle_number_literally (dm, number, 10, 1);
- /* Don't display the offset unless in verbose mode. */
- if (flag_verbose)
- {
- result_append_string (dm, number);
- result_append_char (dm, ' ');
- }
- dyn_string_delete (number);
- /* Demangle the separator. */
- RETURN_IF_ERROR (demangle_char (dm, '_'));
- /* Demangle and emit the vcall offset. */
- number = dyn_string_new (4);
- demangle_number_literally (dm, number, 10, 1);
- /* Don't display the vcall offset unless in verbose mode. */
- if (flag_verbose)
- {
- result_append_string (dm, number);
- result_append_char (dm, ' ');
- }
- dyn_string_delete (number);
+ RETURN_IF_ERROR (result_add (dm, "virtual thunk"));
+ RETURN_IF_ERROR (demangle_v_offset (dm));
/* Demangle the separator. */
RETURN_IF_ERROR (demangle_char (dm, '_'));
/* Demangle and emit the target function. */
- result_append (dm, "to ");
+ RETURN_IF_ERROR (result_add (dm, " to "));
+ RETURN_IF_ERROR (demangle_encoding (dm));
+ break;
+
+ case 'c':
+ /* Covariant return thunk. */
+ advance_char (dm);
+ RETURN_IF_ERROR (result_add (dm, "covariant return thunk"));
+ RETURN_IF_ERROR (demangle_call_offset (dm));
+ RETURN_IF_ERROR (demangle_call_offset (dm));
+ /* Demangle and emit the target function. */
+ RETURN_IF_ERROR (result_add (dm, " to "));
RETURN_IF_ERROR (demangle_encoding (dm));
break;
/* TC is a special g++ mangling for a construction vtable. */
if (!flag_strict)
{
+ dyn_string_t derived_type;
+
advance_char (dm);
- result_append (dm, "construction vtable for ");
+ RETURN_IF_ERROR (result_add (dm, "construction vtable for "));
+
+ /* Demangle the derived type off to the side. */
+ RETURN_IF_ERROR (result_push (dm));
RETURN_IF_ERROR (demangle_type (dm));
+ derived_type = (dyn_string_t) result_pop (dm);
+
/* Demangle the offset. */
number = dyn_string_new (4);
+ if (number == NULL)
+ {
+ dyn_string_delete (derived_type);
+ return STATUS_ALLOCATION_FAILED;
+ }
demangle_number_literally (dm, number, 10, 1);
/* Demangle the underscore separator. */
- RETURN_IF_ERROR (demangle_char (dm, '_'));
+ status = demangle_char (dm, '_');
+
/* Demangle the base type. */
- result_append (dm, "-in-");
- RETURN_IF_ERROR (demangle_type (dm));
+ if (STATUS_NO_ERROR (status))
+ status = demangle_type (dm);
+
+ /* Emit the derived type. */
+ if (STATUS_NO_ERROR (status))
+ status = result_add (dm, "-in-");
+ if (STATUS_NO_ERROR (status))
+ status = result_add_string (dm, derived_type);
+ dyn_string_delete (derived_type);
+
/* Don't display the offset unless in verbose mode. */
if (flag_verbose)
{
- result_append_char (dm, ' ');
- result_append_string (dm, number);
+ status = result_add_char (dm, ' ');
+ if (STATUS_NO_ERROR (status))
+ result_add_string (dm, number);
}
dyn_string_delete (number);
+ RETURN_IF_ERROR (status);
break;
}
/* If flag_strict, fall through. */
::= C1 # complete object (in-charge) ctor
::= C2 # base object (not-in-charge) ctor
::= C3 # complete object (in-charge) allocating ctor
- ::= C4 # base object (not-in-charge) allocating ctor
::= D0 # deleting (in-charge) dtor
::= D1 # complete object (in-charge) dtor
::= D2 # base object (not-in-charge) dtor */
{
"in-charge",
"not-in-charge",
- "in-charge allocating",
- "not-in-charge allocating"
+ "allocating"
};
static const char *const dtor_flavors[] =
{
{
/* A constructor name. Consume the C. */
advance_char (dm);
- if (peek_char (dm) < '1' || peek_char (dm) > '4')
+ flavor = next_char (dm);
+ if (flavor < '1' || flavor > '3')
return "Unrecognized constructor.";
- result_append_string (dm, dm->last_source_name);
+ RETURN_IF_ERROR (result_add_string (dm, dm->last_source_name));
+ switch (flavor)
+ {
+ case '1': dm->is_constructor = gnu_v3_complete_object_ctor;
+ break;
+ case '2': dm->is_constructor = gnu_v3_base_object_ctor;
+ break;
+ case '3': dm->is_constructor = gnu_v3_complete_object_allocating_ctor;
+ break;
+ }
/* Print the flavor of the constructor if in verbose mode. */
- flavor = next_char (dm) - '1';
if (flag_verbose)
{
- result_append (dm, "[");
- result_append (dm, ctor_flavors[flavor]);
- result_append_char (dm, ']');
+ RETURN_IF_ERROR (result_add (dm, "["));
+ RETURN_IF_ERROR (result_add (dm, ctor_flavors[flavor - '1']));
+ RETURN_IF_ERROR (result_add_char (dm, ']'));
}
}
else if (peek == 'D')
{
/* A destructor name. Consume the D. */
advance_char (dm);
- if (peek_char (dm) < '0' || peek_char (dm) > '2')
+ flavor = next_char (dm);
+ if (flavor < '0' || flavor > '2')
return "Unrecognized destructor.";
- result_append_char (dm, '~');
- result_append_string (dm, dm->last_source_name);
+ RETURN_IF_ERROR (result_add_char (dm, '~'));
+ RETURN_IF_ERROR (result_add_string (dm, dm->last_source_name));
+ switch (flavor)
+ {
+ case '0': dm->is_destructor = gnu_v3_deleting_dtor;
+ break;
+ case '1': dm->is_destructor = gnu_v3_complete_object_dtor;
+ break;
+ case '2': dm->is_destructor = gnu_v3_base_object_dtor;
+ break;
+ }
/* Print the flavor of the destructor if in verbose mode. */
- flavor = next_char (dm) - '0';
if (flag_verbose)
{
- result_append (dm, " [");
- result_append (dm, dtor_flavors[flavor]);
- result_append_char (dm, ']');
+ RETURN_IF_ERROR (result_add (dm, " ["));
+ RETURN_IF_ERROR (result_add (dm, dtor_flavors[flavor - '0']));
+ RETURN_IF_ERROR (result_add_char (dm, ']'));
}
}
else
a pointer to data or pointer to function to construct the right
output syntax. C++'s pointer syntax is hairy.
+ This function adds substitution candidates for every nested
+ pointer/reference type it processes, including the outermost, final
+ type, assuming the substitution starts at SUBSTITUTION_START in the
+ demangling result. For example, if this function demangles
+ `PP3Foo', it will add a substitution for `Foo', `Foo*', and
+ `Foo**', in that order.
+
+ *INSERT_POS is a quantity used internally, when this function calls
+ itself recursively, to figure out where to insert pointer
+ punctuation on the way up. On entry to this function, INSERT_POS
+ should point to a temporary value, but that value need not be
+ initialized.
+
<type> ::= P <type>
::= R <type>
::= <pointer-to-member-type>
<pointer-to-member-type> ::= M </class/ type> </member/ type> */
static status_t
-demangle_type_ptr (dm)
+demangle_type_ptr (dm, insert_pos, substitution_start)
demangling_t dm;
+ int *insert_pos;
+ int substitution_start;
{
- char next;
status_t status;
-
- /* Collect pointer symbols into this string. */
- dyn_string_t symbols = dyn_string_new (10);
+ int is_substitution_candidate = 1;
DEMANGLE_TRACE ("type*", dm);
/* Scan forward, collecting pointers and references into symbols,
until we hit something else. Then emit the type. */
- while (1)
+ switch (peek_char (dm))
{
- next = peek_char (dm);
- if (next == 'P')
- {
- dyn_string_append_char (symbols, '*');
- advance_char (dm);
- }
- else if (next == 'R')
- {
- dyn_string_append_char (symbols, '&');
- advance_char (dm);
- }
- else if (next == 'M')
- {
- /* Pointer-to-member. */
- dyn_string_t class_type;
-
- /* Eat the 'M'. */
- advance_char (dm);
-
- /* Capture the type of which this is a pointer-to-member. */
- result_push (dm);
- RETURN_IF_ERROR (demangle_type (dm));
- class_type = (dyn_string_t) result_pop (dm);
-
- /* Build the pointer-to-member notation. It comes before
- other pointer and reference qualifiers -- */
- dyn_string_prepend_cstr (symbols, "::*");
- dyn_string_prepend (symbols, class_type);
- dyn_string_delete (class_type);
+ case 'P':
+ /* A pointer. Snarf the `P'. */
+ advance_char (dm);
+ /* Demangle the underlying type. */
+ RETURN_IF_ERROR (demangle_type_ptr (dm, insert_pos,
+ substitution_start));
+ /* Insert an asterisk where we're told to; it doesn't
+ necessarily go at the end. If we're doing Java style output,
+ there is no pointer symbol. */
+ if (dm->style != DMGL_JAVA)
+ RETURN_IF_ERROR (result_insert_char (dm, *insert_pos, '*'));
+ /* The next (outermost) pointer or reference character should go
+ after this one. */
+ ++(*insert_pos);
+ break;
- if (peek_char (dm) == 'F')
- continue;
+ case 'R':
+ /* A reference. Snarf the `R'. */
+ advance_char (dm);
+ /* Demangle the underlying type. */
+ RETURN_IF_ERROR (demangle_type_ptr (dm, insert_pos,
+ substitution_start));
+ /* Insert an ampersand where we're told to; it doesn't
+ necessarily go at the end. */
+ RETURN_IF_ERROR (result_insert_char (dm, *insert_pos, '&'));
+ /* The next (outermost) pointer or reference character should go
+ after this one. */
+ ++(*insert_pos);
+ break;
- /* Demangle the type of the pointed-to member. */
+ case 'M':
+ {
+ /* A pointer-to-member. */
+ dyn_string_t class_type;
+
+ /* Eat the 'M'. */
+ advance_char (dm);
+
+ /* Capture the type of which this is a pointer-to-member. */
+ RETURN_IF_ERROR (result_push (dm));
+ RETURN_IF_ERROR (demangle_type (dm));
+ class_type = (dyn_string_t) result_pop (dm);
+
+ if (peek_char (dm) == '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')
+ /* 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
+ the parentheses. */
+ status = demangle_array_type (dm, insert_pos);
+ else
+ {
+ /* A pointer-to-member variable. Demangle the type of the
+ pointed-to member. */
status = demangle_type (dm);
/* Make it pretty. */
- result_append_space (dm);
- /* Add the pointer-to-member syntax, and other pointer and
- reference symbols. */
- result_append_string (dm, symbols);
- /* Clean up. */
- dyn_string_delete (symbols);
-
- RETURN_IF_ERROR (status);
- return STATUS_OK;
- }
- else if (next == 'F')
- {
- /* Ooh, tricky, a pointer-to-function. */
- int position = result_length (dm);
- result_append_char (dm, '(');
- result_append_string (dm, symbols);
- result_append_char (dm, ')');
- dyn_string_delete (symbols);
-
- RETURN_IF_ERROR (demangle_function_type (dm, position));
- return STATUS_OK;
+ if (STATUS_NO_ERROR (status)
+ && !result_previous_char_is_space (dm))
+ status = result_add_char (dm, ' ');
+ /* The pointer-to-member notation (e.g. `C::*') follows the
+ member's type. */
+ *insert_pos = result_caret_pos (dm);
}
- else
- {
- /* No more pointe or reference tokens. Finish up. */
- status = demangle_type (dm);
- result_append_string (dm, symbols);
- dyn_string_delete (symbols);
+ /* Build the pointer-to-member notation. */
+ if (STATUS_NO_ERROR (status))
+ status = result_insert (dm, *insert_pos, "::*");
+ if (STATUS_NO_ERROR (status))
+ status = result_insert_string (dm, *insert_pos, class_type);
+ /* There may be additional levels of (pointer or reference)
+ indirection in this type. If so, the `*' and `&' should be
+ added after the pointer-to-member notation (e.g. `C::*&' for
+ a reference to a pointer-to-member of class C). */
+ *insert_pos += dyn_string_length (class_type) + 3;
+
+ /* Clean up. */
+ dyn_string_delete (class_type);
+
+ RETURN_IF_ERROR (status);
+ }
+ break;
+
+ case 'F':
+ /* Ooh, tricky, a pointer-to-function. When we demangle the
+ function type, the return type should go at the very
+ beginning. */
+ *insert_pos = result_caret_pos (dm);
+ /* The parentheses indicate this is a function pointer or
+ reference type. */
+ RETURN_IF_ERROR (result_add (dm, "()"));
+ /* Now 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));
+ /* We should now have something along the lines of
+ `void () (int, int)'. The pointer or reference characters
+ have to inside the first set of parentheses. *insert_pos has
+ already been updated to point past the end of the return
+ type. Move it one character over so it points inside the
+ `()'. */
+ ++(*insert_pos);
+ break;
- RETURN_IF_ERROR (status);
- return STATUS_OK;
- }
+ case 'A':
+ /* An array pointer or reference. demangle_array_type will figure
+ out where the asterisks and ampersands go. */
+ RETURN_IF_ERROR (demangle_array_type (dm, insert_pos));
+ break;
+
+ default:
+ /* No more pointer or reference tokens; this is therefore a
+ pointer to data. Finish up by demangling the underlying
+ type. */
+ RETURN_IF_ERROR (demangle_type (dm));
+ /* The pointer or reference characters follow the underlying
+ type, as in `int*&'. */
+ *insert_pos = result_caret_pos (dm);
+ /* Because of the production <type> ::= <substitution>,
+ demangle_type will already have added the underlying type as
+ a substitution candidate. Don't do it again. */
+ is_substitution_candidate = 0;
+ break;
}
+
+ if (is_substitution_candidate)
+ RETURN_IF_ERROR (substitution_add (dm, substitution_start, 0));
+
+ return STATUS_OK;
}
/* Demangles and emits a <type>.
::= <array-type>
::= <pointer-to-member-type>
::= <template-param>
+ ::= <template-template-param> <template-args>
::= <CV-qualifiers> <type>
::= P <type> # pointer-to
::= R <type> # reference-to
{
int start = substitution_start (dm);
char peek = peek_char (dm);
- int template_p = 0;
- int special_std_substitution;
- int is_builtin_type = 0;
+ char peek_next;
+ int encode_return_type = 0;
template_arg_list_t old_arg_list = current_template_arg_list (dm);
- int template_parm = NOT_TEMPLATE_PARM;
+ int insert_pos;
+
+ /* A <type> can be a <substitution>; therefore, this <type> is a
+ substitution candidate unless a special condition holds (see
+ below). */
+ int is_substitution_candidate = 1;
DEMANGLE_TRACE ("type", dm);
/* A <class-enum-type> can start with a digit (a <source-name>), an
N (a <nested-name>), or a Z (a <local-name>). */
- if (isdigit ((unsigned char) peek) || peek == 'N' || peek == 'Z')
- RETURN_IF_ERROR (demangle_class_enum_type (dm, &template_p));
- else if (peek >= 'a' && peek <= 'z')
+ if (IS_DIGIT ((unsigned char) peek) || peek == 'N' || peek == 'Z')
+ RETURN_IF_ERROR (demangle_class_enum_type (dm, &encode_return_type));
+ /* Lower-case letters begin <builtin-type>s, except for `r', which
+ denotes restrict. */
+ else if (peek >= 'a' && peek <= 'z' && peek != 'r')
{
RETURN_IF_ERROR (demangle_builtin_type (dm));
- is_builtin_type = 1;
+ /* Built-in types are not substitution candidates. */
+ is_substitution_candidate = 0;
}
else
switch (peek)
case 'r':
case 'V':
case 'K':
+ /* CV-qualifiers (including restrict). We have to demangle
+ them off to the side, since C++ syntax puts them in a funny
+ place for qualified pointer and reference types. */
{
status_t status;
dyn_string_t cv_qualifiers = dyn_string_new (24);
- demangle_CV_qualifiers (dm, cv_qualifiers);
+ int old_caret_position = result_get_caret (dm);
- /* If the qualifiers apply to a pointer or reference, they
- need to come after the whole qualified type. */
- if (peek_char (dm) == 'P' || peek_char (dm) == 'R')
- {
- status = demangle_type (dm);
- result_append_space (dm);
- result_append_string (dm, cv_qualifiers);
- }
- /* Otherwise, the qualifiers come first. */
- else
- {
- result_append_string (dm, cv_qualifiers);
- result_append_space (dm);
- status = demangle_type (dm);
- }
+ 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));
+ /* 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));
+
+ /* Put the caret back where it was previously. */
+ result_set_caret (dm, old_caret_position);
}
break;
return "Non-pointer or -reference function type.";
case 'A':
- RETURN_IF_ERROR (demangle_array_type (dm));
+ RETURN_IF_ERROR (demangle_array_type (dm, NULL));
break;
case 'T':
- RETURN_IF_ERROR (demangle_template_param (dm, &template_parm));
+ /* It's either a <template-param> or a
+ <template-template-param>. In either case, demangle the
+ `T' token first. */
+ RETURN_IF_ERROR (demangle_template_param (dm));
+
+ /* Check for a template argument list; if one is found, it's a
+ <template-template-param> ::= <template-param>
+ ::= <substitution> */
+ if (peek_char (dm) == 'I')
+ {
+ /* Add a substitution candidate. The template parameter
+ `T' token is a substitution candidate by itself,
+ without the template argument list. */
+ RETURN_IF_ERROR (substitution_add (dm, start, encode_return_type));
+
+ /* Now demangle the template argument list. */
+ RETURN_IF_ERROR (demangle_template_args (dm));
+ /* The entire type, including the template template
+ parameter and its argument list, will be added as a
+ substitution candidate below. */
+ }
+
break;
case 'S':
- RETURN_IF_ERROR (demangle_substitution (dm, &template_p,
- &special_std_substitution));
- if (special_std_substitution)
+ /* First check if this is a special substitution. If it is,
+ this is a <class-enum-type>. Special substitutions have a
+ letter following the `S'; other substitutions have a digit
+ or underscore. */
+ peek_next = peek_char_next (dm);
+ if (IS_DIGIT (peek_next) || peek_next == '_')
{
- /* This was the magic `std::' substitution. What follows
- must be a class name in that namespace. */
- result_append (dm, "::");
- RETURN_IF_ERROR (demangle_class_enum_type (dm, &template_p));
+ RETURN_IF_ERROR (demangle_substitution (dm, &encode_return_type));
+
+ /* The substituted name may have been a template name.
+ Check if template arguments follow, and if so, demangle
+ them. */
+ if (peek_char (dm) == 'I')
+ RETURN_IF_ERROR (demangle_template_args (dm));
+ else
+ /* A substitution token is not itself a substitution
+ candidate. (However, if the substituted template is
+ instantiated, the resulting type is.) */
+ is_substitution_candidate = 0;
+ }
+ else
+ {
+ /* Now some trickiness. We have a special substitution
+ here. Often, the special substitution provides the
+ name of a template that's subsequently instantiated,
+ for instance `SaIcE' => std::allocator<char>. In these
+ cases we need to add a substitution candidate for the
+ entire <class-enum-type> and thus don't want to clear
+ the is_substitution_candidate flag.
+
+ However, it's possible that what we have here is a
+ substitution token representing an entire type, such as
+ `Ss' => std::string. In this case, we mustn't add a
+ new substitution candidate for this substitution token.
+ To detect this case, remember where the start of the
+ substitution token is. */
+ const char *next = dm->next;
+ /* Now demangle the <class-enum-type>. */
+ RETURN_IF_ERROR
+ (demangle_class_enum_type (dm, &encode_return_type));
+ /* If all that was just demangled is the two-character
+ special substitution token, supress the addition of a
+ new candidate for it. */
+ if (dm->next == next + 2)
+ is_substitution_candidate = 0;
}
+
break;
case 'P':
case 'R':
case 'M':
- RETURN_IF_ERROR (demangle_type_ptr (dm));
+ RETURN_IF_ERROR (demangle_type_ptr (dm, &insert_pos, start));
+ /* demangle_type_ptr adds all applicable substitution
+ candidates. */
+ is_substitution_candidate = 0;
break;
case 'C':
/* A C99 complex type. */
- result_append (dm, "complex ");
+ RETURN_IF_ERROR (result_add (dm, "complex "));
advance_char (dm);
RETURN_IF_ERROR (demangle_type (dm));
break;
case 'G':
/* A C99 imaginary type. */
- result_append (dm, "imaginary ");
+ RETURN_IF_ERROR (result_add (dm, "imaginary "));
advance_char (dm);
RETURN_IF_ERROR (demangle_type (dm));
break;
case 'U':
- /* Vendor extended type qualifier. */
+ /* Vendor-extended type qualifier. */
advance_char (dm);
RETURN_IF_ERROR (demangle_source_name (dm));
- result_append_char (dm, ' ');
+ RETURN_IF_ERROR (result_add_char (dm, ' '));
RETURN_IF_ERROR (demangle_type (dm));
break;
return "Unexpected character in <type>.";
}
- /* Unqualified builin types are not substitution candidates. */
- if (!is_builtin_type)
+ if (is_substitution_candidate)
/* Add a new substitution for the type. If this type was a
<template-param>, pass its index since from the point of
- substitutions, a <template-param> token is a substitution
+ substitutions; a <template-param> token is a substitution
candidate distinct from the type that is substituted for it. */
- substitution_add (dm, start, template_p, template_parm);
+ RETURN_IF_ERROR (substitution_add (dm, start, encode_return_type));
/* Pop off template argument lists added during mangling of this
type. */
"..." /* z */
};
+/* Java source names of builtin types. Types that arn't valid in Java
+ are also included here - we don't fail if someone attempts to demangle a
+ C++ symbol in Java style. */
+static const char *const java_builtin_type_names[26] =
+{
+ "signed char", /* a */
+ "boolean", /* C++ "bool" */ /* b */
+ "byte", /* C++ "char" */ /* c */
+ "double", /* d */
+ "long double", /* e */
+ "float", /* f */
+ "__float128", /* g */
+ "unsigned char", /* h */
+ "int", /* i */
+ "unsigned", /* j */
+ NULL, /* k */
+ "long", /* l */
+ "unsigned long", /* m */
+ "__int128", /* n */
+ "unsigned __int128", /* o */
+ NULL, /* p */
+ NULL, /* q */
+ NULL, /* r */
+ "short", /* s */
+ "unsigned short", /* t */
+ NULL, /* u */
+ "void", /* v */
+ "char", /* C++ "wchar_t" */ /* w */
+ "long", /* C++ "long long" */ /* x */
+ "unsigned long long", /* y */
+ "..." /* z */
+};
+
/* Demangles and emits a <builtin-type>.
<builtin-type> ::= v # void
}
else if (code >= 'a' && code <= 'z')
{
- const char *type_name = builtin_type_names[code - 'a'];
+ const char *type_name;
+ /* Java uses different names for some built-in types. */
+ if (dm->style == DMGL_JAVA)
+ type_name = java_builtin_type_names[code - 'a'];
+ else
+ type_name = builtin_type_names[code - 'a'];
if (type_name == NULL)
return "Unrecognized <builtin-type> code.";
- result_append (dm, type_name);
+ RETURN_IF_ERROR (result_add (dm, type_name));
advance_char (dm);
return STATUS_OK;
}
switch (peek_char (dm))
{
case 'r':
- dyn_string_append_space (qualifiers);
- dyn_string_append_cstr (qualifiers, "restrict");
+ if (!dyn_string_append_space (qualifiers))
+ return STATUS_ALLOCATION_FAILED;
+ if (!dyn_string_append_cstr (qualifiers, "restrict"))
+ return STATUS_ALLOCATION_FAILED;
break;
case 'V':
- dyn_string_append_space (qualifiers);
- dyn_string_append_cstr (qualifiers, "volatile");
+ if (!dyn_string_append_space (qualifiers))
+ return STATUS_ALLOCATION_FAILED;
+ if (!dyn_string_append_cstr (qualifiers, "volatile"))
+ return STATUS_ALLOCATION_FAILED;
break;
case 'K':
- dyn_string_append_space (qualifiers);
- dyn_string_append_cstr (qualifiers, "const");
+ if (!dyn_string_append_space (qualifiers))
+ return STATUS_ALLOCATION_FAILED;
+ if (!dyn_string_append_cstr (qualifiers, "const"))
+ return STATUS_ALLOCATION_FAILED;
break;
default:
}
}
-/* Demangles and emits a <function-type> FUNCTION_NAME_POS is the
+/* Demangles and emits a <function-type>. *FUNCTION_NAME_POS is the
position in the result string of the start of the function
- identifier, at which the function's return type will be inserted.
+ identifier, at which the function's return type will be inserted;
+ *FUNCTION_NAME_POS is updated to position past the end of the
+ function's return type.
<function-type> ::= F [Y] <bare-function-type> E */
static status_t
demangle_function_type (dm, function_name_pos)
demangling_t dm;
- int function_name_pos;
+ int *function_name_pos;
{
DEMANGLE_TRACE ("function-type", dm);
RETURN_IF_ERROR (demangle_char (dm, 'F'));
{
/* Indicate this function has C linkage if in verbose mode. */
if (flag_verbose)
- result_append (dm, " [extern \"C\"] ");
+ RETURN_IF_ERROR (result_add (dm, " [extern \"C\"] "));
advance_char (dm);
}
RETURN_IF_ERROR (demangle_bare_function_type (dm, function_name_pos));
static status_t
demangle_bare_function_type (dm, return_type_pos)
demangling_t dm;
- int return_type_pos;
+ int *return_type_pos;
{
/* Sequence is the index of the current function parameter, counting
from zero. The value -1 denotes the return type. */
DEMANGLE_TRACE ("bare-function-type", dm);
- result_append_char (dm, '(');
+ RETURN_IF_ERROR (result_add_char (dm, '('));
while (!end_of_name_p (dm) && peek_char (dm) != 'E')
{
if (sequence == -1)
/* We're decoding the function's return type. */
{
dyn_string_t return_type;
+ status_t status = STATUS_OK;
/* Decode the return type off to the side. */
- result_push (dm);
+ RETURN_IF_ERROR (result_push (dm));
RETURN_IF_ERROR (demangle_type (dm));
return_type = (dyn_string_t) result_pop (dm);
- /* Add a space to the end of the type. */
- dyn_string_append_space (return_type);
+ /* Add a space to the end of the type. Insert the return
+ type where we've been asked to. */
+ if (!dyn_string_append_space (return_type))
+ status = STATUS_ALLOCATION_FAILED;
+ if (STATUS_NO_ERROR (status))
+ {
+ if (!dyn_string_insert (result_string (dm), *return_type_pos,
+ return_type))
+ status = STATUS_ALLOCATION_FAILED;
+ else
+ *return_type_pos += dyn_string_length (return_type);
+ }
- /* Insert the return type where we've been asked to. */
- dyn_string_insert (result_string (dm), return_type_pos,
- return_type);
dyn_string_delete (return_type);
+ RETURN_IF_ERROR (status);
}
else
{
the only type in a parameter list; in that case, we want
to print `foo ()' instead of `foo (void)'. */
if (peek_char (dm) == 'v')
+ /* Consume the v. */
+ advance_char (dm);
+ else
{
- /* Consume the v. */
- advance_char (dm);
- continue;
+ /* Separate parameter types by commas. */
+ if (sequence > 0)
+ RETURN_IF_ERROR (result_add (dm, ", "));
+ /* Demangle the type. */
+ RETURN_IF_ERROR (demangle_type (dm));
}
- /* Separate parameter types by commas. */
- if (sequence > 0)
- result_append (dm, ", ");
- /* Demangle the type. */
- RETURN_IF_ERROR (demangle_type (dm));
}
++sequence;
}
- result_append_char (dm, ')');
+ RETURN_IF_ERROR (result_add_char (dm, ')'));
+
+ /* We should have demangled at least one parameter type (which would
+ be void, for a function that takes no parameters), plus the
+ return type, if we were supposed to demangle that. */
+ if (sequence == -1)
+ return "Missing function return type.";
+ else if (sequence == 0)
+ return "Missing function parameter.";
return STATUS_OK;
}
-/* Demangles and emits a <class-enum-type>. *TEMPLATE_P is set to
+/* Demangles and emits a <class-enum-type>. *ENCODE_RETURN_TYPE is set to
non-zero if the type is a template-id, zero otherwise.
<class-enum-type> ::= <name> */
static status_t
-demangle_class_enum_type (dm, template_p)
+demangle_class_enum_type (dm, encode_return_type)
demangling_t dm;
- int *template_p;
+ int *encode_return_type;
{
DEMANGLE_TRACE ("class-enum-type", dm);
- RETURN_IF_ERROR (demangle_name (dm, template_p));
+ RETURN_IF_ERROR (demangle_name (dm, encode_return_type));
return STATUS_OK;
}
/* Demangles and emits an <array-type>.
- <array-type> ::= A [<dimension number>] _ <element type> */
+ If PTR_INSERT_POS is not NULL, the array type is formatted as a
+ pointer or reference to an array, except that asterisk and
+ ampersand punctuation is omitted (since it's not know at this
+ point). *PTR_INSERT_POS is set to the position in the demangled
+ name at which this punctuation should be inserted. For example,
+ `A10_i' is demangled to `int () [10]' and *PTR_INSERT_POS points
+ between the parentheses.
+
+ If PTR_INSERT_POS is NULL, the array type is assumed not to be
+ pointer- or reference-qualified. Then, for example, `A10_i' is
+ demangled simply as `int[10]'.
+
+ <array-type> ::= A [<dimension number>] _ <element type>
+ ::= A <dimension expression> _ <element type> */
static status_t
-demangle_array_type (dm)
+demangle_array_type (dm, ptr_insert_pos)
demangling_t dm;
+ int *ptr_insert_pos;
{
- dyn_string_t array_size = dyn_string_new (10);
+ status_t status = STATUS_OK;
+ dyn_string_t array_size = NULL;
+ char peek;
+
+ DEMANGLE_TRACE ("array-type", dm);
RETURN_IF_ERROR (demangle_char (dm, 'A'));
/* Demangle the array size into array_size. */
- RETURN_IF_ERROR (demangle_number_literally (dm, array_size, 10, 0));
+ peek = peek_char (dm);
+ if (peek == '_')
+ /* Array bound is omitted. This is a C99-style VLA. */
+ ;
+ else if (IS_DIGIT (peek_char (dm)))
+ {
+ /* It looks like a constant array bound. */
+ array_size = dyn_string_new (10);
+ if (array_size == NULL)
+ return STATUS_ALLOCATION_FAILED;
+ status = demangle_number_literally (dm, array_size, 10, 0);
+ }
+ else
+ {
+ /* Anything is must be an expression for a nont-constant array
+ bound. This happens if the array type occurs in a template
+ and the array bound references a template parameter. */
+ RETURN_IF_ERROR (result_push (dm));
+ RETURN_IF_ERROR (demangle_expression (dm));
+ array_size = (dyn_string_t) result_pop (dm);
+ }
+ /* array_size may have been allocated by now, so we can't use
+ RETURN_IF_ERROR until it's been deallocated. */
/* Demangle the base type of the array. */
- RETURN_IF_ERROR (demangle_char (dm, '_'));
- RETURN_IF_ERROR (demangle_type (dm));
+ if (STATUS_NO_ERROR (status))
+ status = demangle_char (dm, '_');
+ if (STATUS_NO_ERROR (status))
+ status = demangle_type (dm);
+
+ if (ptr_insert_pos != NULL)
+ {
+ /* This array is actually part of an pointer- or
+ reference-to-array type. Format appropriately, except we
+ don't know which and how much punctuation to use. */
+ if (STATUS_NO_ERROR (status))
+ status = result_add (dm, " () ");
+ /* Let the caller know where to insert the punctuation. */
+ *ptr_insert_pos = result_caret_pos (dm) - 2;
+ }
/* Emit the array dimension syntax. */
- result_append_char (dm, '[');
- result_append_string (dm, array_size);
- result_append_char (dm, ']');
- dyn_string_delete (array_size);
+ if (STATUS_NO_ERROR (status))
+ status = result_add_char (dm, '[');
+ if (STATUS_NO_ERROR (status) && array_size != NULL)
+ status = result_add_string (dm, array_size);
+ if (STATUS_NO_ERROR (status))
+ status = result_add_char (dm, ']');
+ if (array_size != NULL)
+ dyn_string_delete (array_size);
+ RETURN_IF_ERROR (status);
+
return STATUS_OK;
}
-/* Demangles and emits a <template-param>. The zero-indexed position
- in the parameter list is placed in *TEMPLATE_PARM_NUMBER.
+/* Demangles and emits a <template-param>.
<template-param> ::= T_ # first template parameter
::= T <parameter-2 number> _ */
static status_t
-demangle_template_param (dm, template_parm_number)
+demangle_template_param (dm)
demangling_t dm;
- int *template_parm_number;
{
int parm_number;
template_arg_list_t current_arg_list = current_template_arg_list (dm);
/* parm_number exceeded the number of arguments in the current
template argument list. */
return "Template parameter number out of bounds.";
- result_append_string (dm, (dyn_string_t) arg);
-
- if (peek_char (dm) == 'I')
- RETURN_IF_ERROR (demangle_template_args (dm));
+ RETURN_IF_ERROR (result_add_string (dm, (dyn_string_t) arg));
- *template_parm_number = parm_number;
return STATUS_OK;
}
demangling_t dm;
{
int first = 1;
+ dyn_string_t old_last_source_name;
template_arg_list_t arg_list = template_arg_list_new ();
+ if (arg_list == NULL)
+ return STATUS_ALLOCATION_FAILED;
+
/* Preserve the most recently demangled source name. */
- dyn_string_t old_last_source_name = dm->last_source_name;
+ old_last_source_name = dm->last_source_name;
dm->last_source_name = dyn_string_new (0);
DEMANGLE_TRACE ("template-args", dm);
+ if (dm->last_source_name == NULL)
+ return STATUS_ALLOCATION_FAILED;
+
RETURN_IF_ERROR (demangle_char (dm, 'I'));
- result_append_char (dm, '<');
+ RETURN_IF_ERROR (result_open_template_list (dm));
do
{
string_list_t arg;
if (first)
first = 0;
else
- result_append (dm, ", ");
+ RETURN_IF_ERROR (result_add (dm, ", "));
/* Capture the template arg. */
- result_push (dm);
+ RETURN_IF_ERROR (result_push (dm));
RETURN_IF_ERROR (demangle_template_arg (dm));
arg = result_pop (dm);
/* Emit it in the demangled name. */
- result_append_string (dm, (dyn_string_t) arg);
+ RETURN_IF_ERROR (result_add_string (dm, (dyn_string_t) arg));
/* Save it for use in expanding <template-param>s. */
template_arg_list_add_arg (arg_list, arg);
}
while (peek_char (dm) != 'E');
/* Append the '>'. */
- result_close_template_list (dm);
+ RETURN_IF_ERROR (result_close_template_list (dm));
/* Consume the 'E'. */
advance_char (dm);
demangle_literal (dm)
demangling_t dm;
{
- dyn_string_t value = dyn_string_new (0);
char peek = peek_char (dm);
+ dyn_string_t value_string;
+ status_t status;
DEMANGLE_TRACE ("literal", dm);
corresponding to false or true, respectively. */
value = peek_char (dm);
if (value == '0')
- result_append (dm, "false");
+ RETURN_IF_ERROR (result_add (dm, "false"));
else if (value == '1')
- result_append (dm, "true");
+ RETURN_IF_ERROR (result_add (dm, "true"));
else
return "Unrecognized bool constant.";
/* Consume the 0 or 1. */
/* Consume the type character. */
advance_char (dm);
+
/* Demangle the number and write it out. */
- RETURN_IF_ERROR (demangle_number_literally (dm, value, 10, 1));
- result_append_string (dm, value);
+ value_string = dyn_string_new (0);
+ status = demangle_number_literally (dm, value_string, 10, 1);
+ if (STATUS_NO_ERROR (status))
+ status = result_add_string (dm, value_string);
/* For long integers, append an l. */
- if (code == 'l')
- result_append_char (dm, code);
+ if (code == 'l' && STATUS_NO_ERROR (status))
+ status = result_add_char (dm, code);
+ dyn_string_delete (value_string);
+
+ RETURN_IF_ERROR (status);
return STATUS_OK;
}
/* ...else code == ' ', so fall through to represent this
literal's type explicitly using cast syntax. */
}
- result_append_char (dm, '(');
+ RETURN_IF_ERROR (result_add_char (dm, '('));
RETURN_IF_ERROR (demangle_type (dm));
- result_append_char (dm, ')');
+ RETURN_IF_ERROR (result_add_char (dm, ')'));
+
+ value_string = dyn_string_new (0);
+ if (value_string == NULL)
+ return STATUS_ALLOCATION_FAILED;
- RETURN_IF_ERROR (demangle_number_literally (dm, value, 10, 1));
- result_append_string (dm, value);
- dyn_string_delete (value);
+ status = demangle_number_literally (dm, value_string, 10, 1);
+ if (STATUS_NO_ERROR (status))
+ status = result_add_string (dm, value_string);
+ dyn_string_delete (value_string);
+ RETURN_IF_ERROR (status);
return STATUS_OK;
}
/* Expression. */
advance_char (dm);
RETURN_IF_ERROR (demangle_expression (dm));
+ RETURN_IF_ERROR (demangle_char (dm, 'E'));
break;
default:
/* An operator expression. */
{
int num_args;
+ int type_arg;
+ status_t status = STATUS_OK;
dyn_string_t operator_name;
/* We have an operator name. Since we want to output binary
operations in infix notation, capture the operator name
first. */
- result_push (dm);
- RETURN_IF_ERROR (demangle_operator_name (dm, 1, &num_args));
+ RETURN_IF_ERROR (result_push (dm));
+ RETURN_IF_ERROR (demangle_operator_name (dm, 1, &num_args,
+ &type_arg));
operator_name = (dyn_string_t) result_pop (dm);
/* If it's binary, do an operand first. */
if (num_args > 1)
{
- result_append_char (dm, '(');
- RETURN_IF_ERROR (demangle_expression (dm));
- result_append_char (dm, ')');
+ status = result_add_char (dm, '(');
+ if (STATUS_NO_ERROR (status))
+ status = demangle_expression (dm);
+ if (STATUS_NO_ERROR (status))
+ status = result_add_char (dm, ')');
}
- /* Now emit the operator, followed by its second (if binary) or
- only (if unary) operand. */
- result_append_string (dm, operator_name);
+ /* Emit the operator. */
+ if (STATUS_NO_ERROR (status))
+ status = result_add_string (dm, operator_name);
dyn_string_delete (operator_name);
- result_append_char (dm, '(');
- RETURN_IF_ERROR (demangle_expression (dm));
- result_append_char (dm, ')');
+ RETURN_IF_ERROR (status);
+
+ /* Emit its second (if binary) or only (if unary) operand. */
+ RETURN_IF_ERROR (result_add_char (dm, '('));
+ if (type_arg)
+ RETURN_IF_ERROR (demangle_type (dm));
+ else
+ RETURN_IF_ERROR (demangle_expression (dm));
+ RETURN_IF_ERROR (result_add_char (dm, ')'));
/* The ternary operator takes a third operand. */
if (num_args == 3)
{
- result_append (dm, ":(");
+ RETURN_IF_ERROR (result_add (dm, ":("));
RETURN_IF_ERROR (demangle_expression (dm));
- result_append_char (dm, ')');
+ RETURN_IF_ERROR (result_add_char (dm, ')'));
}
}
RETURN_IF_ERROR (demangle_char (dm, 's'));
RETURN_IF_ERROR (demangle_char (dm, 'r'));
RETURN_IF_ERROR (demangle_type (dm));
- result_append (dm, "::");
+ RETURN_IF_ERROR (result_add (dm, "::"));
RETURN_IF_ERROR (demangle_encoding (dm));
return STATUS_OK;
}
demangling_t dm;
{
char peek = peek_char (dm);
- int unused;
DEMANGLE_TRACE ("expr-primary", dm);
if (peek == 'T')
- RETURN_IF_ERROR (demangle_template_param (dm, &unused));
+ RETURN_IF_ERROR (demangle_template_param (dm));
else if (peek == 'L')
{
/* Consume the `L'. */
}
/* Demangles and emits a <substitution>. Sets *TEMPLATE_P to non-zero
- if the substitution is the name of a template, zero otherwise. If
- the substitution token is St, which corresponds to the `::std::'
- namespace and can appear in a non-nested name, sets
- *SPECIAL_STD_SUBSTITUTION to non-zero; zero otherwise.
+ if the substitution is the name of a template, zero otherwise.
<substitution> ::= S <seq-id> _
::= S_
::= So # ::std::basic_ostream<char,
std::char_traits<char> >
::= Sd # ::std::basic_iostream<char,
- std::char_traits<char> >
+ std::char_traits<char> >
*/
static status_t
-demangle_substitution (dm, template_p, special_std_substitution)
+demangle_substitution (dm, template_p)
demangling_t dm;
int *template_p;
- int *special_std_substitution;
{
int seq_id;
int peek;
DEMANGLE_TRACE ("substitution", dm);
RETURN_IF_ERROR (demangle_char (dm, 'S'));
- *special_std_substitution = 0;
/* Scan the substitution sequence index. A missing number denotes
the first index. */
/* If the following character is 0-9 or a capital letter, interpret
the sequence up to the next underscore as a base-36 substitution
index. */
- else if (isdigit ((unsigned char) peek)
+ else if (IS_DIGIT ((unsigned char) peek)
|| (peek >= 'A' && peek <= 'Z'))
RETURN_IF_ERROR (demangle_number (dm, &seq_id, 36, 0));
else
{
+ const char *new_last_source_name = NULL;
+
switch (peek)
{
case 't':
- result_append (dm, "std");
- *special_std_substitution = 1;
+ RETURN_IF_ERROR (result_add (dm, "std"));
break;
case 'a':
- result_append (dm, "std::allocator");
- dyn_string_copy_cstr (dm->last_source_name, "allocator");
+ RETURN_IF_ERROR (result_add (dm, "std::allocator"));
+ new_last_source_name = "allocator";
+ *template_p = 1;
break;
case 'b':
- result_append (dm, "std::basic_string");
- dyn_string_copy_cstr (dm->last_source_name, "basic_string");
+ RETURN_IF_ERROR (result_add (dm, "std::basic_string"));
+ new_last_source_name = "basic_string";
+ *template_p = 1;
break;
case 's':
if (!flag_verbose)
{
- result_append (dm, "std::string");
- dyn_string_copy_cstr (dm->last_source_name, "string");
+ RETURN_IF_ERROR (result_add (dm, "std::string"));
+ new_last_source_name = "string";
}
else
{
- result_append (dm, "std::basic_string<char, std::char_traits<char>, std::allocator<char> >");
- dyn_string_copy_cstr (dm->last_source_name, "basic_string");
+ RETURN_IF_ERROR (result_add (dm, "std::basic_string<char, std::char_traits<char>, std::allocator<char> >"));
+ new_last_source_name = "basic_string";
}
+ *template_p = 0;
break;
case 'i':
if (!flag_verbose)
{
- result_append (dm, "std::istream");
- dyn_string_copy_cstr (dm->last_source_name, "istream");
+ RETURN_IF_ERROR (result_add (dm, "std::istream"));
+ new_last_source_name = "istream";
}
else
{
- result_append (dm, "std::basic_istream<char, std::char_traints<char> >");
- dyn_string_copy_cstr (dm->last_source_name, "basic_istream");
+ RETURN_IF_ERROR (result_add (dm, "std::basic_istream<char, std::char_traints<char> >"));
+ new_last_source_name = "basic_istream";
}
+ *template_p = 0;
break;
case 'o':
if (!flag_verbose)
{
- result_append (dm, "std::ostream");
- dyn_string_copy_cstr (dm->last_source_name, "ostream");
+ RETURN_IF_ERROR (result_add (dm, "std::ostream"));
+ new_last_source_name = "ostream";
}
else
{
- result_append (dm, "std::basic_ostream<char, std::char_traits<char> >");
- dyn_string_copy_cstr (dm->last_source_name, "basic_ostream");
+ RETURN_IF_ERROR (result_add (dm, "std::basic_ostream<char, std::char_traits<char> >"));
+ new_last_source_name = "basic_ostream";
}
+ *template_p = 0;
break;
case 'd':
if (!flag_verbose)
{
- result_append (dm, "std::iostream");
- dyn_string_copy_cstr (dm->last_source_name, "iostream");
+ RETURN_IF_ERROR (result_add (dm, "std::iostream"));
+ new_last_source_name = "iostream";
}
else
{
- result_append (dm, "std::basic_iostream<char, std::char_traits<char> >");
- dyn_string_copy_cstr (dm->last_source_name, "basic_iostream");
+ RETURN_IF_ERROR (result_add (dm, "std::basic_iostream<char, std::char_traits<char> >"));
+ new_last_source_name = "basic_iostream";
}
+ *template_p = 0;
break;
default:
return "Unrecognized <substitution>.";
}
+ /* Consume the character we just processed. */
advance_char (dm);
+
+ if (new_last_source_name != NULL)
+ {
+ if (!dyn_string_copy_cstr (dm->last_source_name,
+ new_last_source_name))
+ return STATUS_ALLOCATION_FAILED;
+ }
+
return STATUS_OK;
}
substitution, `S0_' is the second-most-recent, etc., shift the
numbering by one. */
text = substitution_get (dm, seq_id + 1, template_p);
- if (text == NULL)
+ if (text == NULL)
return "Substitution number out of range.";
/* Emit the substitution text. */
- result_append_string (dm, text);
+ RETURN_IF_ERROR (result_add_string (dm, text));
RETURN_IF_ERROR (demangle_char (dm, '_'));
return STATUS_OK;
RETURN_IF_ERROR (demangle_char (dm, 'Z'));
RETURN_IF_ERROR (demangle_encoding (dm));
RETURN_IF_ERROR (demangle_char (dm, 'E'));
- result_append (dm, "'s ");
+ RETURN_IF_ERROR (result_add (dm, "::"));
if (peek_char (dm) == 's')
{
/* Local character string literal. */
- result_append (dm, "string literal");
+ RETURN_IF_ERROR (result_add (dm, "string literal"));
/* Consume the s. */
advance_char (dm);
RETURN_IF_ERROR (demangle_discriminator (dm, 0));
else
{
int unused;
- result_append (dm, "local ");
/* Local name for some other entity. Demangle its name. */
RETURN_IF_ERROR (demangle_name (dm, &unused));
RETURN_IF_ERROR (demangle_discriminator (dm, 1));
int suppress_first;
{
/* Output for <discriminator>s to the demangled name is completely
- supressed if not in verbose mode. */
+ suppressed if not in verbose mode. */
if (peek_char (dm) == '_')
{
/* Consume the underscore. */
advance_char (dm);
if (flag_verbose)
- result_append (dm, " [#");
+ RETURN_IF_ERROR (result_add (dm, " [#"));
/* Check if there's a number following the underscore. */
- if (isdigit ((unsigned char) peek_char (dm)))
+ if (IS_DIGIT ((unsigned char) peek_char (dm)))
{
int discriminator;
/* Demangle the number. */
/* Write the discriminator. The mangled number is two
less than the discriminator ordinal, counting from
zero. */
- int_to_dyn_string (discriminator + 2,
- (dyn_string_t) dm->result);
+ RETURN_IF_ERROR (int_to_dyn_string (discriminator + 1,
+ (dyn_string_t) dm->result));
}
else
- {
- if (flag_verbose)
- /* A missing digit correspond to one. */
- result_append_char (dm, '1');
- }
+ return STATUS_ERROR;
if (flag_verbose)
- result_append_char (dm, ']');
+ RETURN_IF_ERROR (result_add_char (dm, ']'));
}
else if (!suppress_first)
{
if (flag_verbose)
- result_append (dm, " [#0]");
+ RETURN_IF_ERROR (result_add (dm, " [#0]"));
}
return STATUS_OK;
an error message, and the contents of RESULT are unchanged. */
static status_t
-cp_demangle (name, result)
- char *name;
+cp_demangle (name, result, style)
+ const char *name;
dyn_string_t result;
+ int style;
{
status_t status;
int length = strlen (name);
if (length > 2 && name[0] == '_' && name[1] == 'Z')
{
- demangling_t dm = demangling_new (name);
+ demangling_t dm = demangling_new (name, style);
+ if (dm == NULL)
+ return STATUS_ALLOCATION_FAILED;
- result_push (dm);
- status = demangle_mangled_name (dm);
+ status = result_push (dm);
+ if (status != STATUS_OK)
+ {
+ demangling_delete (dm);
+ return status;
+ }
- if (status == STATUS_OK)
+ status = demangle_mangled_name (dm);
+ if (STATUS_NO_ERROR (status))
{
dyn_string_t demangled = (dyn_string_t) result_pop (dm);
- dyn_string_copy (result, demangled);
+ if (!dyn_string_copy (result, demangled))
+ return STATUS_ALLOCATION_FAILED;
dyn_string_delete (demangled);
}
/* It's evidently not a mangled C++ name. It could be the name
of something with C linkage, though, so just copy NAME into
RESULT. */
- dyn_string_copy_cstr (result, name);
+ if (!dyn_string_copy_cstr (result, name))
+ return STATUS_ALLOCATION_FAILED;
status = STATUS_OK;
}
+ return status;
+}
+
+/* Demangle TYPE_NAME into RESULT, which must be an initialized
+ dyn_string_t. On success, returns STATUS_OK. On failiure, returns
+ an error message, and the contents of RESULT are unchanged. */
+
+static status_t
+cp_demangle_type (type_name, result)
+ const char* type_name;
+ dyn_string_t result;
+{
+ status_t status;
+ demangling_t dm = demangling_new (type_name, DMGL_GNU_V3);
+
+ if (dm == NULL)
+ return STATUS_ALLOCATION_FAILED;
+
+ /* Demangle the type name. The demangled name is stored in dm. */
+ status = result_push (dm);
+ if (status != STATUS_OK)
+ {
+ demangling_delete (dm);
+ return status;
+ }
+
+ status = demangle_type (dm);
+
+ if (STATUS_NO_ERROR (status))
+ {
+ /* The demangling succeeded. Pop the result out of dm and copy
+ it into RESULT. */
+ dyn_string_t demangled = (dyn_string_t) result_pop (dm);
+ if (!dyn_string_copy (result, demangled))
+ return STATUS_ALLOCATION_FAILED;
+ dyn_string_delete (demangled);
+ }
+
+ /* Clean up. */
+ demangling_delete (dm);
+
return status;
}
+#if defined(IN_LIBGCC2) || defined(IN_GLIBCPP_V3)
+extern char *__cxa_demangle PARAMS ((const char *, char *, size_t *, int *));
+
+/* ia64 ABI-mandated entry point in the C++ runtime library for performing
+ demangling. MANGLED_NAME is a NUL-terminated character string
+ containing the name to be demangled.
+
+ OUTPUT_BUFFER is a region of memory, allocated with malloc, of
+ *LENGTH bytes, into which the demangled name is stored. If
+ OUTPUT_BUFFER is not long enough, it is expanded using realloc.
+ OUTPUT_BUFFER may instead be NULL; in that case, the demangled name
+ is placed in a region of memory allocated with malloc.
+
+ If LENGTH is non-NULL, the length of the buffer conaining the
+ demangled name, is placed in *LENGTH.
+
+ The return value is a pointer to the start of the NUL-terminated
+ demangled name, or NULL if the demangling fails. The caller is
+ responsible for deallocating this memory using free.
+
+ *STATUS is set to one of the following values:
+ 0: The demangling operation succeeded.
+ -1: A memory allocation failiure occurred.
+ -2: MANGLED_NAME is not a valid name under the C++ ABI mangling rules.
+ -3: One of the arguments is invalid.
+
+ The demagling is performed using the C++ ABI mangling rules, with
+ GNU extensions. */
+
+char *
+__cxa_demangle (mangled_name, output_buffer, length, status)
+ const char *mangled_name;
+ char *output_buffer;
+ size_t *length;
+ int *status;
+{
+ struct dyn_string demangled_name;
+ status_t result;
+
+ if (status == NULL)
+ return NULL;
+
+ if (mangled_name == NULL) {
+ *status = -3;
+ return NULL;
+ }
+
+ /* Did the caller provide a buffer for the demangled name? */
+ if (output_buffer == NULL) {
+ /* No; dyn_string will malloc a buffer for us. */
+ if (!dyn_string_init (&demangled_name, 0))
+ {
+ *status = -1;
+ return NULL;
+ }
+ }
+ else {
+ /* Yes. Check that the length was provided. */
+ if (length == NULL) {
+ *status = -3;
+ return NULL;
+ }
+ /* Install the buffer into a dyn_string. */
+ demangled_name.allocated = *length;
+ demangled_name.length = 0;
+ demangled_name.s = output_buffer;
+ }
+
+ if (mangled_name[0] == '_' && mangled_name[1] == 'Z')
+ /* MANGLED_NAME apprears to be a function or variable name.
+ Demangle it accordingly. */
+ result = cp_demangle (mangled_name, &demangled_name, 0);
+ else
+ /* Try to demangled MANGLED_NAME as the name of a type. */
+ result = cp_demangle_type (mangled_name, &demangled_name);
+
+ if (result == STATUS_OK)
+ /* The demangling succeeded. */
+ {
+ /* If LENGTH isn't NULL, store the allocated buffer length
+ there; the buffer may have been realloced by dyn_string
+ functions. */
+ if (length != NULL)
+ *length = demangled_name.allocated;
+ /* The operation was a success. */
+ *status = 0;
+ return dyn_string_buf (&demangled_name);
+ }
+ else if (result == STATUS_ALLOCATION_FAILED)
+ /* A call to malloc or realloc failed during the demangling
+ operation. */
+ {
+ *status = -1;
+ return NULL;
+ }
+ else
+ /* The demangling failed for another reason, most probably because
+ MANGLED_NAME isn't a valid mangled name. */
+ {
+ /* If the buffer containing the demangled name wasn't provided
+ by the caller, free it. */
+ if (output_buffer == NULL)
+ free (dyn_string_buf (&demangled_name));
+ *status = -2;
+ return NULL;
+ }
+}
+
+#else /* ! (IN_LIBGCC2 || IN_GLIBCPP_V3) */
+
/* Variant entry point for integration with the existing cplus-dem
demangler. Attempts to demangle MANGLED. If the demangling
succeeds, returns a buffer, allocated with malloc, containing the
If the demangling failes, returns NULL. */
char *
-cplus_demangle_new_abi (mangled)
+cplus_demangle_v3 (mangled, options)
const char* mangled;
+ int options;
{
+ dyn_string_t demangled;
+ status_t status;
+ int type = !!(options & DMGL_TYPES);
+
+ if (mangled[0] == '_' && mangled[1] == 'Z')
+ /* It is not a type. */
+ type = 0;
+ else
+ {
+ /* It is a type. Stop if we don't want to demangle types. */
+ if (!type)
+ return NULL;
+ }
+
+ flag_verbose = !!(options & DMGL_VERBOSE);
+
/* Create a dyn_string to hold the demangled name. */
- dyn_string_t demangled = dyn_string_new (0);
+ demangled = dyn_string_new (0);
/* Attempt the demangling. */
- status_t status = cp_demangle ((char *) mangled, demangled);
- if (status == STATUS_OK)
+ if (!type)
+ /* Appears to be a function or variable name. */
+ status = cp_demangle (mangled, demangled, 0);
+ else
+ /* Try to demangle it as the name of a type. */
+ status = cp_demangle_type (mangled, demangled);
+
+ if (STATUS_NO_ERROR (status))
/* Demangling succeeded. */
{
/* Grab the demangled result from the dyn_string. It was
allocated with malloc, so we can return it directly. */
char *return_value = dyn_string_release (demangled);
- /* The dyn_string can go away. */
- dyn_string_delete (demangled);
/* Hand back the demangled name. */
return return_value;
}
+ else if (status == STATUS_ALLOCATION_FAILED)
+ {
+ fprintf (stderr, "Memory allocation failed.\n");
+ abort ();
+ }
else
/* Demangling failed. */
{
}
}
+/* Demangle a Java symbol. Java uses a subset of the V3 ABI C++ mangling
+ conventions, but the output formatting is a little different.
+ This instructs the C++ demangler not to emit pointer characters ("*"), and
+ to use Java's namespace separator symbol ("." instead of "::"). It then
+ does an additional pass over the demangled output to replace instances
+ of JArray<TYPE> with TYPE[]. */
+
+char *
+java_demangle_v3 (mangled)
+ const char* mangled;
+{
+ dyn_string_t demangled;
+ char *next;
+ char *end;
+ int len;
+ status_t status;
+ int nesting = 0;
+ char *cplus_demangled;
+ char *return_value;
+
+ /* Create a dyn_string to hold the demangled name. */
+ demangled = dyn_string_new (0);
+
+ /* Attempt the demangling. */
+ status = cp_demangle ((char *) mangled, demangled, DMGL_JAVA);
+
+ if (STATUS_NO_ERROR (status))
+ /* Demangling succeeded. */
+ {
+ /* Grab the demangled result from the dyn_string. */
+ cplus_demangled = dyn_string_release (demangled);
+ }
+ else if (status == STATUS_ALLOCATION_FAILED)
+ {
+ fprintf (stderr, "Memory allocation failed.\n");
+ abort ();
+ }
+ else
+ /* Demangling failed. */
+ {
+ dyn_string_delete (demangled);
+ return NULL;
+ }
+
+ len = strlen (cplus_demangled);
+ next = cplus_demangled;
+ end = next + len;
+ demangled = NULL;
+
+ /* Replace occurances of JArray<TYPE> with TYPE[]. */
+ while (next < end)
+ {
+ char *open_str = strstr (next, "JArray<");
+ char *close_str = NULL;
+ if (nesting > 0)
+ close_str = strchr (next, '>');
+
+ if (open_str != NULL && (close_str == NULL || close_str > open_str))
+ {
+ ++nesting;
+
+ if (!demangled)
+ demangled = dyn_string_new(len);
+
+ /* Copy prepending symbols, if any. */
+ if (open_str > next)
+ {
+ open_str[0] = 0;
+ dyn_string_append_cstr (demangled, next);
+ }
+ next = open_str + 7;
+ }
+ else if (close_str != NULL)
+ {
+ --nesting;
+
+ /* Copy prepending type symbol, if any. Squash any spurious
+ whitespace. */
+ if (close_str > next && next[0] != ' ')
+ {
+ close_str[0] = 0;
+ dyn_string_append_cstr (demangled, next);
+ }
+ dyn_string_append_cstr (demangled, "[]");
+ next = close_str + 1;
+ }
+ else
+ {
+ /* There are no more arrays. Copy the rest of the symbol, or
+ simply return the original symbol if no changes were made. */
+ if (next == cplus_demangled)
+ return cplus_demangled;
+
+ dyn_string_append_cstr (demangled, next);
+ next = end;
+ }
+ }
+
+ free (cplus_demangled);
+
+ if (demangled)
+ return_value = dyn_string_release (demangled);
+ else
+ return_value = NULL;
+
+ return return_value;
+}
+
+#endif /* IN_LIBGCC2 || IN_GLIBCPP_V3 */
+
+
+#ifndef IN_GLIBCPP_V3
+/* Demangle NAME in the G++ V3 ABI demangling style, and return either
+ zero, indicating that some error occurred, or a demangling_t
+ holding the results. */
+static demangling_t
+demangle_v3_with_details (name)
+ const char *name;
+{
+ demangling_t dm;
+ status_t status;
+
+ if (strncmp (name, "_Z", 2))
+ return 0;
+
+ dm = demangling_new (name, DMGL_GNU_V3);
+ if (dm == NULL)
+ {
+ fprintf (stderr, "Memory allocation failed.\n");
+ abort ();
+ }
+
+ status = result_push (dm);
+ if (! STATUS_NO_ERROR (status))
+ {
+ demangling_delete (dm);
+ fprintf (stderr, "%s\n", status);
+ abort ();
+ }
+
+ status = demangle_mangled_name (dm);
+ if (STATUS_NO_ERROR (status))
+ return dm;
+
+ demangling_delete (dm);
+ return 0;
+}
+
+
+/* Return non-zero iff NAME is the mangled form of a constructor name
+ in the G++ V3 ABI demangling style. Specifically, return:
+ - '1' if NAME is a complete object constructor,
+ - '2' if NAME is a base object constructor, or
+ - '3' if NAME is a complete object allocating constructor. */
+enum gnu_v3_ctor_kinds
+is_gnu_v3_mangled_ctor (name)
+ const char *name;
+{
+ demangling_t dm = demangle_v3_with_details (name);
+
+ if (dm)
+ {
+ enum gnu_v3_ctor_kinds result = dm->is_constructor;
+ demangling_delete (dm);
+ return result;
+ }
+ else
+ return (enum gnu_v3_ctor_kinds) 0;
+}
+
+
+/* Return non-zero iff NAME is the mangled form of a destructor name
+ in the G++ V3 ABI demangling style. Specifically, return:
+ - '0' if NAME is a deleting destructor,
+ - '1' if NAME is a complete object destructor, or
+ - '2' if NAME is a base object destructor. */
+enum gnu_v3_dtor_kinds
+is_gnu_v3_mangled_dtor (name)
+ const char *name;
+{
+ demangling_t dm = demangle_v3_with_details (name);
+
+ if (dm)
+ {
+ enum gnu_v3_dtor_kinds result = dm->is_destructor;
+ demangling_delete (dm);
+ return result;
+ }
+ else
+ return (enum gnu_v3_dtor_kinds) 0;
+}
+#endif /* IN_GLIBCPP_V3 */
+
+
#ifdef STANDALONE_DEMANGLER
#include "getopt.h"
/* Non-zero if CHAR is a character than can occur in a mangled name. */
#define is_mangled_char(CHAR) \
- (isalnum ((unsigned char) (CHAR)) || (CHAR) == '_')
+ (IS_ALPHA (CHAR) || IS_DIGIT (CHAR) \
+ || (CHAR) == '_' || (CHAR) == '.' || (CHAR) == '$')
/* The name of this program, as invoked. */
const char* program_name;
int exit_value;
{
fprintf (fp, "Usage: %s [options] [names ...]\n", program_name);
- fprintf (fp, "Options:\n", program_name);
+ fprintf (fp, "Options:\n");
fprintf (fp, " -h,--help Display this message.\n");
fprintf (fp, " -s,--strict Demangle standard names only.\n");
fprintf (fp, " -v,--verbose Produce verbose demanglings.\n");
}
/* Option specification for getopt_long. */
-static struct option long_options[] =
+static const struct option long_options[] =
{
{ "help", no_argument, NULL, 'h' },
{ "strict", no_argument, NULL, 's' },
}
/* Attempt to demangle the name. */
- status = cp_demangle (dyn_string_buf (mangled), demangled);
+ status = cp_demangle (dyn_string_buf (mangled), demangled, 0);
/* If the demangling succeeded, great! Print out the
demangled version. */
- if (status == STATUS_OK)
+ if (STATUS_NO_ERROR (status))
fputs (dyn_string_buf (demangled), stdout);
+ /* Abort on allocation failures. */
+ else if (status == STATUS_ALLOCATION_FAILED)
+ {
+ fprintf (stderr, "Memory allocation failed.\n");
+ abort ();
+ }
/* Otherwise, it might not have been a mangled name. Just
print out the original text. */
else
for (i = optind; i < argc; ++i)
{
/* Attempt to demangle. */
- status = cp_demangle (argv[i], result);
+ status = cp_demangle (argv[i], result, 0);
/* If it worked, print the demangled name. */
- if (status == STATUS_OK)
+ if (STATUS_NO_ERROR (status))
printf ("%s\n", dyn_string_buf (result));
+ /* Abort on allocaiton failures. */
+ else if (status == STATUS_ALLOCATION_FAILED)
+ {
+ fprintf (stderr, "Memory allocation failed.\n");
+ abort ();
+ }
/* If not, print the error message to stderr instead. */
else
fprintf (stderr, "%s\n", status);