X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=libiberty%2Fcp-demangle.c;h=9adf5fd05031df5579405cfb4d8ec9bae7a1906e;hb=4e84a9475cf7e6be1993534b52cd93556c6cb33b;hp=502035f4ff24317eb4b7cc60a273e57edd49329a;hpb=4b7bc488905309a3a9fa951bc3df7961076385d4;p=pf3gnuchains%2Fgcc-fork.git diff --git a/libiberty/cp-demangle.c b/libiberty/cp-demangle.c index 502035f4ff2..9adf5fd0503 100644 --- a/libiberty/cp-demangle.c +++ b/libiberty/cp-demangle.c @@ -1,12 +1,23 @@ -/* 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 . + 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 @@ -18,29 +29,30 @@ */ /* 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 +#include #ifdef HAVE_STDLIB_H #include #endif -#if defined(CP_DEMANGLE_DEBUG) || defined(STANDALONE_DEMANGLER) #include -#endif #ifdef HAVE_STRING_H #include #endif +#include + #include "ansidecl.h" #include "libiberty.h" #include "dyn-string.h" @@ -49,13 +61,27 @@ /* 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 , 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. @@ -67,13 +93,22 @@ static int flag_verbose; 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; }; @@ -86,17 +121,10 @@ struct substitution_def /* 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 @@ -120,10 +148,10 @@ typedef struct template_arg_list_def *template_arg_list_t; 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. */ @@ -152,6 +180,18 @@ struct demangling_def /* 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; @@ -161,27 +201,46 @@ 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 @@ -207,7 +266,7 @@ static void template_arg_list_print 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)); @@ -216,15 +275,12 @@ static void demangling_delete (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) @@ -251,42 +307,61 @@ static void demangling_delete /* 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; @@ -297,14 +372,16 @@ int_to_dyn_string (value, 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; } @@ -320,23 +397,32 @@ int_to_dyn_string (value, ds) 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; } @@ -349,36 +435,50 @@ string_list_delete (node) 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 @@ -394,6 +494,51 @@ result_pop (dm) 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. */ @@ -402,63 +547,66 @@ static int 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. + non-zero, this potential substitution is a template-id. */ - If TEMPLATE_PARM_NUMBER is not NOT_TEMPLATE_PARM, the substitution - is for that particular , and is distinct from other - otherwise-identical types and other s with - different indices. */ - -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 @@ -511,13 +659,16 @@ substitutions_print (dm, fp) #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; @@ -654,24 +805,38 @@ current_template_arg_list (dm) } /* 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; } @@ -713,7 +878,7 @@ demangling_delete (dm) structure. */ static status_t demangle_char - PARAMS ((demangling_t, char)); + PARAMS ((demangling_t, int)); static status_t demangle_mangled_name PARAMS ((demangling_t)); static status_t demangle_encoding @@ -725,7 +890,7 @@ static status_t demangle_nested_name 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 @@ -735,13 +900,19 @@ static status_t demangle_number_literally 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 @@ -749,15 +920,15 @@ 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 @@ -765,23 +936,25 @@ static status_t demangle_literal static status_t demangle_template_arg PARAMS ((demangling_t)); static status_t demangle_expression - PARAMS ((demangling_t));; + PARAMS ((demangling_t)); static status_t demangle_scope_expression PARAMS ((demangling_t)); static status_t demangle_expr_primary - PARAMS ((demangling_t));; + 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. */ @@ -789,7 +962,7 @@ static status_t cp_demangle static status_t demangle_char (dm, c) demangling_t dm; - char c; + int c; { static char *error_message = NULL; @@ -801,7 +974,7 @@ demangle_char (dm, c) else { if (error_message == NULL) - error_message = strdup ("Expected ?"); + error_message = (char *) strdup ("Expected ?"); error_message[9] = c; return error_message; } @@ -826,16 +999,14 @@ demangle_mangled_name (dm) ::= ::= - ::= */ + ::= */ 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); @@ -843,44 +1014,31 @@ demangle_encoding (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 @@ -905,68 +1063,81 @@ demangle_encoding (dm) ::= */ 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 . */ - 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. - ::= St # ::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 . */ + 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 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:: is a + substitution candidate. */ + if (is_std_substitution) + RETURN_IF_ERROR (substitution_add (dm, start, 0)); + /* Demangle the here. */ + RETURN_IF_ERROR (demangle_template_args (dm)); + *encode_return_type = !suppress_return_type; } + else + *encode_return_type = 0; + break; default: /* This is an or . */ - RETURN_IF_ERROR (demangle_unqualified_name (dm)); + RETURN_IF_ERROR (demangle_unqualified_name (dm, &suppress_return_type)); /* If the is followed by template args, this is an . */ 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; } @@ -976,12 +1147,12 @@ demangle_name (dm, template_p) /* Demangles and emits a . - ::= N [] E */ + ::= N [] 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; @@ -992,17 +1163,33 @@ demangle_nested_name (dm, template_p) 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 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 ; demangle_prefix will - handle it. */ + + RETURN_IF_ERROR (demangle_prefix (dm, encode_return_type)); + /* No need to demangle the final ; demangle_prefix + will handle it. */ RETURN_IF_ERROR (demangle_char (dm, 'E')); return STATUS_OK; @@ -1010,73 +1197,89 @@ demangle_nested_name (dm, template_p) /* Demangles and emits a . - ::= + ::= ::= ::= # empty ::= ::= - ::= - - ::= - ::= */ + ::= */ 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 - , 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 , 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 ."; 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 following a + , 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 + . 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. */ @@ -1084,37 +1287,56 @@ demangle_prefix (dm, template_p) else return "Unexpected character in ."; - /* 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 . If the - 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 . If this + 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. ::= ::= ::= */ 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 ."; @@ -1144,7 +1366,7 @@ demangle_source_name (dm) 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; } @@ -1170,6 +1392,9 @@ demangle_number (dm, value, base, is_signed) 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); @@ -1203,17 +1428,21 @@ demangle_number_literally (dm, str, base, is_signed) 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; @@ -1234,12 +1463,74 @@ demangle_identifier (dm, length, identifier) 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 ."; - 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; @@ -1248,7 +1539,9 @@ demangle_identifier (dm, length, identifier) /* Demangles and emits an . 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. ::= nw # new @@ -1298,24 +1591,26 @@ demangle_identifier (dm, length, identifier) ::= cl # () ::= ix # [] ::= qu # ? - ::= sz # sizeof + ::= st # sizeof (a type) + ::= sz # sizeof (an expression) ::= cv # cast - ::= vx # vendor extended operator */ + ::= v [0-9] # 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[] = @@ -1329,9 +1624,9 @@ demangle_operator_name (dm, short_name, num_args) { "cm", "," , 2 }, { "co", "~" , 1 }, { "dV", "/=" , 2 }, - { "da", "delete[]" , 1 }, + { "da", " delete[]", 1 }, { "de", "*" , 1 }, - { "dl", "delete" , 1 }, + { "dl", " delete" , 1 }, { "dv", "/" , 2 }, { "eO", "^=" , 2 }, { "eo", "^" , 2 }, @@ -1348,11 +1643,11 @@ demangle_operator_name (dm, short_name, num_args) { "mi", "-" , 2 }, { "ml", "*" , 2 }, { "mm", "--" , 1 }, - { "na", "new[]" , 1 }, + { "na", " new[]" , 1 }, { "ne", "!=" , 2 }, { "ng", "-" , 1 }, { "nt", "!" , 1 }, - { "nw", "new" , 1 }, + { "nw", " new" , 1 }, { "oR", "|=" , 2 }, { "oo", "||" , 2 }, { "or", "|" , 2 }, @@ -1361,12 +1656,13 @@ demangle_operator_name (dm, short_name, num_args) { "pm", "->*" , 2 }, { "pp", "++" , 1 }, { "ps", "+" , 1 }, + { "pt", "->" , 2 }, { "qu", "?" , 3 }, { "rM", "%=" , 2 }, { "rS", ">>=" , 2 }, { "rm", "%" , 2 }, { "rs", ">>" , 2 }, - { "sz", "sizeof" , 1 } + { "sz", " sizeof" , 1 } }; const int num_operators = @@ -1379,10 +1675,14 @@ demangle_operator_name (dm, short_name, num_args) 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; @@ -1391,13 +1691,23 @@ demangle_operator_name (dm, short_name, num_args) /* 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) { @@ -1409,8 +1719,8 @@ demangle_operator_name (dm, short_name, num_args) /* 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; @@ -1428,128 +1738,278 @@ demangle_operator_name (dm, short_name, num_args) } } -/* Demangles and emits a . +/* Demangles and omits an . - ::= GV # Guard variable - ::= Th[n] _ - # non-virtual base override thunk - ::= Tv[n] _ - _ - # virtual base override thunk - ::= TV # virtual table - ::= TT # VTT - ::= TI # typeinfo structure - ::= TS # typeinfo name + ::= # 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; +} - Also demangles the special g++ mangling, +/* Demangles and emits a . - ::= CT _ - # construction vtable */ + ::= _ + # virtual base override, with vcall offset */ static status_t -demangle_special_name (dm) +demangle_v_offset (dm) demangling_t dm; { dyn_string_t number; - int unused; - char peek = peek_char (dm); + status_t status = STATUS_OK; - DEMANGLE_TRACE ("special-name", dm); + DEMANGLE_TRACE ("v-offset", dm); - if (peek == 'G') + /* 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) { - /* A guard variable name. 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)); + 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, ','); } - else if (peek == 'T') - { - /* Other C++ implementation miscellania. Consume the T. */ - advance_char (dm); + dyn_string_delete (number); + RETURN_IF_ERROR (status); - switch (peek_char (dm)) - { - case 'V': - /* Virtual table. */ - advance_char (dm); - result_append (dm, "vtable for "); - RETURN_IF_ERROR (demangle_type (dm)); - break; + /* Demangle the separator. */ + RETURN_IF_ERROR (demangle_char (dm, '_')); - case 'T': - /* VTT structure. */ - advance_char (dm); - result_append (dm, "VTT for "); - RETURN_IF_ERROR (demangle_type (dm)); - break; + /* Demangle the vcall offset. */ + number = dyn_string_new (4); + if (number == NULL) + return STATUS_ALLOCATION_FAILED; + demangle_number_literally (dm, number, 10, 1); - case 'I': - /* Typeinfo structure. */ - advance_char (dm); - result_append (dm, "typeinfo for "); - RETURN_IF_ERROR (demangle_type (dm)); - break; + /* 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); - case 'S': - /* Character string containing type name, used in typeinfo. */ - advance_char (dm); - result_append (dm, "typeinfo name for "); - RETURN_IF_ERROR (demangle_type (dm)); - break; + return STATUS_OK; +} - case 'h': - /* Non-virtual thunk. */ +/* Demangles and emits a . + + ::= h _ + ::= v _ */ + +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 ."; + } + + return STATUS_OK; +} + +/* Demangles and emits a . + + ::= GV # Guard variable + ::= TV # virtual table + ::= TT # VTT + ::= TI # typeinfo structure + ::= TS # typeinfo name + + Other relevant productions include thunks: + + ::= T + # base is the nominal target function of thunk + + ::= Tc + # base is the nominal target function of thunk + # first call-offset is 'this' adjustment + # second call-offset is result adjustment + + where + + ::= h _ + ::= v _ + + Also demangles the special g++ manglings, + + ::= TC _ + # construction vtable + ::= TF # typeinfo function (old ABI only) + ::= TJ # java Class structure */ + +static status_t +demangle_special_name (dm) + demangling_t dm; +{ + dyn_string_t number; + int unused; + char peek = peek_char (dm); + + DEMANGLE_TRACE ("special-name", dm); + + if (peek == 'G') + { + /* Consume the G. */ + advance_char (dm); + switch (peek_char (dm)) + { + case 'V': + /* A guard variable name. */ 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, "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 ."; + } + } + else if (peek == 'T') + { + status_t status = STATUS_OK; + + /* Other C++ implementation miscellania. Consume the T. */ + advance_char (dm); + + switch (peek_char (dm)) + { + case 'V': + /* Virtual table. */ + advance_char (dm); + RETURN_IF_ERROR (result_add (dm, "vtable for ")); + RETURN_IF_ERROR (demangle_type (dm)); + break; + + case 'T': + /* VTT structure. */ + advance_char (dm); + RETURN_IF_ERROR (result_add (dm, "VTT for ")); + RETURN_IF_ERROR (demangle_type (dm)); + break; + + case 'I': + /* Typeinfo structure. */ + advance_char (dm); + 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); + 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); + 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); + RETURN_IF_ERROR (result_add (dm, "java Class for ")); + RETURN_IF_ERROR (demangle_type (dm)); + break; + + case 'h': + /* Non-virtual thunk. */ + advance_char (dm); + 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; @@ -1557,24 +2017,47 @@ demangle_special_name (dm) /* 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. */ @@ -1595,7 +2078,6 @@ demangle_special_name (dm) ::= 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 */ @@ -1608,8 +2090,7 @@ demangle_ctor_dtor_name (dm) { "in-charge", "not-in-charge", - "in-charge allocating", - "not-in-charge allocating" + "allocating" }; static const char *const dtor_flavors[] = { @@ -1627,33 +2108,51 @@ demangle_ctor_dtor_name (dm) { /* 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 @@ -1669,6 +2168,19 @@ demangle_ctor_dtor_name (dm) 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. + ::= P ::= R ::= @@ -1676,91 +2188,154 @@ demangle_ctor_dtor_name (dm) ::= M */ 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 ::= , + 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 . @@ -1771,6 +2346,7 @@ demangle_type_ptr (dm) ::= ::= ::= + ::= ::= ::= P # pointer-to ::= R # reference-to @@ -1785,22 +2361,29 @@ demangle_type (dm) { 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 can be a ; therefore, this is a + substitution candidate unless a special condition holds (see + below). */ + int is_substitution_candidate = 1; DEMANGLE_TRACE ("type", dm); /* A can start with a digit (a ), an N (a ), or a Z (a ). */ - 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 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) @@ -1808,29 +2391,36 @@ demangle_type (dm) 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; @@ -1838,50 +2428,112 @@ demangle_type (dm) 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 or a + . 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 + ::= + ::= */ + 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 . 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 == '_') + { + 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 { - /* 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)); + /* 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. In these + cases we need to add a substitution candidate for the + entire 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 . */ + 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; @@ -1889,13 +2541,12 @@ demangle_type (dm) return "Unexpected character in ."; } - /* 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 , pass its index since from the point of - substitutions, a token is a substitution + substitutions; a 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. */ @@ -1936,6 +2587,39 @@ static const char *const builtin_type_names[26] = "..." /* 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 . ::= v # void @@ -1978,11 +2662,16 @@ demangle_builtin_type (dm) } 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 code."; - result_append (dm, type_name); + RETURN_IF_ERROR (result_add (dm, type_name)); advance_char (dm); return STATUS_OK; } @@ -2006,18 +2695,24 @@ demangle_CV_qualifiers (dm, qualifiers) 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: @@ -2028,16 +2723,18 @@ demangle_CV_qualifiers (dm, qualifiers) } } -/* Demangles and emits a FUNCTION_NAME_POS is the +/* Demangles and emits a . *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. ::= F [Y] 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')); @@ -2045,7 +2742,7 @@ demangle_function_type (dm, function_name_pos) { /* 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)); @@ -2063,7 +2760,7 @@ demangle_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. */ @@ -2072,26 +2769,35 @@ demangle_bare_function_type (dm, return_type_pos) 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 { @@ -2099,79 +2805,144 @@ demangle_bare_function_type (dm, return_type_pos) 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 . *TEMPLATE_P is set to +/* Demangles and emits a . *ENCODE_RETURN_TYPE is set to non-zero if the type is a template-id, zero otherwise. ::= */ 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 . - ::= A [] _ */ + 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]'. + + ::= A [] _ + ::= A _ */ 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 . The zero-indexed position - in the parameter list is placed in *TEMPLATE_PARM_NUMBER. +/* Demangles and emits a . ::= T_ # first template parameter ::= T _ */ 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); @@ -2199,12 +2970,8 @@ demangle_template_param (dm, template_parm_number) /* 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); + RETURN_IF_ERROR (result_add_string (dm, (dyn_string_t) arg)); - if (peek_char (dm) == 'I') - RETURN_IF_ERROR (demangle_template_args (dm)); - - *template_parm_number = parm_number; return STATUS_OK; } @@ -2217,16 +2984,23 @@ demangle_template_args (dm) 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; @@ -2234,22 +3008,22 @@ demangle_template_args (dm) 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 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); @@ -2279,8 +3053,9 @@ static status_t 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); @@ -2317,9 +3092,9 @@ demangle_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. */ @@ -2332,25 +3107,37 @@ demangle_literal (dm) /* 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; } @@ -2389,6 +3176,7 @@ demangle_template_arg (dm) /* Expression. */ advance_char (dm); RETURN_IF_ERROR (demangle_expression (dm)); + RETURN_IF_ERROR (demangle_char (dm, 'E')); break; default: @@ -2422,37 +3210,48 @@ demangle_expression (dm) /* 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, ')')); } } @@ -2471,7 +3270,7 @@ demangle_scope_expression (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; } @@ -2487,12 +3286,11 @@ demangle_expr_primary (dm) 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'. */ @@ -2513,10 +3311,7 @@ demangle_expr_primary (dm) } /* Demangles and emits a . 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. ::= S _ ::= S_ @@ -2532,14 +3327,13 @@ demangle_expr_primary (dm) ::= So # ::std::basic_ostream > ::= Sd # ::std::basic_iostream > + std::char_traits > */ 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; @@ -2548,7 +3342,6 @@ demangle_substitution (dm, template_p, special_std_substitution) 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. */ @@ -2558,85 +3351,101 @@ demangle_substitution (dm, template_p, special_std_substitution) /* 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, std::allocator >"); - dyn_string_copy_cstr (dm->last_source_name, "basic_string"); + RETURN_IF_ERROR (result_add (dm, "std::basic_string, std::allocator >")); + 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 >"); - dyn_string_copy_cstr (dm->last_source_name, "basic_istream"); + RETURN_IF_ERROR (result_add (dm, "std::basic_istream >")); + 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 >"); - dyn_string_copy_cstr (dm->last_source_name, "basic_ostream"); + RETURN_IF_ERROR (result_add (dm, "std::basic_ostream >")); + 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 >"); - dyn_string_copy_cstr (dm->last_source_name, "basic_iostream"); + RETURN_IF_ERROR (result_add (dm, "std::basic_iostream >")); + new_last_source_name = "basic_iostream"; } + *template_p = 0; break; default: return "Unrecognized ."; } + /* 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; } @@ -2644,11 +3453,11 @@ demangle_substitution (dm, template_p, special_std_substitution) 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; @@ -2668,12 +3477,12 @@ demangle_local_name (dm) 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)); @@ -2681,7 +3490,6 @@ demangle_local_name (dm) 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)); @@ -2704,16 +3512,16 @@ demangle_discriminator (dm, suppress_first) int suppress_first; { /* Output for 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. */ @@ -2722,22 +3530,18 @@ demangle_discriminator (dm, suppress_first) /* 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; @@ -2748,24 +3552,33 @@ demangle_discriminator (dm, suppress_first) 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); } @@ -2776,13 +3589,165 @@ cp_demangle (name, result) /* 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 @@ -2790,31 +3755,251 @@ cp_demangle (name, result) 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. */ + { + dyn_string_delete (demangled); + return NULL; + } +} + +/* 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 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 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 @@ -2825,7 +4010,8 @@ static void print_usage /* 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; @@ -2838,7 +4024,7 @@ print_usage (fp, exit_value) 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"); @@ -2848,7 +4034,7 @@ print_usage (fp, exit_value) } /* 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' }, @@ -2950,12 +4136,18 @@ main (argc, argv) } /* 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 @@ -2983,11 +4175,17 @@ main (argc, argv) 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);