gcc/libstdc++-v3/libsupc++/cxa/decode.c

8429 lines
322 KiB
C

/******************************************************************************
* \ ___ / *
* / \ *
* Edison Design Group C++/C Front End - | \^/ | - *
* \ / *
* / | | \ *
* Copyright 1996-2018 Edison Design Group Inc. [_] *
* *
******************************************************************************/
/*
Redistribution and use in source and binary forms are permitted
provided that the above copyright notice and this paragraph are
duplicated in all source code forms. The name of Edison Design
Group, Inc. may not be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
Any use of this software is at the user's own risk.
*/
/*
decode.c -- Name demangler for C++.
The demangling is intended to work only on names of external entities.
There is some name mangling done for internal entities, or by the
C-generating back end, that this program does not try to decode.
When IA64_ABI is defined as 1, the demangling matches the IA-64 ABI,
which in addition to its use on Itanium is used on a lot of versions of
gcc.
*/
#if COMPILE_DECODE_FOR_LIB_SRC
/*
When COMPILE_DECODE_FOR_LIB_SRC is TRUE, this file is being cross compiled for
inclusion in the runtime library (so that __cxa_demangle is available).
Compiling in this mode implies that IA64_ABI is TRUE (no externally visible
symbols are defined in Cfront mode). When cross compiling, don't include any
header files from the front end. Since we don't have access to the settings of
the front end configuration macros, make sure the ones we use have been
defined.
*/
#ifndef IA64_ABI
#define IA64_ABI 1
#else /* defined(IA64_ABI) */
#if !IA64_ABI
#error IA64_ABI macro must be TRUE when COMPILE_DECODE_FOR_LIB_SRC is TRUE
#endif /* !IA64_ABI */
#endif /* ifndef IA64_ABI */
#ifndef DEFAULT_EMULATE_GNU_ABI_BUGS
#error DEFAULT_EMULATE_GNU_ABI_BUGS macro must be set when \
COMPILE_DECODE_FOR_LIB_SRC is TRUE
#endif /* ifndef DEFAULT_EMULATE_GNU_ABI_BUGS */
#ifndef USE_LONG_DOUBLE_FOR_HOST_FP_VALUE
#error USE_LONG_DOUBLE_FOR_HOST_FP_VALUE macro must be set when \
COMPILE_DECODE_FOR_LIB_SRC is TRUE
#endif /* ifndef USE_LONG_DOUBLE_FOR_HOST_FP_VALUE */
#include "basics.h" /* Includes version in lib_src directory. */
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#ifndef sizeof_t
typedef size_t sizeof_t;
#endif /* ifndef sizeof_t */
#ifndef true_size_t
typedef size_t true_size_t;
#endif /* ifndef true_size_t */
#else /* !COMPILE_DECODE_FOR_LIB_SRC */
#include "basics.h" /* Includes version in src directory. */
#include "host_envir.h"
#if IA64_ABI
#include "targ_def.h" /* For DEFAULT_EMULATE_GNU_ABI_BUGS and others. */
#endif /* IA64_ABI */
#include "decode.h"
#endif /* COMPILE_DECODE_FOR_LIB_SRC */
/*
Block used to hold state variables. A block is used so that these routines
will be reentrant.
*/
typedef struct a_decode_control_block *a_decode_control_block_ptr;
typedef struct a_decode_control_block {
char *output_id;
/* Pointer to buffer for demangled version of
the current identifier. */
sizeof_t output_id_len;
/* Length of output_id, not counting the final
null. */
sizeof_t output_id_size;
/* Allocated size of output_id. */
a_boolean err_in_id;
/* TRUE if any error was encountered in the current
identifier. */
a_boolean output_overflow_err;
/* TRUE if the demangled output overflowed the
output buffer. */
unsigned long suppress_id_output;
/* If > 0, demangled id output is suppressed. This
might be because of an error or just as a way
of avoiding output during some processing. */
sizeof_t uncompressed_length;
/* If non-zero, the original name was compressed,
and this indicates the length of the uncompressed
(but still mangled) name. */
#if !IA64_ABI
a_const_char *end_of_name;
/* Set to the character position just after the end of
the mangled name. When sections with indicated
lengths are scanned, set temporarily to just after
that section of the name. */
unsigned long mangling_nesting_level;
/* The nesting level of calls to
full_demangle_identifier. Used to ensure that
template parameter names for recursive calls are
given different nesting_levels (and therefore
different template parameter names). */
#else /* IA64_ABI */
unsigned long suppress_substitution_recording;
/* If > 0, suppress recording of substitutions. */
a_boolean contains_conversion_operator;
/* TRUE if the name being demangled contains a
conversion operator (i.e., "cv <type>"). Such
names may require a second pass at demangling
if the first pass ends in failure. */
a_boolean parse_template_args_after_conversion_operator;
/* TRUE if template arguments should be parsed
as part of the type following a templated conversion
operator. The initial attempt at demangling uses
a value of FALSE, but a subsequent attempt will
have this field set to TRUE. */
#endif /* IA64_ABI */
} a_decode_control_block;
static void clear_control_block(a_decode_control_block_ptr dctl)
/*
Clear a decoding control block.
*/
{
dctl->output_id = NULL;
dctl->output_id_len = 0;
dctl->output_id_size = 0;
dctl->err_in_id = FALSE;
dctl->output_overflow_err = FALSE;
dctl->suppress_id_output = 0;
dctl->uncompressed_length = 0;
#if !IA64_ABI
dctl->end_of_name = NULL;
dctl->mangling_nesting_level = 0;
#else /* IA64_ABI */
dctl->suppress_substitution_recording = 0;
dctl->contains_conversion_operator = FALSE;
dctl->parse_template_args_after_conversion_operator = FALSE;
#endif /* IA64_ABI */
} /* clear_control_block */
#if !IA64_ABI
/*
Block that contains information used to control the output of template
parameter lists.
*/
typedef struct a_template_param_block *a_template_param_block_ptr;
typedef struct a_template_param_block {
unsigned long nesting_level;
/* Number of levels of template nesting at this
point (1 == top level). */
a_const_char *final_specialization;
/* Set to point to the mangled encoding for the final
specialization encountered while working from
outermost template to innermost. NULL if
no specialization has been found yet. */
a_boolean set_final_specialization;
/* TRUE if final_specialization should be set while
scanning. */
a_boolean actual_template_args_until_final_specialization;
/* TRUE if template parameter names should not be
put out. Reset when the final_specialization
position is reached. */
a_boolean output_only_correspondences;
/* TRUE if doing a post-pass to output only template
parameter/argument correspondences and not
anything else. suppress_id_output will have been
incremented to suppress everything else, and
gets decremented temporarily when correspondences
are output. */
a_boolean first_correspondence;
/* TRUE until the first template parameter/argument
correspondence is put out. */
a_boolean use_old_form_for_template_output;
/* TRUE if templates should be output in the old
form that always puts actual argument values
in template argument lists. */
} a_template_param_block;
/*
Declarations needed because of forward references:
*/
static a_const_char *demangle_identifier_with_preceding_length(
a_const_char *ptr,
a_boolean suppress_parent_and_local_info,
a_decode_control_block_ptr dctl);
static a_const_char *demangle_operation(a_const_char *ptr,
a_boolean need_parens,
a_decode_control_block_ptr dctl);
static a_const_char *demangle_operator(a_const_char *ptr,
int *mangled_length,
a_boolean *takes_type,
a_boolean *is_new_style_cast,
a_boolean *is_postfix,
a_boolean *need_adl_parens,
a_boolean *is_initializer_list,
a_boolean *ud_suffix_follows,
a_decode_control_block_ptr dctl);
static a_const_char *demangle_type(a_const_char *ptr,
a_decode_control_block_ptr dctl);
static a_const_char *full_demangle_type_name(
a_const_char *ptr,
a_boolean base_name_only,
a_template_param_block_ptr temp_par_info,
a_boolean is_destructor_name,
a_decode_control_block_ptr dctl);
static a_const_char *demangle_template_arguments(
a_const_char *ptr,
a_boolean emit_arg_values,
a_boolean suppress_angle_brackets,
a_template_param_block_ptr temp_par_info,
a_decode_control_block_ptr dctl);
static a_boolean is_mangled_type_name(a_const_char *ptr,
a_decode_control_block_ptr dctl);
static a_const_char *demangle_name(
a_const_char *ptr,
unsigned long nchars,
a_boolean stop_on_underscores,
unsigned long *nchars_left,
a_const_char *mclass,
a_template_param_block_ptr temp_par_info,
a_boolean *instance_emitted,
a_decode_control_block_ptr dctl);
static a_const_char *demangle_name_with_preceding_length(
a_const_char *ptr,
a_decode_control_block_ptr dctl);
static a_const_char *demangle_ref_qualifiers(
a_const_char *p,
a_const_char **ref_qual,
a_decode_control_block_ptr dctl);
static a_const_char *demangle_type_qualifiers(
a_const_char *ptr,
a_boolean trailing_space,
a_decode_control_block_ptr dctl);
/*
Interface to full_demangle_type_name for the simple case.
*/
#define demangle_type_name(ptr, dctl) \
full_demangle_type_name((ptr), /*base_name_only=*/FALSE, \
/*temp_par_info=*/(a_template_param_block_ptr)NULL, \
/*is_destructor_name=*/FALSE, \
(dctl))
static a_const_char *full_demangle_identifier(
a_const_char *ptr,
unsigned long nchars,
a_boolean suppress_parent_and_local_info,
a_decode_control_block_ptr dctl);
/* Interface to full_demangle_identifier for the simple case. */
#define demangle_identifier(ptr, dctl) \
full_demangle_identifier((ptr), (unsigned long)0, FALSE, (dctl))
#endif /* !IA64_ABI */
static void write_id_ch(char ch,
a_decode_control_block_ptr dctl)
/*
Add the indicated character to the demangled version of the current identifier.
*/
{
if (!dctl->suppress_id_output) {
if (!dctl->output_overflow_err) {
/* Test for buffer overflow, leaving room for a terminating null. */
if (dctl->output_id_len+1 >= dctl->output_id_size) {
/* There's no room for the character in the buffer. */
dctl->output_overflow_err = TRUE;
/* Make sure the (truncated) output is null-terminated. */
if (dctl->output_id_size != 0) {
dctl->output_id[dctl->output_id_size-1] = '\0';
} /* if */
} else {
/* No overflow; put the character in the buffer. */
dctl->output_id[dctl->output_id_len] = ch;
} /* if */
} /* if */
/* Keep track of the number of characters (even if output has overflowed
the buffer). */
dctl->output_id_len++;
} /* if */
} /* write_id_ch */
static void write_id_str(a_const_char *str,
a_decode_control_block_ptr dctl)
/*
Add the indicated string to the demangled version of the current identifier.
*/
{
a_const_char *p = str;
if (!dctl->suppress_id_output) {
for (; *p != '\0'; p++) write_id_ch(*p, dctl);
} /* if */
} /* write_id_str */
static void write_id_number(unsigned long num,
a_decode_control_block_ptr dctl)
/*
Utility to write the specified non-negative number to the demangled version
of the current identifier.
*/
{
char buffer[50];
(void)sprintf(buffer, "%lu", num);
write_id_str(buffer, dctl);
} /* write_id_number */
#if IA64_ABI
static void write_id_signed_number(long num,
a_decode_control_block_ptr dctl)
/*
Utility to write the specified signed number to the demangled version
of the current identifier.
*/
{
char buffer[50];
(void)sprintf(buffer, "%ld", num);
write_id_str(buffer, dctl);
} /* write_id_signed_number */
#endif /* IA64_ABI */
static void bad_mangled_name(a_decode_control_block_ptr dctl)
/*
A bad name mangling has been encountered. Record an error.
*/
{
if (!dctl->err_in_id) {
dctl->err_in_id = TRUE;
dctl->suppress_id_output++;
#if IA64_ABI
dctl->suppress_substitution_recording++;
#endif /* IA64_ABI */
} /* if */
} /* bad_mangled_name */
#if IA64_ABI
/*ARGSUSED*/
static a_const_char get_char(a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Get and return the character pointed to by ptr. Stub version; this
does nothing in the IA-64 ABI, but it's called from some low-level routines.
*/
{
return *ptr;
} /* get_char */
static a_boolean start_of_id_is(a_const_char *str,
a_const_char *id)
/*
Return TRUE if the part of the mangled name at id begins with the string str.
*/
{
a_boolean is_start = FALSE;
for (;;) {
char chs = *str++;
if (chs == '\0') {
is_start = TRUE;
break;
} /* if */
if (chs != *id++) break;
} /* for */
return is_start;
} /* start_of_id_is */
#else /* !IA64_ABI */
static a_const_char get_char(a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Get and return the character pointed to by ptr. However, if that
position is at or beyond dctl->end_of_name, return a null character
instead.
*/
{
char ch;
if (ptr >= dctl->end_of_name) {
ch = '\0';
} else {
ch = *ptr;
} /* if */
return ch;
} /* get_char */
static a_boolean start_of_id_is(a_const_char *str,
a_const_char *id,
a_decode_control_block_ptr dctl)
/*
Return TRUE if the part of the mangled name at id begins with the string str.
*/
{
a_boolean is_start = FALSE;
for (;;) {
char chs = *str++;
if (chs == '\0') {
is_start = TRUE;
break;
} /* if */
if (chs != get_char(id++, dctl)) break;
} /* for */
return is_start;
} /* start_of_id_is */
#endif /* IA64_ABI */
static a_const_char *advance_past(a_const_char ch,
a_const_char *p,
a_decode_control_block_ptr dctl)
/*
The character ch is expected at *p. If it's there, advance past it. If
not, call bad_mangled_name. In either case, return the updated value of p.
*/
{
if (get_char(p, dctl) == ch) {
p++;
} else {
bad_mangled_name(dctl);
} /* if */
return p;
} /* advance_past */
static a_const_char *advance_past_underscore(a_const_char *p,
a_decode_control_block_ptr dctl)
/*
An underscore is expected at *p. If it's there, advance past it. If
not, call bad_mangled_name. In either case, return the updated value of p.
*/
{
return advance_past('_', p, dctl);
} /* advance_past_underscore */
#if IA64_ABI
static a_const_char *get_number(a_const_char *p,
long *num,
a_decode_control_block_ptr dctl);
#else /* !IA64_ABI */
static a_const_char *get_number(a_const_char *p,
unsigned long *num,
a_decode_control_block_ptr dctl);
#endif /* IA64_ABI */
static a_const_char *demangle_module_id(a_const_char *ptr,
unsigned long num,
a_const_char *prefix,
a_decode_control_block_ptr dctl)
/*
Demangle a module id name (an EDG extension), which has the form
_ <file-name-length> _ <file-name> _ <str1> [ _ <str2> ]
Only the file name part is parsed and put out. num specifies the number of
characters in the entire module id. prefix points earlier in the mangled
name to a prefix that may precede the module id (if no such prefix is used,
prefix == NULL). Return a pointer to the character position following the
entire module id.
*/
{
#if IA64_ABI
long num_chars_to_output;
#else /* !IA64_ABI */
unsigned long num_chars_to_output;
#endif /* IA64_ABI */
a_const_char *start;
if (*ptr != '_' || !isdigit((unsigned char)ptr[1])) {
/* May not be an EDG module_id, in which case, emit the entire string
(including any prefix that may have been parsed by the caller). */
if (prefix != NULL) {
while (prefix != ptr) write_id_ch(*prefix++, dctl);
} /* if */
num_chars_to_output = num;
start = ptr;
} else {
start = get_number(ptr+1, &num_chars_to_output, dctl);
if (!dctl->err_in_id) {
uint32_t prefix_len = (uint32_t)((start-ptr)+1);
if (*start != '_' ||
#if IA64_ABI
num_chars_to_output <= 0 ||
#endif /* IA64_ABI */
num < ((unsigned long)num_chars_to_output + prefix_len)) {
bad_mangled_name(dctl);
} else {
/* Skip the underscore. */
start++;
} /* if */
} /* if */
} /* if */
if (!dctl->err_in_id) {
/* Write the filename (or entire module id). */
while (num_chars_to_output-- > 0) write_id_ch(*start++, dctl);
} /* if */
return ptr+num;
} /* demangle_module_id */
#if !IA64_ABI
static a_const_char *get_length(a_const_char *p,
unsigned long *num,
a_const_char **prev_end,
a_decode_control_block_ptr dctl)
/*
Accumulate a number indicating a length, starting at position p, and
return its value in *num. Return a pointer to the character position
following the number. dctl->end_of_name is updated to reflect the location
after the end of the entity with the length, and *prev_end is set to the
previous value of dctl->end_of_name for later restoration.
*/
{
unsigned long n = 0;
char ch;
*prev_end = dctl->end_of_name;
ch = get_char(p, dctl);
if (!isdigit((unsigned char)ch)) {
bad_mangled_name(dctl);
goto end_of_routine;
} /* if */
do {
n = n*10 + (ch - '0');
if (n > (unsigned long)((dctl->end_of_name - p) - 1)) {
/* Bad number (bigger than the amount of text remaining). */
bad_mangled_name(dctl);
n = (unsigned long)((dctl->end_of_name - p) - 1);
goto end_of_routine;
} /* if */
p++;
ch = get_char(p, dctl);
} while (isdigit((unsigned char)ch));
dctl->end_of_name = p + n;
end_of_routine:
*num = n;
return p;
} /* get_length */
static a_const_char *get_number(a_const_char *p,
unsigned long *num,
a_decode_control_block_ptr dctl)
/*
Accumulate a number starting at position p and return its value in *num.
Return a pointer to the character position following the number.
*/
{
unsigned long n = 0;
char ch;
ch = get_char(p, dctl);
if (!isdigit((unsigned char)ch)) {
bad_mangled_name(dctl);
goto end_of_routine;
} /* if */
do {
n = n*10 + (ch - '0');
p++;
ch = get_char(p, dctl);
} while (isdigit((unsigned char)ch));
end_of_routine:
*num = n;
return p;
} /* get_number */
static a_const_char *get_single_digit_number(a_const_char *p,
unsigned long *num,
a_decode_control_block_ptr dctl)
/*
Accumulate a number starting at position p and return its value in *num.
The number is a single digit. Return a pointer to the character position
following the number.
*/
{
char ch;
*num = 0;
ch = get_char(p, dctl);
if (!isdigit((unsigned char)ch)) {
bad_mangled_name(dctl);
goto end_of_routine;
} /* if */
*num = (ch - '0');
p++;
end_of_routine:
return p;
} /* get_single_digit_number */
static a_const_char *get_single_digit_length(
a_const_char *p,
unsigned long *num,
a_const_char **prev_end,
a_decode_control_block_ptr dctl)
/*
Accumulate a length starting at position p and return its value in *num.
The length is a single digit. Return a pointer to the character position
following the length. dctl->end_of_name is updated to reflect the location
after the end of the entity with the length, and *prev_end is set to the
previous value of dctl->end_of_name for later restoration.
*/
{
p = get_single_digit_number(p, num, dctl);
*prev_end = dctl->end_of_name;
if (*num > (unsigned long)(dctl->end_of_name - p)) {
/* Bad length (too large). */
bad_mangled_name(dctl);
} else {
dctl->end_of_name = p + *num;
} /* if */
return p;
} /* get_single_digit_length */
static a_const_char *get_length_with_optional_underscore(
a_const_char *p,
unsigned long *num,
a_const_char **prev_end,
a_decode_control_block_ptr dctl)
/*
Accumulate a number starting at position p and return its value in *num.
If the number has more than one digit, it is followed by an underscore.
(Or, in a newer representation, surrounded by underscores.)
Return a pointer to the character position following the number.
dctl->end_of_name is updated to reflect the location after the end
of the entity with the length, and *prev_end is set to the previous value
of dctl->end_of_name for later restoration.
*/
{
if (get_char(p, dctl) == '_') {
/* New encoding (not from cfront) -- the length is surrounded by
underscores whether it's a single digit or several digits,
e.g., "L_10_1234567890". */
p++;
/* Multi-digit number followed by underscore. */
p = get_length(p, num, prev_end, dctl);
p = advance_past_underscore(p, dctl);
dctl->end_of_name++; /* Adjust for underscore. */
} else if (isdigit((unsigned char)get_char(p, dctl)) &&
isdigit((unsigned char)get_char(p+1, dctl)) &&
get_char(p+2, dctl) == '_') {
/* The cfront version -- a multi-digit length is followed by an
underscore, e.g., "L10_1234567890". This doesn't work well because
something like "L11", intended to have a one-digit length, can
be made ambiguous by following it by a "_" for some other reason.
So this form is not used in new cases where that can come up, e.g.,
nontype template arguments for functions. In any case, interpret
"multi-digit" as "2-digit" and don't look further for the underscore. */
/* Multi-digit number followed by underscore. */
p = get_length(p, num, prev_end, dctl);
p = advance_past_underscore(p, dctl);
dctl->end_of_name++; /* Adjust for underscore. */
} else {
/* Single-digit number not followed by underscore. */
p = get_single_digit_length(p, num, prev_end, dctl);
} /* if */
return p;
} /* get_length_with_optional_underscore */
static a_const_char *get_number_with_optional_underscore(
a_const_char *p,
unsigned long *num,
a_decode_control_block_ptr dctl)
/*
Accumulate a number starting at position p and return its value in *num.
If the number has more than one digit, it is followed by an underscore.
(Or, in a newer representation, surrounded by underscores.)
Return a pointer to the character position following the number.
Parses the same string as get_length_with_optional_underscore, except that
dctl->end_of_name is not altered (meaning that this routine can be used to
retrieve a count of something other than the number of characters that
immediately follow the number).
*/
{
if (get_char(p, dctl) == '_') {
/* New encoding (not from cfront) -- the number is surrounded by
underscores whether it's a single digit or several digits,
e.g., "L_10_". */
p++;
/* Multi-digit number followed by underscore. */
p = get_number(p, num, dctl);
p = advance_past_underscore(p, dctl);
} else if (isdigit((unsigned char)get_char(p, dctl)) &&
isdigit((unsigned char)get_char(p+1, dctl)) &&
get_char(p+2, dctl) == '_') {
/* The cfront version -- a multi-digit number is followed by an
underscore, e.g., "L10_". This doesn't work well because
something like "L11", intended to have a one-digit length, can
be made ambiguous by following it by a "_" for some other reason.
So this form is not used in new cases where that can come up, e.g.,
nontype template arguments for functions. In any case, interpret
"multi-digit" as "2-digit" and don't look further for the underscore. */
/* Multi-digit number followed by underscore. */
p = get_number(p, num, dctl);
p = advance_past_underscore(p, dctl);
} else {
/* Single-digit number not followed by underscore. */
p = get_single_digit_number(p, num, dctl);
} /* if */
return p;
} /* get_number_with_optional_underscore */
static a_boolean is_immediate_type_qualifier(a_const_char *p,
a_decode_control_block_ptr dctl)
/*
Return TRUE if the encoding pointed to is one that indicates type
qualification.
*/
{
a_boolean is_type_qual = FALSE;
char ch;
ch = get_char(p, dctl);
if (ch == 'C' || ch == 'V' || (ch == 'D' && get_char(p+1, dctl) == 'r')) {
/* This is a type qualifier. */
is_type_qual = TRUE;
} /* if */
return is_type_qual;
} /* is_immediate_type_qualifier */
static a_const_char *remove_immediate_type_qualifiers(
a_const_char *p,
a_decode_control_block_ptr dctl)
/*
Return a pointer to the mangled name after removing any type qualifiers
that might be present.
*/
{
while (is_immediate_type_qualifier(p, dctl)) {
if (get_char(p, dctl) == 'D' && get_char(p+1, dctl) == 'r') {
/* Two-character qualifier. */
p+=2;
} else {
/* One-character qualifier. */
p++;
} /* if */
} /* while */
return p;
} /* remove_immediate_type_qualifiers */
static void write_template_parameter_name(unsigned long depth,
unsigned long position,
a_boolean nontype,
a_decode_control_block_ptr dctl)
/*
Output a representation of a template parameter with depth and position
as indicated. It's a nontype parameter if nontype is TRUE.
*/
{
char buffer[100];
char letter = '\0';
if (nontype) {
/* Nontype parameter. */
/* Use a code letter for the first few levels, then the depth number. */
if (depth == 1) {
letter = 'N';
} else if (depth == 2) {
letter = 'O';
} else if (depth == 3) {
letter = 'P';
} /* if */
if (letter != '\0') {
(void)sprintf(buffer, "%c%lu", letter, position);
} else {
(void)sprintf(buffer, "N_%lu_%lu", depth, position);
} /* if */
} else {
/* Normal type parameter. */
/* Use a code letter for the first few levels, then the depth number. */
if (depth == 1) {
letter = 'T';
} else if (depth == 2) {
letter = 'U';
} else if (depth == 3) {
letter = 'V';
} /* if */
if (letter != '\0') {
(void)sprintf(buffer, "%c%lu", letter, position);
} else {
(void)sprintf(buffer, "T_%lu_%lu", depth, position);
} /* if */
} /* if */
write_id_str(buffer, dctl);
} /* write_template_parameter_name */
static a_const_char *demangle_template_parameter_name(
a_const_char *ptr,
a_boolean nontype,
a_decode_control_block_ptr dctl)
/*
Demangle a template parameter name at the indicated location. The parameter
is a nontype parameter if nontype is TRUE. Return a pointer to the character
position following what was demangled.
*/
{
a_const_char *p = ptr;
unsigned long position, depth = 1;
/* This comes up with the modern mangling for template functions.
Form is "ZnZ" or "Zn_mZ", where n is the parameter number and m
is the depth number (1 if not specified). */
p++; /* Advance past the "Z". */
/* Get the position number. */
p = get_number(p, &position, dctl);
if (get_char(p, dctl) == '_' && get_char(p+1, dctl) != '_') {
/* Form including depth ("Zn_mZ"). */
p++;
p = get_number(p, &depth, dctl);
} /* if */
/* Output the template parameter name. */
write_template_parameter_name(depth, position, nontype, dctl);
if (get_char(p , dctl) == '_' &&
get_char(p+1, dctl) == '_' &&
get_char(p+2, dctl) == 't' &&
get_char(p+3, dctl) == 'm' &&
get_char(p+4, dctl) == '_' &&
get_char(p+5, dctl) == '_') {
/* A template template parameter followed by a template
argument list. */
p = demangle_template_arguments(p+6, /*emit_arg_values=*/FALSE,
/*suppress_angle_brackets=*/FALSE,
(a_template_param_block_ptr)NULL, dctl);
} /* if */
/* Check for the final "Z". This appears in the mangling to avoid
ambiguities when the template parameter is followed by something whose
encoding begins with a digit, e.g., a class name. */
if (get_char(p, dctl) != 'Z') {
bad_mangled_name(dctl);
} else {
p++;
} /* if */
return p;
} /* demangle_template_parameter_name */
static a_const_char *demangle_constant_value(
a_const_char *ptr,
a_boolean is_bool,
a_boolean is_nullptr,
a_decode_control_block_ptr dctl)
/*
Demangle a constant value that is part of a literal. The form of the
constant has an initial length (which may or may not use the new underscore
for specifying the length), followed by some number of characters, for example:
3n12 encoding for -12
^^^---- Characters of constant. Some characters get remapped:
n --> -
p --> +
d --> .
^------- Length of constant (may or may not include underscores).
When is_bool is TRUE, emit "true"/"false" instead of 1/0. Likewise when
is_nullptr is TRUE (emits "nullptr" rather than 0).
*/
{
a_const_char *p = ptr, *prev_end;
char ch;
unsigned long nchars;
a_boolean is_nonzero = FALSE;
/* Get the length of the constant. */
p = get_length_with_optional_underscore(p, &nchars, &prev_end, dctl);
/* Process the characters of the literal constant. */
for (; nchars > 0; nchars--, p++) {
/* Remap characters where necessary. */
ch = get_char(p, dctl);
switch (ch) {
case '\0':
case '_':
/* Ran off end of string. */
bad_mangled_name(dctl);
goto end_of_routine;
case 'p':
ch = '+';
break;
case 'n':
ch = '-';
break;
case 'd':
ch = '.';
break;
} /* switch */
if (is_bool) {
/* For the bool case, just keep track of whether the constant is
non-zero; true or false will be output later. */
if (ch != '0') is_nonzero = TRUE;
} else if (is_nullptr) {
/* Constant should only ever be zero. Suppress it. */
} else {
/* Normal (non-bool, non-nullptr) case. Output the character of the
constant. */
write_id_ch(ch, dctl);
} /* if */
} /* for */
dctl->end_of_name = prev_end;
if (is_bool) {
/* For bool, output true or false. */
write_id_str((char *)(is_nonzero ? "true" : "false"), dctl);
} /* if */
if (is_nullptr) write_id_str("nullptr", dctl);
end_of_routine:
return p;
} /* demangle_constant_value */
static a_const_char *demangle_constant(
a_const_char *ptr,
a_boolean suppress_address_of,
a_boolean need_parens,
a_decode_control_block_ptr dctl)
/*
Demangle a constant (e.g., a nontype template class argument) beginning at
ptr, and output the demangled form. When suppress_address_of is TRUE, the
ampersand that is normally emitted before an address constant is suppressed
(this is used when demangling expressions where any "address of" operation is
explicit). When need_parens is TRUE, parentheses are emitted around literals
and expressions (but not addresses or template parameters). Return a pointer
to the character position following what was demangled.
*/
{
a_const_char *p = ptr, *type = NULL, *index, *prev_end, *quals;
unsigned long nchars;
char ch;
/* A constant has a form like
CiL15 <-- integer constant 5
^-- Literal constant representation.
^--- Length of literal constant.
^---- L indicates literal constant; c indicates address
of variable, etc.
^^----- Type of template argument, with "const" added.
A template parameter constant or a constant expression does not have
the initial "C" and type.
*/
if (get_char(p, dctl) == 'C') {
/* Advance past the type. */
type = p;
dctl->suppress_id_output++;
p = demangle_type(p, dctl);
dctl->suppress_id_output--;
} /* if */
/* The next thing has one of the following forms:
3abc Address of "abc".
L211 Literal constant; length ("2") followed by the characters of
the constant ("11").
LM0_L2n1_1j Pointer-to-member-function constant; the three parts
correspond to the triplet of values in the __mptr
data structure.
Z1Z Template parameter.
Opl2Z1ZZ2ZO Expression.
*/
ch = get_char(p, dctl);
if (isdigit((unsigned char)ch)) {
/* A name preceded by its length, e.g., "3abc". Put out "&name". */
if (!suppress_address_of) write_id_ch('&', dctl);
/* Process the length and name. */
p = demangle_identifier_with_preceding_length(
p,
/*suppress_parent_and_local_info=*/FALSE,
dctl);
} else if (ch == 'L') {
/* Emit parentheses around the literal if requested. */
if (need_parens) write_id_ch('(', dctl);
if (type == NULL) {
bad_mangled_name(dctl);
} else if (get_char(p+1, dctl) == 'M') {
/* Pointer-to-member-function. The form of the constant is
LM0_L2n1_1j Non-virtual function
LM0_L11_0 Virtual function
LM0_L10_0 Null pointer
The three parts match the three components of the __mptr structure:
(delta, index, function or offset). The index is -1 for a non-virtual
function, 0 for a null pointer, and greater than 0 for a virtual
function. The index is represented like an integer constant (see
above). For virtual functions, the last component is always "0"
even if the offset is not zero. */
/* Advance past the "LM". */
p += 2;
/* Advance over the first component, ignoring it. */
while (isdigit((unsigned char)get_char(p, dctl))) p++;
p = advance_past_underscore(p, dctl);
/* The index component should be next. */
if (get_char(p, dctl) != 'L') {
bad_mangled_name(dctl);
goto end_of_routine;
} /* if */
p++;
/* Get the index length. */
/* Note that get_length_with_optional_underscore is not used because
this is an ambiguous situation: an underscore follows the index
value, and there's no way to tell if it's the multi-digit
indicator for the length or the separator between fields. */
if (get_char(p, dctl) == '_') {
/* New-form encoding, no ambiguity. */
p = get_length_with_optional_underscore(p, &nchars, &prev_end, dctl);
} else {
p = get_single_digit_length(p, &nchars, &prev_end, dctl);
} /* if */
/* Remember the start of the index. */
index = p;
/* Skip the rest of the index. */
while (isdigit((unsigned char)get_char(p, dctl)) ||
(get_char(p, dctl) == 'n')) p++;
dctl->end_of_name = prev_end;
p = advance_past_underscore(p, dctl);
/* If the index number starts with 'n', this is a non-virtual
function. */
if (*index == 'n') {
/* Non-virtual function. */
/* The third component is a name preceded by its length, e.g.,
"1f". Put out "&A::f", where "A" is the class type retrieved
from the type. */
write_id_ch('&', dctl);
/* Start at type+2 to skip the "C" for const and the "M" for
pointer-to-member. */
quals = demangle_type_name(type+2, dctl);
write_id_str("::", dctl);
/* Demangle the length and name. */
p = demangle_identifier_with_preceding_length(
p,
/*suppress_parent_and_local_info=*/TRUE,
dctl);
if (is_immediate_type_qualifier(quals, dctl)) {
/* Get any optional cv-qualifiers. */
write_id_ch(' ', dctl);
quals = demangle_type_qualifiers(quals, /*trailing_space=*/FALSE,
dctl);
} /* if */
if (get_char(quals, dctl) == 'F') {
/* See if there is a ref-qualifier. */
a_const_char *ref_qual;
(void)demangle_ref_qualifiers(quals+1, &ref_qual, dctl);
if (ref_qual != NULL) {
write_id_str(ref_qual, dctl);
} /* if */
} /* if */
} else {
/* Not a non-virtual function. The encoding for the third component
should be simply "0". */
if (get_char(p, dctl) != '0') {
bad_mangled_name(dctl);
goto end_of_routine;
} /* if */
p++;
if (nchars == 1 && *index == '0') {
/* Null pointer constant. Output "(type)0", that is, a zero cast
to the pointer-to-member type. */
write_id_ch('(', dctl);
(void)demangle_type(type, dctl);
write_id_str(")0", dctl);
} else {
/* Virtual function. This case can't really be demangled properly,
because the mangled name doesn't have enough information.
Output "&A::virtual-function-n". */
write_id_ch('&', dctl);
/* Start at type+2 to skip the "C" for const and the "M" for
pointer-to-member. */
(void)demangle_type_name(type+2, dctl);
write_id_str("::", dctl);
write_id_str("virtual-function-", dctl);
/* Write the index number. */
for (; nchars > 0; nchars--, index++) write_id_ch(*index, dctl);
} /* if */
} /* if */
} else if (get_char(p+1, dctl) == 'S') {
/* String literal constant. */
p+=2;
if (type == NULL) {
bad_mangled_name(dctl);
} else {
/* The type is the type of the string. Emit "..." cast to the
proper type. */
write_id_ch('(', dctl);
(void)demangle_type(type+1, dctl);
write_id_str(")\"...\"", dctl);
} /* if */
} else {
/* Normal literal constant. Form is something like
L3n12 encoding for -12
^^^---- Characters of constant. Some characters get remapped:
n --> -
p --> +
d --> .
^------- Length of constant.
Output is
(type)constant
That is, the literal constant preceded by a cast to the right type.
*/
/* See if the type is bool. */
a_boolean is_bool = (type+2 == p && *(type+1) == 'b');
a_boolean is_managed_nullptr = (type+2 == p && *(type+1) == 'j');
a_boolean is_nullptr = (type+2 == p && *(type+1) == 'n') ||
is_managed_nullptr;
a_boolean is_complex = (type+3 == p && *(type+1) == 'x');
/* If the type is bool or nullptr, don't put out the cast. */
if (!(is_bool || is_nullptr)) {
write_id_ch('(', dctl);
/* Start at type+1 to avoid the "C" for const. */
(void)demangle_type(type+1, dctl);
write_id_ch(')', dctl);
} /* if */
if (is_complex) write_id_ch('(', dctl);
p++; /* Advance past the "L". */
if (is_managed_nullptr) {
/* If this is a managed C++/CLI __nullptr, emit the underscores to
distinguish it from the standard nullptr. */
write_id_str("__", dctl);
} /* if */
p = demangle_constant_value(p, is_bool, is_nullptr, dctl);
if (!dctl->err_in_id && is_complex) {
/* Now emit the imaginary portion of the complex number. */
write_id_ch('+', dctl);
p = demangle_constant_value(p, /*is_bool=*/FALSE, /*is_nullptr=*/FALSE,
dctl);
write_id_str("i)", dctl);
} /* if */
} /* if */
if (need_parens) write_id_ch(')', dctl);
} else if (ch == 'Z') {
/* A template parameter. */
p = demangle_template_parameter_name(p, /*nontype=*/TRUE, dctl);
} else if (ch == 'O') {
/* An operation. */
p = demangle_operation(p, need_parens, dctl);
} else {
/* The constant starts with something unexpected. */
bad_mangled_name(dctl);
} /* if */
end_of_routine:
return p;
} /* demangle_constant */
static a_const_char *demangle_parameter_reference(
a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Demangle a function parameter reference (e.g., in a late specified return
type) as pointed to by ptr:
v-vv----- These are optional.
IC1_2I <-- "const param#1 two levels up"
^---- Terminating non-digit character so parameter number won't run
into an entity with an initial length.
^^----- Number of "levels up" for this parameter (0-based). Omitted
if zero.
^------- Parameter number (1-based) or 0 for "this".
^-------- Optional cv-qualifiers.
^--------- "I" indicates parameter reference.
*/
{
a_const_char *p = ptr;
unsigned long num, level = 0;
char buffer[60];
/* Advance past the initial "I" (verified by caller). */
p++;
if (is_immediate_type_qualifier(p, dctl)) {
/* Get any optional cv-qualifiers. */
p = demangle_type_qualifiers(p, /*trailing_space=*/TRUE, dctl);
} /* if */
p = get_number(p, &num, dctl);
if (!dctl->err_in_id) {
if (get_char(p, dctl) != 'I') {
p = advance_past_underscore(p, dctl);
if (!dctl->err_in_id) {
p = get_number(p, &level, dctl);
} /* if */
} /* if */
} /* if */
if (!dctl->err_in_id) {
if (num == 0) {
/* An explicit "this" in a trailing return type. */
write_id_str("this", dctl);
} else {
if (level == 0) {
(void)sprintf(buffer, "param#%ld", num);
} else {
(void)sprintf(buffer, "param#%ld[up %ld level%s]", num, level,
level > 1 ? "s" : "");
} /* if */
write_id_str(buffer, dctl);
} /* if */
p = advance_past('I', p, dctl);
} /* if */
return p;
} /* demangle_parameter_reference */
static a_const_char *demangle_expression(
a_const_char *ptr,
a_boolean need_parens,
a_decode_control_block_ptr dctl)
/*
Demangle an expression; ensure that the expression is enclosed in
parentheses when necessary if need_parens is TRUE (names aren't parenthesized
even when need_parens is TRUE).
*/
{
a_const_char *p = ptr;
if (get_char(p, dctl) == 'I') {
/* A function parameter reference. */
p = demangle_parameter_reference(p, dctl);
} else if (get_char(p, dctl) == '_' && get_char(p+1, dctl) == '_') {
/* Certain special names can occur here, for example, an operator name
that appears as the first operand of a call. */
p = demangle_name(p, (unsigned long)0, /*stop_on_underscores=*/TRUE,
(unsigned long *)NULL, (char *)NULL,
(a_template_param_block_ptr)NULL, (a_boolean *)NULL,
dctl);
if (get_char(p, dctl) == '_' && get_char(p+1, dctl) == '_') {
p += 2;
} else {
bad_mangled_name(dctl);
} /* if */
} else {
/* Used to demangle literals as well as template parameters, operations.
Within an expression, suppress implicit "&"s during the demangling. */
p = demangle_constant(p, /*suppress_address_of=*/TRUE, need_parens, dctl);
} /* if */
return p;
} /* demangle_expression */
static a_const_char *demangle_operation(a_const_char *ptr,
a_boolean need_parens,
a_decode_control_block_ptr dctl)
/*
Demangle an operation in a constant expression (these come up in template
arguments and array sizes, in template function parameter lists) beginning
at ptr, and output the demangled form. When need_parens is TRUE, parentheses
are emitted around the operation. Return a pointer to the character
position following what was demangled.
*/
{
a_const_char *p = ptr, *operator_str, *close_str = "";
int op_length;
unsigned long num_operands, i, num_dimensions;
a_boolean takes_type, is_new_style_cast, is_postfix, need_adl_parens;
a_boolean has_variable_number_of_operands = FALSE, is_initializer_list;
a_boolean is_call = FALSE, is_cli_subscript = FALSE;
a_boolean ud_suffix_follows;
/* An operation has the form
Opl2Z1ZZ2ZO <-- "Z1 + Z2", Z1/Z2 indicating nontype template parameters.
^---- "O" to end the operation encoding.
^^^----- Second operand.
^^^-------- First operand.
^----------- Count of operands (which may or may not have an
initial "_" indicating possibly more than 9 operands).
^^------------ Operation, using same encoding as for operator
function names.
^-------------- "O" for operation.
*/
p++; /* Advance past the "O". */
/* Decode the operator name, e.g., "pl" is "+". */
operator_str = demangle_operator(p, &op_length, &takes_type,
&is_new_style_cast, &is_postfix,
&need_adl_parens, &is_initializer_list,
&ud_suffix_follows, dctl);
if (operator_str == NULL) {
bad_mangled_name(dctl);
} else {
/* Put parentheses around the operation if necessary. */
if (need_parens) write_id_ch('(', dctl);
if (*operator_str == 'f' && strcmp(operator_str, "fold-ex") == 0) {
/* A fold expression; handle it here (since it's significantly
different than a standard operation). */
a_boolean unary, left;
p++;
switch (get_char(p, dctl)) {
case 'l': unary = TRUE; left = TRUE; break;
case 'L': unary = FALSE; left = TRUE; break;
case 'r': unary = TRUE; left = FALSE; break;
case 'R': unary = FALSE; left = FALSE; break;
default:
bad_mangled_name(dctl);
break;
} /* switch */
p++;
operator_str = demangle_operator(p, &op_length, &takes_type,
&is_new_style_cast, &is_postfix,
&need_adl_parens, &is_initializer_list,
&ud_suffix_follows, dctl);
if (operator_str == NULL) {
bad_mangled_name(dctl);
} else {
p += op_length;
write_id_ch('(', dctl);
if (unary) {
if (left) {
write_id_str("...", dctl);
write_id_str(operator_str, dctl);
p = demangle_expression(p, /*need_parens=*/FALSE, dctl);
} else {
p = demangle_expression(p, /*need_parens=*/FALSE, dctl);
write_id_str(operator_str, dctl);
write_id_str("...", dctl);
} /* if */
} else {
p = demangle_expression(p, /*need_parens=*/FALSE, dctl);
write_id_str(operator_str, dctl);
write_id_str("...", dctl);
write_id_str(operator_str, dctl);
p = demangle_expression(p, /*need_parens=*/FALSE, dctl);
} /* if */
write_id_ch(')', dctl);
} /* if */
goto skip_operand_loop;
} /* if */
p += op_length;
if (is_initializer_list) {
/* An initializer list (with an optional type). */
if (takes_type) {
p = demangle_type(p, dctl);
} /* if */
write_id_str(operator_str, dctl);
has_variable_number_of_operands = TRUE;
close_str = "}";
} else if (takes_type) {
/* For casts, sizeof, __alignof__, __uuidof__, typeid, new, or sizeof...
get the type. */
if (strcmp(operator_str, "cast") == 0) {
a_const_char *num_args_ptr;
/* A "cast" can have zero or more operands (aside from the type).
For casts with exactly one operand, emit "(type)arg", but for
other cases, emit the functional-notation type conversion syntax:
"type(args)". Look ahead at the number of arguments to determine
which case we have. */
dctl->suppress_id_output++;
num_args_ptr = demangle_type(p, dctl);
dctl->suppress_id_output--;
(void)get_number_with_optional_underscore(num_args_ptr,
&num_operands, dctl);
if (!dctl->err_in_id) {
operator_str = "";
if (num_operands == 1) {
/* Output as "(type)arg". */
write_id_ch('(', dctl);
p = demangle_type(p, dctl);
write_id_ch(')', dctl);
} else {
/* Output as "type(args)". */
p = demangle_type(p, dctl);
write_id_ch('(', dctl);
has_variable_number_of_operands = TRUE;
close_str = ")";
} /* if */
} /* if */
} else if (strcmp(operator_str, "sizeof(") == 0 ||
strcmp(operator_str, "__alignof__(") == 0 ||
strcmp(operator_str, "__uuidof(") == 0 ||
strcmp(operator_str, "typeid(") == 0 ||
strcmp(operator_str, "sizeof...(") == 0) {
/* These manglings have three forms, dependent on the next character
in the mangled name. They're sufficiently different that they
are handled (mostly separately) here. */
write_id_str(operator_str, dctl);
operator_str = "";
if (get_char(p, dctl) == 'e') {
/* An "old style expression" where the expression was not
encoded (and the operand count is zero). Just note that there
was an expression and we're done. */
write_id_str("expr)", dctl);
p++;
} else if (get_char(p, dctl) == 'X') {
/* A "new style expression" where the expression is
included in the mangled name and will be demangled below. */
close_str = ")";
p++;
} else {
/* The "type" case; simply decode the type (the mangled
encoding specifies zero operands -- which are ignored below). */
p = demangle_type(p, dctl);
write_id_ch(')', dctl);
} /* if */
} else if (strcmp(operator_str, "::typeid") == 0) {
/* C++/CLI T::typeid. */
p = demangle_type(p, dctl);
write_id_str(operator_str, dctl);
} else {
/* Generic processing of items that take a type (e.g., static_cast). */
write_id_str(operator_str, dctl);
p = demangle_type(p, dctl);
if (is_new_style_cast) {
/* Something like static_cast<type>(expression). The operator and
type have been emitted, close the type with a right angle
bracket and parse the expression below. */
operator_str = "";
write_id_str(">(", dctl);
close_str = ")";
} else {
write_id_ch(')', dctl);
} /* if */
} /* if */
} else if (strcmp(operator_str, "builtin-operation") == 0) {
unsigned long kind;
/* A builtin operation. */
has_variable_number_of_operands = TRUE;
write_id_str("builtin-operation-", dctl);
/* Extract the operation number following the "bi". */
p = advance_past_underscore(p, dctl);
p = get_number(p, &kind, dctl);
if (kind > 99) {
bad_mangled_name(dctl);
} else {
write_id_number(kind, dctl);
} /* if */
p = advance_past_underscore(p, dctl);
write_id_ch('(', dctl);
close_str = ")";
} else if (strcmp(operator_str, "__real(") == 0 ||
strcmp(operator_str, "__imag(") == 0 ||
strcmp(operator_str, "noexcept(") == 0) {
/* These need a closing paren after their operand. */
close_str = ")";
} else if (strcmp(operator_str, "()") == 0) {
/* A call operation. The first operand is the target of the call,
the rest are arguments. */
operator_str = "";
is_call = TRUE;
has_variable_number_of_operands = TRUE;
} else if (strcmp(operator_str, "new") == 0 ||
strcmp(operator_str, "new[]") == 0) {
/* new has an optional "g" (indicating that ::new was used), followed by
an optional initial list of placement expressions, followed
by a type and then another optional list of initializer
expressions. Handle the first expression list and the type here,
then let the generic loop below handle the initializer list. */
/* new may have an optional "g" indicating a global scope new. */
if (get_char(p, dctl) == 'g') {
p++;
write_id_str("::", dctl);
} /* if */
write_id_str(operator_str, dctl);
write_id_ch(' ', dctl);
operator_str = "";
has_variable_number_of_operands = TRUE;
/* Get the count of operands. */
p = get_number_with_optional_underscore(p, &num_operands, dctl);
if (num_operands != 0) {
write_id_ch('(', dctl);
for (i = 1; i <= num_operands; i++) {
p = demangle_expression(p, /*need_parens=*/FALSE, dctl);
if (i != num_operands) write_id_str(", ", dctl);
} /* for */
write_id_str(") ", dctl);
} /* if */
p = demangle_type(p, dctl);
handle_new_operands:
if (get_char(p, dctl) == 'O') {
/* There are no initializers; skip the loop below. */
goto skip_operand_loop;
} /* if */
if (get_char(p, dctl) == 'b' && get_char(p+1, dctl) == 'i') {
/* A brace-enclosed initializer list. */
p += 2;
write_id_ch('{', dctl);
close_str = "}";
} else {
/* A parenthesized initializer list. */
write_id_ch('(', dctl);
close_str = ")";
} /* if */
} else if (strcmp(operator_str, "gcnew") == 0) {
/* C++/CLI gcnew. */
write_id_str(operator_str, dctl);
write_id_ch(' ', dctl);
operator_str = "";
has_variable_number_of_operands = TRUE;
/* Get the count of dimensions. */
p = get_number_with_optional_underscore(p, &num_dimensions, dctl);
if (num_dimensions == 0) {
/* Non-array case. */
p = demangle_type(p, dctl);
} else {
a_const_char *dim_p = p;
/* Array case; emit the dimensions (but emit the type first). */
dctl->suppress_id_output++;
for (i = 1; i <= num_dimensions; i++) {
p = demangle_expression(p, /*need_parens=*/FALSE, dctl);
} /* for */
dctl->suppress_id_output--;
p = demangle_type(p, dctl);
write_id_ch('(', dctl);
for (i = 1; i <= num_dimensions; i++) {
dim_p = demangle_expression(dim_p, /*need_parens=*/FALSE, dctl);
if (i != num_dimensions) write_id_str(", ", dctl);
} /* for */
write_id_str(") ", dctl);
} /* if */
goto handle_new_operands;
} else if (strcmp(operator_str, "delete") == 0 ||
strcmp(operator_str, "delete[]") == 0) {
/* delete may have an optional "g" indicating a global scope delete. */
if (get_char(p, dctl) == 'g') {
p++;
write_id_str("::", dctl);
} /* if */
} else if (strcmp(operator_str, "subscript") == 0) {
/* A C++/CLI subscript operation (with a variable number of operands). */
has_variable_number_of_operands = TRUE;
is_cli_subscript = TRUE;
} /* if */
/* Get the count of operands. */
p = get_number_with_optional_underscore(p, &num_operands, dctl);
/* Some operations (e.g., sizeof(type), __alignof__(type), etc.) take
zero operands. */
if (num_operands != 0) {
if (has_variable_number_of_operands) {
/* Operation has a variable number of operations, and
they may be type operands (i.e., builtin-operation). */
for (i = 1; i <= num_operands; i++) {
if (get_char(p, dctl) == 'T') {
/* Type operand. */
p = demangle_type(p+1, dctl);
} else {
p = demangle_expression(p, need_adl_parens, dctl);
} /* if */
if (is_call) {
/* This is a call to the target just emitted; the rest are
arguments. */
write_id_str("(", dctl);
close_str = ")";
is_call = FALSE;
} else if (is_cli_subscript) {
/* This is a C++/CLI subscript operation, we've just emitted
the array, the remaining operands are subscripts. */
write_id_str("[", dctl);
close_str = "]";
is_cli_subscript = FALSE;
} else if (i != num_operands) {
write_id_str(", ", dctl);
} /* if */
} /* for */
} else {
/* Normal case, i.e., the operation has one, two, or three operands
(and isn't an operation that has a variable number of operands --
some of which may be types -- like a builtin-operation). */
if (num_operands == 1 && !is_postfix) {
/* Prefix unary operator -- operator comes first. */
write_id_str(operator_str, dctl);
if (strcmp(operator_str, "delete") == 0 ||
strcmp(operator_str, "delete[]") == 0) {
/* Add a space to separate from expression. */
write_id_ch(' ', dctl);
} /* if */
} /* if */
/* Process the first operand. */
p = demangle_expression(p, /*need_parens=*/TRUE, dctl);
if (num_operands == 1 && is_postfix) {
/* Postfix unary operator -- operator comes last. */
write_id_str(operator_str, dctl);
} /* if */
if (num_operands > 1) {
/* Binary and ternary operators -- operator comes after first
operand. */
if (strcmp(operator_str, "[]") == 0) {
/* For subscripting, put one "[" between the operands and one
at the end. */
operator_str = "[";
close_str = "]";
} /* if */
write_id_str(operator_str, dctl);
/* Process the second operand. */
p = demangle_expression(p, /*need_parens=*/TRUE, dctl);
if (num_operands > 2) {
/* Ternary operand -- "?". */
write_id_ch(':', dctl);
/* Process the third operand. */
p = demangle_expression(p, /*need_parens=*/TRUE, dctl);
} /* if */
} /* if */
} /* if */
} else if (strcmp(operator_str, "throw ") == 0) {
/* A rethrow has no operands, just put out the string. */
write_id_str(operator_str, dctl);
} /* if */
write_id_str(close_str, dctl);
skip_operand_loop:
if (need_parens) write_id_ch(')', dctl);
/* Check for the final "O". */
if (get_char(p, dctl) != 'O') {
bad_mangled_name(dctl);
} else {
p++;
} /* if */
} /* if */
return p;
} /* demangle_operation */
static void clear_template_param_block(a_template_param_block_ptr tpbp)
/*
Clear the fields of the indicated template parameter block.
*/
{
tpbp->nesting_level = 0;
tpbp->final_specialization = NULL;
tpbp->set_final_specialization = FALSE;
tpbp->actual_template_args_until_final_specialization = FALSE;
tpbp->output_only_correspondences = FALSE;
tpbp->first_correspondence = FALSE;
tpbp->use_old_form_for_template_output = FALSE;
} /* clear_template_param_block */
static a_const_char *demangle_template_arguments(
a_const_char *ptr,
a_boolean emit_arg_values,
a_boolean suppress_angle_brackets,
a_template_param_block_ptr temp_par_info,
a_decode_control_block_ptr dctl)
/*
Demangle the template class arguments or template parameter pack beginning at
ptr and output the demangled form. Return a pointer to the character position
following what was demangled. ptr points to just past the "__tm__", "__ps__",
"__pt__", or "__pk__" string. emit_arg_values is TRUE if the template
argument "values" (i.e., type or nontype value) should be emitted rather than
the template parameter name. This is used for a partial-specialization
parameter list ("__ps__") or parameter pack ("__pk__"). Angle brackets are
suppressed when suppress_angle_brackets is TRUE (when called to demangle
a parameter pack). When temp_par_info != NULL, it points to a block that
controls output of extra information on template parameters.
*/
{
a_const_char *p = ptr, *arg_base, *prev_end;
char ch;
unsigned long nchars, position;
a_boolean nontype, skipped, unskipped, is_pack;
if (temp_par_info != NULL && !emit_arg_values) {
temp_par_info->nesting_level++;
} /* if */
/* A template argument list looks like
__tm__3_ii
^^---- Argument types.
^------- Size of argument types, including the underscore.
^------- ptr points here.
For the first argument list of a partial specialization, "__tm__" is
replaced by "__ps__". For old-form mangling of templates, "__tm__"
is replaced by "__pt__". Template arguments can be either nontype
(as identified by an "X"), a template argument pack (as identified
by "__pk__"), or types (otherwise).
*/
if (!suppress_angle_brackets) write_id_ch('<', dctl);
/* Scan the size. */
p = get_length(p, &nchars, &prev_end, dctl);
arg_base = p;
p = advance_past_underscore(p, dctl);
/* Loop to process the arguments. */
for (position = 1;; position++) {
/* Check for zero arguments case. */
if ((unsigned long)(p - arg_base) >= nchars) break;
if (dctl->err_in_id) break; /* Avoid infinite loops on errors. */
if (start_of_id_is("__pk__", p, dctl)) {
/* Template argument packs are encoded much like a template argument
list, except "__pk__" is used to introduce them:
__pk__3_sc
^^---- Argument types.
^------- Size of argument types, including the underscore.
*/
is_pack = TRUE;
p+=6; /* Advance past the "__pk__". */
} else {
is_pack = FALSE;
} /* if */
ch = get_char(p, dctl);
if (ch == '\0' || (ch == '_' && !is_pack)) {
/* We ran off the end of the string. */
bad_mangled_name(dctl);
break;
} /* if */
/* "X" identifies the beginning of a nontype argument. */
nontype = (ch == 'X');
skipped = unskipped = FALSE;
if (!emit_arg_values && temp_par_info != NULL &&
!temp_par_info->use_old_form_for_template_output &&
!temp_par_info->actual_template_args_until_final_specialization) {
/* Doing something special: writing out the template parameter name. */
if (temp_par_info->output_only_correspondences) {
/* This is the second pass, which writes out parameter/argument
correspondences, e.g., "T1=int". Output has been suppressed
in general, and is turned on briefly here. */
dctl->suppress_id_output--;
unskipped = TRUE;
/* Put out a comma between entries and a left bracket preceding the
first entry. */
if (temp_par_info->first_correspondence) {
write_id_str(" [with ", dctl);
temp_par_info->first_correspondence = FALSE;
} else {
write_id_str(", ", dctl);
} /* if */
} /* if */
/* Write the template parameter name. */
write_template_parameter_name(temp_par_info->nesting_level +
dctl->mangling_nesting_level,
position,
nontype, dctl);
if (temp_par_info->output_only_correspondences) {
/* This is the second pass, to write out correspondences, so put the
argument value out after the parameter name. */
if (is_pack) {
/* Indicate this is a pack (only in the correspondences). */
write_id_str("...", dctl);
} /* if */
write_id_ch('=', dctl);
} else {
/* This is the first pass. The argument value is skipped. In
the second pass, its value will be written out. */
/* We still have to scan over the argument value, but suppress
output. */
dctl->suppress_id_output++;
skipped = TRUE;
} /* if */
} /* if */
/* Write the argument value. */
if (nontype) {
/* Nontype argument. */
p++; /* Advance past the "X". */
p = demangle_constant(p, /*suppress_address_of=*/FALSE,
/*need_parens=*/FALSE, dctl);
} else if (is_pack) {
/* A template argument pack. */
a_template_param_block pack_temp_par_info;
clear_template_param_block(&pack_temp_par_info);
/* Recurse to handle the template argument pack. */
p = demangle_template_arguments(p, /*emit_arg_values=*/TRUE,
/*suppress_angle_brackets=*/TRUE,
&pack_temp_par_info, dctl);
} else {
/* Type argument. */
p = demangle_type(p, dctl);
} /* if */
if (skipped) dctl->suppress_id_output--;
if (unskipped) dctl->suppress_id_output++;
/* Stop after the last argument. */
if ((unsigned long)(p - arg_base) >= nchars) break;
write_id_str(", ", dctl);
} /* for */
dctl->end_of_name = prev_end;
if (!suppress_angle_brackets) write_id_ch('>', dctl);
return p;
} /* demangle_template_arguments */
static a_const_char *demangle_operator(
a_const_char *ptr,
int *mangled_length,
a_boolean *takes_type,
a_boolean *is_new_style_cast,
a_boolean *is_postfix,
a_boolean *need_adl_parens,
a_boolean *is_initializer_list,
a_boolean *ud_suffix_follows,
a_decode_control_block_ptr dctl)
/*
Examine the first few characters at ptr to see if they are an encoding for
an operator (e.g., "pl" for plus). If so, return a pointer to a string for
the operator (e.g., "+"), set *mangled_length to the number of characters
in the encoding, and *takes_type to TRUE if the operator takes a type
modifier (e.g., cast). *is_new_style_cast is set to TRUE if the operator
is a new style cast (and needs a closing '>' and expression emitted).
*is_postfix is set to TRUE if the operator is a postfix operator (unary
operators are typically emitted as prefix). *need_adl_parens is set to TRUE
if the operator is a call that requires parentheses to suppress ADL.
*is_initializer_list is set to TRUE if the operator is an initializer list.
*ud_suffix_follows is set to TRUE if a literal operator (i.e., operator "")
is scanned and the ud-suffix follows the decoded operator name (which
must then be demangled by the caller).
If the first few characters are not an operator encoding, return NULL.
*/
{
a_const_char *s;
int len = 2;
*takes_type = FALSE;
*is_new_style_cast = FALSE;
*is_postfix = FALSE;
*need_adl_parens = FALSE;
*is_initializer_list = FALSE;
*ud_suffix_follows = FALSE;
/* The length-3 codes are tested first to avoid taking their first two
letters as one of the length-2 codes. */
if (start_of_id_is("apl", ptr, dctl)) {
s = "+=";
len = 3;
} else if (start_of_id_is("ami", ptr, dctl)) {
s = "-=";
len = 3;
} else if (start_of_id_is("amu", ptr, dctl)) {
s = "*=";
len = 3;
} else if (start_of_id_is("adv", ptr, dctl)) {
s = "/=";
len = 3;
} else if (start_of_id_is("amd", ptr, dctl)) {
s = "%=";
len = 3;
} else if (start_of_id_is("aer", ptr, dctl)) {
s = "^=";
len = 3;
} else if (start_of_id_is("aad", ptr, dctl)) {
s = "&=";
len = 3;
} else if (start_of_id_is("aor", ptr, dctl)) {
s = "|=";
len = 3;
} else if (start_of_id_is("ars", ptr, dctl)) {
s = ">>=";
len = 3;
} else if (start_of_id_is("als", ptr, dctl)) {
s = "<<=";
len = 3;
} else if (start_of_id_is("ppe", ptr, dctl)) {
s = "++";
len = 3;
} else if (start_of_id_is("mme", ptr, dctl)) {
s = "--";
len = 3;
} else if (start_of_id_is("nwa", ptr, dctl)) {
s = "new[]";
len = 3;
} else if (start_of_id_is("dla", ptr, dctl)) {
s = "delete[]";
len = 3;
} else if (start_of_id_is("nw", ptr, dctl)) {
s = "new";
} else if (start_of_id_is("gc", ptr, dctl)) {
s = "gcnew";
} else if (start_of_id_is("dl", ptr, dctl)) {
s = "delete";
} else if (start_of_id_is("pl", ptr, dctl)) {
s = "+";
} else if (start_of_id_is("mi", ptr, dctl)) {
s = "-";
} else if (start_of_id_is("ml", ptr, dctl)) {
s = "*";
} else if (start_of_id_is("dv", ptr, dctl)) {
s = "/";
} else if (start_of_id_is("md", ptr, dctl)) {
s = "%";
} else if (start_of_id_is("er", ptr, dctl)) {
s = "^";
} else if (start_of_id_is("ad", ptr, dctl)) {
s = "&";
} else if (start_of_id_is("or", ptr, dctl)) {
s = "|";
} else if (start_of_id_is("co", ptr, dctl)) {
s = "~";
} else if (start_of_id_is("nt", ptr, dctl)) {
s = "!";
} else if (start_of_id_is("as", ptr, dctl)) {
s = "=";
} else if (start_of_id_is("lt", ptr, dctl)) {
s = "<";
} else if (start_of_id_is("gt", ptr, dctl)) {
s = ">";
} else if (start_of_id_is("ls", ptr, dctl)) {
s = "<<";
} else if (start_of_id_is("rs", ptr, dctl)) {
s = ">>";
} else if (start_of_id_is("eq", ptr, dctl)) {
s = "==";
} else if (start_of_id_is("ne", ptr, dctl)) {
s = "!=";
} else if (start_of_id_is("le", ptr, dctl)) {
s = "<=";
} else if (start_of_id_is("ge", ptr, dctl)) {
s = ">=";
} else if (start_of_id_is("aa", ptr, dctl)) {
s = "&&";
} else if (start_of_id_is("oo", ptr, dctl)) {
s = "||";
} else if (start_of_id_is("pp", ptr, dctl)) {
s = "++";
*is_postfix = TRUE;
} else if (start_of_id_is("mm", ptr, dctl)) {
s = "--";
*is_postfix = TRUE;
} else if (start_of_id_is("cm", ptr, dctl)) {
s = ",";
} else if (start_of_id_is("rm", ptr, dctl)) {
s = "->*";
} else if (start_of_id_is("rf", ptr, dctl)) {
s = "->";
} else if (start_of_id_is("cl", ptr, dctl)) {
s = "()";
} else if (start_of_id_is("cp", ptr, dctl)) {
*need_adl_parens = TRUE;
s = "()";
} else if (start_of_id_is("vc", ptr, dctl)) {
s = "[]";
} else if (start_of_id_is("qs", ptr, dctl)) {
s = "?";
} else if (start_of_id_is("mn", ptr, dctl)) {
s = "<?";
} else if (start_of_id_is("mx", ptr, dctl)) {
s = ">?";
} else if (start_of_id_is("ds", ptr, dctl)) {
s = ".*";
} else if (start_of_id_is("dt", ptr, dctl)) {
s = ".";
} else if (start_of_id_is("ps", ptr, dctl)) {
s = "+";
} else if (start_of_id_is("ng", ptr, dctl)) {
s = "-";
} else if (start_of_id_is("de", ptr, dctl)) {
s = "*";
} else if (start_of_id_is("ao", ptr, dctl)) {
s = "&";
} else if (start_of_id_is("rl", ptr, dctl)) {
s = "__real(";
} else if (start_of_id_is("im", ptr, dctl)) {
s = "__imag(";
} else if (start_of_id_is("dc", ptr, dctl)) {
s = "dynamic_cast<";
*is_new_style_cast = TRUE;
*takes_type = TRUE;
} else if (start_of_id_is("sc", ptr, dctl)) {
s = "static_cast<";
*is_new_style_cast = TRUE;
*takes_type = TRUE;
} else if (start_of_id_is("cc", ptr, dctl)) {
s = "const_cast<";
*is_new_style_cast = TRUE;
*takes_type = TRUE;
} else if (start_of_id_is("rc", ptr, dctl)) {
s = "reinterpret_cast<";
*is_new_style_cast = TRUE;
*takes_type = TRUE;
} else if (start_of_id_is("sf", ptr, dctl)) {
s = "safe_cast<";
*is_new_style_cast = TRUE;
*takes_type = TRUE;
} else if (start_of_id_is("tw", ptr, dctl)) {
s = "throw ";
} else if (start_of_id_is("sz", ptr, dctl)) {
s = "sizeof(";
*takes_type = TRUE;
} else if (start_of_id_is("cs", ptr, dctl)) {
s = "cast";
*takes_type = TRUE;
} else if (start_of_id_is("af", ptr, dctl)) {
s = "__alignof__(";
*takes_type = TRUE;
} else if (start_of_id_is("uu", ptr, dctl)) {
s = "__uuidof(";
*takes_type = TRUE;
} else if (start_of_id_is("ty", ptr, dctl)) {
s = "typeid(";
*takes_type = TRUE;
} else if (start_of_id_is("ct", ptr, dctl)) {
s = "::typeid";
*takes_type = TRUE;
} else if (start_of_id_is("bi", ptr, dctl)) {
s = "builtin-operation";
} else if (start_of_id_is("sp", ptr, dctl)) {
s = "...";
*is_postfix = TRUE;
} else if (start_of_id_is("sk", ptr, dctl)) {
s = "sizeof...(";
*takes_type = TRUE;
} else if (start_of_id_is("ht", ptr, dctl)) {
s = "%";
} else if (start_of_id_is("sb", ptr, dctl)) {
s = "subscript";
} else if (start_of_id_is("il", ptr, dctl)) {
s = "{";
*is_initializer_list = TRUE;
} else if (start_of_id_is("tl", ptr, dctl)) {
s = "{";
*takes_type = TRUE;
*is_initializer_list = TRUE;
} else if (start_of_id_is("nx", ptr, dctl)) {
s = "noexcept(";
} else if (start_of_id_is("li", ptr, dctl)) {
/* Note that the ud-suffix follows the "li" and needs to be
demangled by the caller. */
s = "\"\"";
*ud_suffix_follows = TRUE;
} else if (start_of_id_is("aw", ptr, dctl)) {
s = "co_await";
} else if (start_of_id_is("fr", ptr, dctl) ||
start_of_id_is("fl", ptr, dctl) ||
start_of_id_is("fR", ptr, dctl) ||
start_of_id_is("fL", ptr, dctl)) {
/* A fold expression. The actual length is not easily known and must be
determined by the caller. The returned value is simply a flag (and
not an actual operation string). */
s = "fold-ex";
} else {
s = NULL;
} /* if */
*mangled_length = len;
return s;
} /* demangle_operator */
static a_boolean is_operator_function_name(
a_const_char *ptr,
a_const_char **demangled_name,
int *mangled_length,
a_boolean *ud_suffix_follows,
a_decode_control_block_ptr dctl)
/*
Examine the string beginning at ptr to see if it is the mangled name for
an operator function. If so, return TRUE and set *demangled_name to
the demangled form, and *mangled_length to the length of the mangled form.
*ud_suffix_follows is set to TRUE if the encoding for the operator function
name is followed by a user-defined literal suffix (which must be demangled
by the caller).
*/
{
a_const_char *s, *end_ptr;
int len;
a_boolean takes_type, is_new_style_cast, is_postfix, need_adl_parens;
a_boolean is_initializer_list;
/* Get the operator name. */
s = demangle_operator(ptr, &len, &takes_type, &is_new_style_cast,
&is_postfix, &need_adl_parens, &is_initializer_list,
ud_suffix_follows, dctl);
if (s != NULL) {
/* Make sure we took the whole name and nothing more. */
end_ptr = ptr + len;
if (*ud_suffix_follows && end_ptr != NULL && !dctl->err_in_id &&
isdigit((unsigned char)get_char(end_ptr, dctl))) {
/* If a ud-suffix follows, make sure its length is accounted for.
Note that prev_end is restored here as this is just speculative
look ahead. */
unsigned long num;
a_const_char *prev_end;
end_ptr = get_length(end_ptr, &num, &prev_end, dctl);
end_ptr = end_ptr + num;
/* Restore any fields that might have been changed by the speculative
lookahead. */
dctl->end_of_name = prev_end;
dctl->err_in_id = FALSE;
} /* if */
if (get_char(end_ptr, dctl) == '\0' ||
(get_char(end_ptr, dctl) == '_' && get_char(end_ptr+1, dctl) == '_')) {
/* Okay. */
} else {
s = NULL;
*ud_suffix_follows = FALSE;
} /* if */
} /* if */
*demangled_name = s;
*mangled_length = len;
return (s != NULL);
} /* is_operator_function_name */
static void note_specialization(a_const_char *ptr,
a_template_param_block_ptr temp_par_info)
/*
Note the fact that a specialization indication has been encountered at ptr
while scanning a mangled name. temp_par_info, if non-NULL, points to
a block of information related to template parameter processing.
*/
{
if (temp_par_info != NULL) {
if (temp_par_info->set_final_specialization) {
/* Remember the location of the last specialization seen. */
temp_par_info->final_specialization = ptr;
} else if (temp_par_info->actual_template_args_until_final_specialization&&
ptr == temp_par_info->final_specialization) {
/* Stop doing the special processing for specializations when the
final specialization is reached. */
temp_par_info->actual_template_args_until_final_specialization = FALSE;
} /* if */
} /* if */
} /* note_specialization */
static a_const_char *demangle_function_local_indication(
a_const_char *ptr,
unsigned long nchars,
unsigned long *instance,
a_decode_control_block_ptr dctl)
/*
Demangle the function name and id number in a function-local indication:
__L2__f__Fv
^-- returned pointer points here
^------- mangled function name
^---------- instance number within function (ptr points here on entry)
ptr points to the character after the "__L". If nchars is non-zero, it
indicates the length of the string, starting from ptr. Return a pointer
to the character following the mangled function name. Output a function
indication like "f(void)::". The instance number is simply a way of
differentiating between similarly named entities in the same function and
may be a discriminator (for class/scoped enums), scope number, or block number
depending what is being mangled and is returned to the caller in *instance.
This allows the caller to emit it later (after the name of the entity) or
suppress it (in cases where it is duplicated).
*/
{
a_const_char *p = ptr, *prev_end = NULL;
if (nchars != 0) {
prev_end = dctl->end_of_name;
dctl->end_of_name = ptr + nchars;
} /* if */
/* Get the instance number. */
p = get_number(ptr, instance, dctl);
/* Check for the two underscores following the instance number. For local
class names in some older versions of the mangling scheme, there is no
following function name. */
if (get_char(p, dctl) == '_' && get_char(p+1, dctl) == '_') {
p += 2;
/* Put out the function name. */
if (nchars != 0) nchars -= (unsigned long)(p - ptr);
p = full_demangle_identifier(p, nchars,
/*suppress_parent_and_local_info=*/FALSE,
dctl);
write_id_str("::", dctl);
} /* if */
if (prev_end != NULL) dctl->end_of_name = prev_end;
return p;
} /* demangle_function_local_indication */
static void emit_instance(unsigned long instance,
a_decode_control_block_ptr dctl)
/*
The instance number is part of a local function mangling (used to
differentiate between entities with the same name within the same function).
This could represent a discriminator, scope number or block number depending
on what has been mangled. Emit it as an instance number.
*/
{
if (!dctl->err_in_id) {
write_id_str(" (instance ", dctl);
write_id_number(instance, dctl);
write_id_str(")", dctl);
} /* if */
} /* emit_instance */
static a_const_char *demangle_name(
a_const_char *ptr,
unsigned long nchars,
a_boolean stop_on_underscores,
unsigned long *nchars_left,
a_const_char *mclass,
a_template_param_block_ptr temp_par_info,
a_boolean *instance_emitted,
a_decode_control_block_ptr dctl)
/*
Demangle the name at ptr and output the demangled form. Return a pointer
to the character position following what was demangled. A "name" is
usually just a string of alphanumeric characters. However, names of
constructors, destructors, and operator functions require special
handling, as do template entity names. A name at this level
does not include any associated parent or function-local information,
nor function-parameter information. nchars indicates the number
of characters in the name, or is zero if the name is open-ended
(it's ended by a null or double underscore). A double underscore
ends the name if stop_on_underscores is TRUE (though some sequences
beginning with two underscores and related to templates, e.g., "__pt",
are recognized and processed locally regardless of the setting of
stop_on_underscores). If nchars_left is non-NULL, no error is
issued if too few characters are taken to satisfy nchars;
the count of remaining characters is placed in *nchars_left.
mclass, when non-NULL, points to the mangled form of the class of
which this name is a member. When it's non-NULL, constructor and
destructor names will be put out in the proper form (otherwise,
they are left in their original forms). If instance_emitted is non-NULL,
it is set to TRUE if the name has an instance number (as is the case
with unnamed types and lambdas); this allows the caller to suppress
duplicate instance numbers when the type appears in a local environment.
instance_emitted is set to FALSE otherwise. When temp_par_info != NULL,
it points to a block that controls output of extra information on
template parameters.
*/
{
a_const_char *p, *end_ptr = NULL, *prev_end = NULL;
a_boolean is_special_name = FALSE, is_pt, is_partial_spec = FALSE;
a_boolean partial_spec_output_suppressed = FALSE, ud_suffix_follows;
a_const_char *demangled_name;
int mangled_length;
unsigned long discriminator;
if (instance_emitted != NULL) *instance_emitted = FALSE;
if (nchars != 0) {
prev_end = dctl->end_of_name;
dctl->end_of_name = ptr + nchars;
} /* if */
if (nchars_left != NULL) *nchars_left = 0;
/* See if the name is special in some way. */
if (get_char(ptr, dctl) == '_' && get_char(ptr+1, dctl) == '_') {
/* Name beginning with two underscores. */
p = ptr + 2;
if (start_of_id_is("ct__", p, dctl) ||
start_of_id_is("st__", p, dctl)) {
/* Constructor or C++/CLI static constructor. */
end_ptr = p + 2;
if (mclass == NULL) {
/* The mangled name for the class is not provided, so handle this as
a normal name. */
} else {
/* Output the class name for the constructor name. */
is_special_name = TRUE;
(void)full_demangle_type_name(mclass, /*base_name_only=*/TRUE,
/*temp_par_info=*/
(a_template_param_block_ptr)NULL,
/*is_destructor_name=*/FALSE,
dctl);
if (start_of_id_is("st__", p, dctl)) {
/* Add an indication that this is a C++/CLI static constructor. */
write_id_str("[static]", dctl);
} /* if */
} /* if */
} else if (start_of_id_is("dt__", p, dctl) ||
start_of_id_is("df__", p, dctl)) {
/* Destructor or C++/CLI finalizer. */
end_ptr = p + 2;
if (mclass == NULL) {
/* The mangled name for the class is not provided, so handle this as
a normal name. */
} else {
/* Output ~class-name for the destructor name, or !class-name for
a C++/CLI finalizer. */
is_special_name = TRUE;
if (start_of_id_is("df__", p, dctl)) {
write_id_ch('!', dctl);
} else {
write_id_ch('~', dctl);
} /* if */
(void)full_demangle_type_name(mclass, /*base_name_only=*/TRUE,
/*temp_par_info=*/
(a_template_param_block_ptr)NULL,
/*is_destructor_name=*/FALSE,
dctl);
} /* if */
} else if (start_of_id_is("dn__", p, dctl)) {
/* Destructor name. */
/* This differs from the dt__ case above in two ways: its demangling
doesn't always have a scope operator (i.e., ::), and it doesn't
require that the destructor name be the same as the qualifying type
(e.g., it can handle T::~X()). What follows (a "destructor name")
can be parsed as a nested type, but has an implied ~ before the
final qualifier. For example, Q4_1A1B1C1D would demangle as
A::B::C::~D and 1A would demangle as ~A (as in a.~A()). */
is_special_name = TRUE;
if (get_char(p+4, dctl) == 'Q') {
/* Destructor is qualified. */
end_ptr = full_demangle_type_name(p+4, /*base_name_only=*/FALSE,
/*temp_par_info=*/
(a_template_param_block_ptr)NULL,
/*is_destructor_name=*/TRUE,
dctl);
} else {
/* An unqualified type. */
write_id_ch('~', dctl);
end_ptr = demangle_type(p+4, dctl);
} /* if */
} else if (start_of_id_is("op", p, dctl)) {
/* Conversion function. Name looks like __opi__... where the part
after "op" encodes the type (e.g., "opi" is "operator int"). */
is_special_name = TRUE;
write_id_str("operator ", dctl);
end_ptr = demangle_type(p+2, dctl);
} else if (is_operator_function_name(p, &demangled_name,
&mangled_length, &ud_suffix_follows,
dctl)) {
/* Operator function. */
is_special_name = TRUE;
write_id_str("operator ", dctl);
write_id_str(demangled_name, dctl);
end_ptr = p + mangled_length;
if (ud_suffix_follows) {
/* A literal operator (i.e., operator ""); the ud-suffix follows. */
end_ptr = demangle_name_with_preceding_length(end_ptr, dctl);
} /* if */
} else if (nchars != 0 && start_of_id_is("N", p, dctl)) {
/* __Nxxxx: unnamed namespace name. Put out "<unnamed>" and ignore
the characters after "__N". For nested unnamed namespaces there
is no number after the "__N". */
is_special_name = TRUE;
write_id_str("<unnamed>", dctl);
end_ptr = p + nchars - 2;
} else if (nchars != 0 && start_of_id_is("INTERNAL", p, dctl)) {
/* __INTERNAL<module_id>: An individuated namespace name. */
is_special_name = TRUE;
write_id_str("[local to ", dctl);
end_ptr = demangle_module_id(p+8, nchars-(8+2), p-2, dctl);
write_id_str("]", dctl);
} else if (start_of_id_is("Ut", p, dctl)) {
/* __Utnn: An unnamed type. */
write_id_str("[unnamed type", dctl);
p = get_number(p+2, &discriminator, dctl);
if (discriminator > 0) {
write_id_str(" (instance ", dctl);
write_id_number(discriminator, dctl);
write_id_str(")", dctl);
is_special_name = TRUE;
end_ptr = p;
if (instance_emitted != NULL) *instance_emitted = TRUE;
} else {
bad_mangled_name(dctl);
} /* if */
write_id_str("]", dctl);
} else if (start_of_id_is("Ul", p, dctl) ||
start_of_id_is("Um", p, dctl)) {
/* __Ulnn_<function-type> or __Umnn_<function-type>: Lambda closure.
For demangling purposes, treat these the same; the member initializer
case will be preceded by the name of the member being initialized,
so no further words are necessary. */
p = get_number(p+2, &discriminator, dctl);
if (get_char(p, dctl) == '_') {
write_id_str("[lambda", dctl);
p = demangle_type(p+1, dctl);
if (discriminator > 0) {
write_id_str(" (instance ", dctl);
write_id_number(discriminator, dctl);
write_id_str(")", dctl);
is_special_name = TRUE;
end_ptr = p;
if (instance_emitted != NULL) *instance_emitted = TRUE;
} else {
bad_mangled_name(dctl);
} /* if */
write_id_str("]", dctl);
} else {
bad_mangled_name(dctl);
} /* if */
} else if (start_of_id_is("Ud", p, dctl)) {
/* __Udnn_p_<function-type>: Lambda closure in default argument.
Note that this will always appear in a local function context, but
that is handled at a higher level. */
unsigned long param_num;
p = get_number(p+2, &discriminator, dctl);
if (get_char(p, dctl) == '_') {
p = get_number(p+1, &param_num, dctl);
if (get_char(p, dctl) == '_') {
write_id_str("[lambda", dctl);
p = demangle_type(p+1, dctl);
write_id_str(" in default argument ", dctl);
write_id_number(param_num, dctl);
write_id_str(" (from end)", dctl);
if (discriminator > 0) {
write_id_str(" (instance ", dctl);
write_id_number(discriminator, dctl);
write_id_str(")", dctl);
is_special_name = TRUE;
end_ptr = p;
if (instance_emitted != NULL) *instance_emitted = TRUE;
} else {
bad_mangled_name(dctl);
} /* if */
write_id_str("]", dctl);
} else {
bad_mangled_name(dctl);
} /* if */
} /* if */
} else if (start_of_id_is("ab", p, dctl)) {
/* __abnn<tag>: Mangling for __attribute(abi_tag((tag)). Multiple
"abi_tag" attributes can be specified. */
unsigned long count;
p = p+2;
write_id_str("[abi:", dctl);
for (;;) {
p = get_number(p, &count, dctl);
if (count == 0 || p+count > dctl->end_of_name) {
bad_mangled_name(dctl);
break;
} /* if */
while (count--) {
write_id_ch(*p++, dctl);
} /* while */
if (start_of_id_is("__ab", p, dctl)) {
p = p+4;
write_id_ch(',', dctl);
} else {
break;
} /* if */
} /* for */
write_id_ch(']', dctl);
if (!dctl->err_in_id) {
/* This mangling is basically a prefix; what remains is still a name
(possibly with special names that are checked for above); recurse
to handle that case. */
if (nchars != 0) {
if (nchars > (p - ptr)) {
nchars -= (unsigned long)(p - ptr);
} else {
bad_mangled_name(dctl);
} /* if */
} /* if */
end_ptr = demangle_name(p, nchars, stop_on_underscores, nchars_left,
mclass, temp_par_info, instance_emitted, dctl);
} else {
end_ptr = p;
} /* if */
goto end_of_routine;
} else if (start_of_id_is("SBC__", p, dctl)) {
/* Mangled name for a structured binding container. */
write_id_str("structured binding for [", dctl);
for (p = p+5; *p != '\0';) {
if (get_char(p, dctl) == '_') {
if (get_char(p+1, dctl) == '_') {
if (get_char(p+2, dctl) == '_') {
if (get_char(p+3, dctl) == '_') {
/* Quadruple underscore -- end of structured binding. */
p += 4;
} else {
p += 3;
bad_mangled_name(dctl);
} /* if */
break;
} else {
/* Double underscore -- structured binding delimiter. */
write_id_ch(',', dctl);
p += 2;
} /* if */
} else {
/* Underscore followed by non-underscore -- just copy to output. */
write_id_ch('_', dctl);
write_id_ch(p[1], dctl);
p += 2;
} /* if */
} else {
/* Not an underscore -- just copy to output. */
write_id_ch(*p, dctl);
p += 1;
} /* if */
} /* for */
write_id_ch(']', dctl);
end_ptr = p;
goto end_of_routine;
} else {
/* Something unrecognized. */
} /* if */
} /* if */
/* Here, end_ptr non-null means the end of the string has been found
already (because the name is special in some way). */
if (end_ptr == NULL) {
/* Not a special name. Find the end of the string and set end_ptr.
Also look for template-related things that terminate the name
earlier. */
for (p = ptr; ; p++) {
char ch = get_char(p, dctl);
/* Stop at the end of the string. */
if (ch == '\0') break;
/* Stop on a double underscore, but not one at the start of the string.
More than 2 underscores in a row does not terminate the string,
so that something like the name for "void f_()" (i.e., "f___Fv")
can be demangled successfully. */
if (ch == '_' && p != ptr &&
get_char(p+1, dctl) == '_' &&
get_char(p+2, dctl) != '_' &&
/* When stop_on_underscores is FALSE, stop only on "__tm__",
"__ps__", "__pt__", or "__S". Double underscores can appear
in the middle of some names, e.g., member names used as
template arguments. */
(stop_on_underscores ||
(get_char(p+2, dctl) == 't' &&
get_char(p+3, dctl) == 'm' &&
get_char(p+4, dctl) == '_' &&
get_char(p+5, dctl) == '_') ||
(get_char(p+2, dctl) == 'p' &&
get_char(p+3, dctl) == 's' &&
get_char(p+4, dctl) == '_' &&
get_char(p+5, dctl) == '_') ||
(get_char(p+2, dctl) == 'p' &&
get_char(p+3, dctl) == 't' &&
get_char(p+4, dctl) == '_' &&
get_char(p+5, dctl) == '_') ||
get_char(p+2, dctl) == 'S')) {
break;
} /* if */
} /* for */
end_ptr = p;
} /* if */
/* Here, end_ptr indicates the character after the end of the initial
part of the name. */
if (!is_special_name) {
/* Output the characters of the base name. */
for (p = ptr; p < end_ptr; p++) write_id_ch(*p, dctl);
} /* if */
/* If there's a template argument list for a partial specialization
(beginning with "__ps__"), process it. */
if (start_of_id_is("__ps__", end_ptr, dctl)) {
/* Write the arguments. This first argument list gives the arguments
that appear in the partial specialization declaration:
template <class T, class U> struct A { ... };
template <class T> struct A<T *, int> { ... };
^^^^^^^^this argument list
This first argument list will be followed by another argument list
that gives the arguments according to the partial specialization.
For A<int *, int> according to the example above, the second
argument list is <int>. The second argument list is scanned but
not put out, except when argument correspondences are output. */
end_ptr = demangle_template_arguments(end_ptr+6, /*emit_arg_values=*/TRUE,
/*suppress_angle_brackets=*/FALSE,
temp_par_info, dctl);
note_specialization(end_ptr, temp_par_info);
is_partial_spec = TRUE;
} /* if */
/* If there's a specialization indication ("__S"), ignore it. */
if (get_char(end_ptr, dctl) == '_' &&
get_char(end_ptr+1, dctl) == '_' &&
get_char(end_ptr+2, dctl) == 'S' &&
(!stop_on_underscores ||
get_char(end_ptr+3, dctl) == '\0' ||
(get_char(end_ptr+3, dctl) == '_' &&
get_char(end_ptr+4, dctl) == '_'))) {
note_specialization(end_ptr, temp_par_info);
end_ptr += 3;
} /* if */
/* If there's a template argument list (beginning with "__pt__" or "__tm__"),
process it. */
if ((is_pt = start_of_id_is("__pt__", end_ptr, dctl)) ||
start_of_id_is("__tm__", end_ptr, dctl)) {
/* The "__pt__ form indicates an old-style mangled template name. */
if (is_pt && temp_par_info != NULL ) {
temp_par_info->use_old_form_for_template_output = TRUE;
} /* if */
/* For the second argument list of a partial specialization,
process the argument list but suppress output. */
if (is_partial_spec && temp_par_info != NULL &&
!temp_par_info->output_only_correspondences) {
dctl->suppress_id_output++;
partial_spec_output_suppressed = TRUE;
} /* if */
/* Write the arguments. */
end_ptr = demangle_template_arguments(end_ptr+6, /*emit_arg_values=*/FALSE,
/*suppress_angle_brackets=*/FALSE,
temp_par_info, dctl);
if (partial_spec_output_suppressed) dctl->suppress_id_output--;
/* If there's a(nother) specialization indication ("__S"), ignore it. */
if (get_char(end_ptr, dctl) == '_' &&
get_char(end_ptr+1, dctl) == '_' &&
get_char(end_ptr+2, dctl) == 'S' &&
(!stop_on_underscores ||
get_char(end_ptr+3, dctl) == '\0' ||
(get_char(end_ptr+3, dctl) == '_' &&
get_char(end_ptr+4, dctl) == '_'))) {
note_specialization(end_ptr, temp_par_info);
end_ptr += 3;
} /* if */
} /* if */
/* Check that we took exactly the characters we should have. */
if (nchars_left != NULL) {
/* Return the count of characters not taken. We're not required to
end at the right place. */
*nchars_left = nchars - (unsigned long)(end_ptr-ptr);
} else if (((nchars != 0) ? (end_ptr-ptr == nchars) : (*end_ptr == '\0')) ||
(stop_on_underscores &&
get_char(end_ptr, dctl) == '_' &&
get_char(end_ptr+1, dctl) == '_')) {
/* Okay. */
} else {
bad_mangled_name(dctl);
} /* if */
end_of_routine:
if (prev_end != NULL) dctl->end_of_name = prev_end;
return end_ptr;
} /* demangle_name */
static a_const_char *demangle_type_name_with_preceding_length(
a_const_char *ptr,
a_boolean base_name_only,
unsigned long nchars,
unsigned long *nchars_left,
a_template_param_block_ptr temp_par_info,
a_decode_control_block_ptr dctl)
/*
Demangle a type name (or namespace name) that is preceded by a length, e.g.,
"3abc" for the type name "abc". The name can include template parameters or a
function-local indication but is not a nested type. If nchars is non-zero on
input, the length has already been scanned and nchars gives its value. In that
case, not all nchars characters of input need be taken, scanning will
stop on a "__", and *nchars_left is set to the number of characters not
taken. Return a pointer to the character position following what was
demangled. When temp_par_info != NULL, it points to a block that controls
output of extra information on template parameters. When base_name_only
is TRUE, suppress any function-local information.
*/
{
a_const_char *p = ptr, *orig_end, *prev_end;
a_const_char *p2;
unsigned long nchars2, instance;
a_boolean has_function_local_info = FALSE;
a_boolean instance_emitted;
a_boolean stop_on_underscores;
if (nchars == 0) {
/* Get the length. */
p = get_length(p, &nchars, &prev_end, dctl);
nchars_left = NULL;
stop_on_underscores = FALSE;
} else {
/* Length was gotten by the caller. */
if (nchars_left != NULL) *nchars_left = 0;
prev_end = dctl->end_of_name;
dctl->end_of_name = orig_end = ptr+nchars;
stop_on_underscores = TRUE;
} /* if */
if (nchars >= 8) {
/* Look for a function-local indication, e.g., "__Ln__f" for block
"n" of function "f". */
for (p2 = p+1; p2+6 < p+nchars; p2++) {
if (get_char(p2, dctl) == '_' &&
get_char(p2+1, dctl) == '_') {
if (get_char(p2+2, dctl) == 't' &&
get_char(p2+3, dctl) == 'm' &&
get_char(p2+4, dctl) == '_' &&
get_char(p2+5, dctl) == '_') {
/* Beware of a local type in a template argument list; don't
decode the local information yet, it will be decoded when the
local type is processed. */
unsigned long skip;
a_const_char *dummy;
p2 = get_length(p2+6, &skip, &dummy, dctl);
p2 += skip;
} else if (get_char(p2+2, dctl) == 'L') {
has_function_local_info = TRUE;
nchars2 = nchars;
/* Set the length for the scan below to stop just before "__L". */
nchars = (unsigned long)(p2 - p);
p2 += 3; /* Points to block number after "__L". */
nchars2 -= (unsigned long)(p2 - p);
/* Output the block number and function name. */
if (base_name_only) dctl->suppress_id_output++;
p2 = demangle_function_local_indication(p2, nchars2, &instance,
dctl);
if (base_name_only) dctl->suppress_id_output--;
break;
} /* if */
} /* if */
} /* for */
} /* if */
/* Demangle the name. */
p = demangle_name(p, nchars, stop_on_underscores,
nchars_left, (char *)NULL, temp_par_info,
&instance_emitted, dctl);
if (has_function_local_info) {
/* Don't write the instance number in cases where an unnamed type or
lambda has already emitted it. */
if (!instance_emitted && !base_name_only) emit_instance(instance, dctl);
p = p2;
if (nchars_left != NULL) *nchars_left = (unsigned long)(orig_end - p2);
} /* if */
dctl->end_of_name = prev_end;
return p;
} /* demangle_type_name_with_preceding_length */
static a_const_char *demangle_name_with_preceding_length(
a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Demangle a name with a preceding length (e.g., "3abc") and return a pointer
to the character position following what was demangled.
*/
{
ptr = demangle_type_name_with_preceding_length(
ptr,
/*base_name_only=*/TRUE,
(unsigned long)0,
(unsigned long *)NULL,
(a_template_param_block_ptr)NULL,
dctl);
return ptr;
} /* demangle_name_with_preceding_length */
static a_const_char *demangle_simple_type_name(
a_const_char *ptr,
a_boolean base_name_only,
a_template_param_block_ptr temp_par_info,
a_decode_control_block_ptr dctl)
/*
Demangle a type name (or namespace name) that can appear as part of a
nested name. Return a pointer to the character position following what
was demangled. The name is not a nested name, but it can have template
arguments. When temp_par_info != NULL, it points to a block that
controls output of extra information on template parameters.
When base_name_only is TRUE, suppress any function-local information.
*/
{
a_const_char *p = ptr;
if (get_char(p, dctl) == 'Z') {
/* A template parameter name. */
p = demangle_template_parameter_name(p, /*nontype=*/FALSE, dctl);
} else if (get_char(p, dctl) == 'G') {
/* A global scope indicator (e.g., ::A). This only occurs in the
context of a qualified name, so the caller will emit the requisite
"::" string (this is basically treated as a null qualifier). */
p++;
} else if (isdigit((unsigned char)get_char(p, dctl))) {
/* A simple mangled type name consists of digits indicating the length of
the name followed by the name itself, e.g., "3abc". */
p = demangle_type_name_with_preceding_length(p, base_name_only,
(unsigned long)0,
(unsigned long *)NULL,
temp_par_info, dctl);
} else {
/* Presumably a decltype or typeof. */
p = demangle_type(p, dctl);
} /* if */
return p;
} /* demangle_simple_type_name */
static a_const_char *full_demangle_type_name(
a_const_char *ptr,
a_boolean base_name_only,
a_template_param_block_ptr temp_par_info,
a_boolean is_destructor_name,
a_decode_control_block_ptr dctl)
/*
Demangle the type name at ptr and output the demangled form. Return a pointer
to the character position following what was demangled. The name can be
a simple type name or a nested type name, or the name of a namespace.
If base_name_only is TRUE, do not put out any nested type qualifiers,
e.g., put out "A::x" as simply "x". When temp_par_info != NULL, it
points to a block that controls output of extra information on template
parameters. Note that this routine is called for namespaces too
(the mangling is the same as for class names; you can't actually tell
the difference in a mangled name). If is_destructor_name is TRUE, this type is
actually the name of a destructor and an implied "~" should be emitted before
the last component of a qualified name (e.g., T::~X). See demangle_type_name
for an interface to this routine for the simple case.
*/
{
a_const_char *p = ptr;
unsigned long nquals;
if (get_char(p, dctl) == 'Q') {
/* A nested type name has the form
Q2_5outer5inner (outer::inner)
^-----^--------Names from outermost to innermost
^----------------Number of levels of qualification.
Note that the levels in the qualifier can be class names or namespace
names. */
p = get_number(p+1, &nquals, dctl);
p = advance_past_underscore(p, dctl);
/* Handle each level of qualification. */
for (; nquals > 0; nquals--) {
if (dctl->err_in_id) break; /* Avoid infinite loops on errors. */
/* Do not put out the nested type qualifiers if base_name_only is
TRUE. */
if (base_name_only && nquals != 1) dctl->suppress_id_output++;
if (is_destructor_name && nquals == 1) write_id_ch('~', dctl);
p = demangle_simple_type_name(p, base_name_only, temp_par_info, dctl);
if (nquals != 1) write_id_str("::", dctl);
if (base_name_only && nquals != 1) dctl->suppress_id_output--;
} /* for */
} else {
/* A simple (non-nested) type name. */
if (is_destructor_name) write_id_ch('~', dctl);
p = demangle_simple_type_name(p, base_name_only, temp_par_info, dctl);
} /* if */
return p;
} /* full_demangle_type_name */
static a_const_char *demangle_vtbl_class_name(a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Demangle a class or base class name that is one component of a virtual
function table name. Such names are mangled mostly as types, but with
a few special quirks.
*/
{
a_const_char *p = ptr, *prev_end;
unsigned long nchars, nchars_left;
/* This code handles both the base class part of the name and
the class part. A base class name has the form
<length> followed by one or more <class spec> optionally followed
by an ambiguity specification, __A optionally followed by a number.
The ambiguity specification is not included in the length.
A <class spec> is a class name mangling without preceding length, or
a "Q" nested-type-name specification.
A class name has the form
<length> <class spec>
or
Q nested-type-name specification (i.e., without preceding length).
*/
if (get_char(p, dctl) == 'Q') {
/* Nested-type-name "Q" without preceding length. This is used only
for the complete object class (the last section), not for the
base classes. */
p = demangle_type_name(p, dctl);
} else {
/* Get the length. */
p = get_length(p, &nchars, &prev_end, dctl);
while (!dctl->err_in_id) {
a_boolean nested_name_case = FALSE;
/* Check a "Q" nested-type-name specification by checking for "Q",
some digits, and an underscore. This rules out class names that
start with "Q". A class whose name starts with something like
"Q2_" is still going to be a problem, but that's a truly
ambiguous case. This is inherited from Cfront. */
if (get_char(p, dctl) == 'Q') {
a_const_char *p2 = p+1;
if (isdigit((unsigned char)get_char(p2, dctl))) {
do { p2++; } while (isdigit((unsigned char)get_char(p2, dctl)));
if (get_char(p2, dctl) == '_') {
nested_name_case = TRUE;
} /* if */
} /* if */
} /* if */
if (nested_name_case) {
/* Nested class name. */
a_const_char *end_ptr = demangle_type_name(p, dctl);
unsigned long chars_taken = (unsigned long)(end_ptr - p);
nchars -= chars_taken;
p = end_ptr;
} else {
/* Non-nested class name without preceding length. */
p = demangle_type_name_with_preceding_length(
p, /*base_name_only=*/FALSE,
nchars, &nchars_left,
(a_template_param_block_ptr)NULL,
dctl);
nchars = nchars_left;
} /* if */
/* Leave the loop if there is not another base class in the
derivation. */
if (nchars < 3 || !start_of_id_is("__", p, dctl)) break;
p += 2;
nchars -= 2;
write_id_str(" in ", dctl);
} /* while */
/* Make sure we took all the characters indicated by the length. */
if (nchars != 0) {
bad_mangled_name(dctl);
} /* if */
dctl->end_of_name = prev_end;
if (start_of_id_is("__A", p, dctl)) {
/* "__A" indicates an ambiguous base class. This is used only on
the base class specifications. */
write_id_str(" (ambiguous)", dctl);
p += 3;
/* Ignore the number following __A, if any. */
while (isdigit((unsigned char)get_char(p, dctl))) p++;
} /* if */
} /* if */
return p;
} /* demangle_vtbl_class_name */
static a_const_char *demangle_type_qualifiers(
a_const_char *ptr,
a_boolean trailing_space,
a_decode_control_block_ptr dctl)
/*
Demangle any type qualifiers (const/volatile/restrict) at the indicated
location. Return a pointer to the character position following what was
demangled. If trailing_space is TRUE, add a space at the end if any qualifiers
were put out.
*/
{
a_const_char *p = ptr;
a_boolean any_quals = FALSE;
for (;; p++) {
if (get_char(p, dctl) == 'C') {
if (any_quals) write_id_ch(' ', dctl);
write_id_str("const", dctl);
} else if (get_char(p, dctl) == 'V') {
if (any_quals) write_id_ch(' ', dctl);
write_id_str("volatile", dctl);
} else if (get_char(p, dctl) == 'D' && get_char(p+1, dctl) == 'r') {
if (any_quals) write_id_ch(' ', dctl);
write_id_str("restrict", dctl);
p++;
} else {
break;
} /* if */
any_quals = TRUE;
} /* for */
if (any_quals && trailing_space) write_id_ch(' ', dctl);
return p;
} /* demangle_type_qualifiers */
static a_const_char *demangle_ref_qualifiers(
a_const_char *p,
a_const_char **ref_qual,
a_decode_control_block_ptr dctl)
/*
The character preceding *p is an "F", indicating a function type; see if
there is a ref-qualifier, and if so, set *ref_qual to a string suitable for
output (set to NULL otherwise). Returns a pointer to the character position
following the optional ref-qualifier.
*/
{
*ref_qual = NULL;
if (get_char(p, dctl) == '_' && (get_char(p+1, dctl) == 'R')) {
p += 2;
*ref_qual = "&";
} else if (get_char(p, dctl) == '_' && (get_char(p+1, dctl) == 'E')) {
p += 2;
*ref_qual = "&&";
} /* if */
return p;
} /* demangle_ref_qualifiers */
static a_const_char *demangle_type_specifier(a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Demangle the type at ptr and output the specifier part. Return a pointer
to the character position following what was demangled.
*/
{
a_const_char *p = ptr, *s;
char ch;
/* Process type qualifiers. */
p = demangle_type_qualifiers(p, /*trailing_space=*/TRUE, dctl);
ch = get_char(p, dctl);
if (isdigit((unsigned char)ch) || ch == 'Q' || ch == 'Z') {
/* Named type, like class or enum, e.g., "3abc". */
p = demangle_type_name(p, dctl);
} else {
/* Builtin type. */
if (ch == 'a') {
/* GNU vector_size attribute. */
write_id_str("__attribute__((vector_size(", dctl);
p++;
/* Scan the size. */
while (ch = get_char(p, dctl), isdigit((unsigned char)ch)) {
write_id_ch(ch, dctl);
p++;
} /* while */
write_id_str("))) ", dctl);
/* The underlying type follows an underscore. */
p = advance_past_underscore(p, dctl);
ch = get_char(p, dctl);
} /* if */
/* Handle signed and unsigned, and _Complex. */
if (ch == 'S') {
write_id_str("signed ", dctl);
p++;
} else if (ch == 'U') {
write_id_str("unsigned ", dctl);
p++;
} else if (ch == 'x') {
write_id_str("_Complex ", dctl);
p++;
} /* if */
switch (get_char(p++, dctl)) {
case 'v':
s = "void";
break;
case 'c':
s = "char";
break;
case 'w':
s = "wchar_t";
break;
case 'b':
s = "bool";
break;
case 's':
s = "short";
break;
case 'i':
s = "int";
break;
case 'l':
s = "long";
break;
case 'L':
s = "long long";
break;
case 'f':
s = "float";
break;
case 'd':
s = "double";
break;
case 'r':
s = "long double";
break;
case 'm':
/* Microsoft intrinsic __intN types (Visual C++ 6.0 and later), as
well as GNU 128-bit integers (m16) and GNU __float80/__float128. */
switch (get_char(p++, dctl)) {
case '1':
if (get_char(p, dctl) == '6') {
s = "__int128";
p++;
} else {
s = "__int8";
} /* if */
break;
case '2':
s = "__int16";
break;
case '4':
s = "__int32";
break;
case '8':
s = "__int64";
break;
case 'f':
if (get_char(p++, dctl) == '1') {
switch (get_char(p++, dctl)) {
case '0':
s = "__float80";
break;
case '6':
s = "__float128";
break;
default:
bad_mangled_name(dctl);
s = "";
} /* switch */
} else {
bad_mangled_name(dctl);
s = "";
} /* if */
break;
default:
bad_mangled_name(dctl);
s = "";
} /* switch */
break;
case 'n':
s = "std::nullptr_t";
break;
case 'j':
s = "__nullptr";
break;
case 'u':
s = "auto";
break;
case 'q':
s = "decltype(auto)";
break;
case 'g':
s = "char16_t";
break;
case 'k':
s = "char32_t";
break;
case 't':
/* typeof(type) */
write_id_str("typeof(", dctl);
p = demangle_type(p, dctl);
s = ")";
break;
case 'p':
/* typeof(expression) */
write_id_str("typeof(", dctl);
p = demangle_expression(p, /*need_parens=*/FALSE, dctl);
s = ")";
break;
case 'y':
/* decltype of an id-expression or class member access. */
write_id_str("decltype(", dctl);
p = demangle_expression(p, /*need_parens=*/FALSE, dctl);
s = ")";
break;
case 'Y':
/* decltype of an expression. */
write_id_str("decltype((", dctl);
p = demangle_expression(p, /*need_parens=*/FALSE, dctl);
s = "))";
break;
case 'o':
/* __underlying_type(type) */
write_id_str("__underlying_type(", dctl);
p = demangle_type(p, dctl);
s = ")";
break;
default:
bad_mangled_name(dctl);
s = "";
} /* switch */
write_id_str(s, dctl);
} /* if */
return p;
} /* demangle_type_specifier */
static a_const_char *demangle_function_parameters(
a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Demangle the parameter list beginning at ptr and output the demangled form.
Return a pointer to the character position following what was demangled.
*/
{
a_const_char *p = ptr;
a_const_char *param_pos[10];
unsigned long curr_param_num, param_num, nreps;
a_boolean any_params = FALSE;
write_id_ch('(', dctl);
if (get_char(p, dctl) == 'v') {
/* Void parameter list. */
p++;
} else {
any_params = TRUE;
/* Loop for each parameter. */
curr_param_num = 1;
for (;;) {
char ch;
if (dctl->err_in_id) break; /* Avoid infinite loops on errors. */
ch = get_char(p, dctl);
if ((ch == 'T' && isdigit((unsigned char)get_char(p+1, dctl))) ||
ch == 'N') {
/* Tn means repeat the type of parameter "n". Note that a type can
begin with "Tr" (i.e., a C++/CLI tracking reference), so check for
a digit following the "T" to differentiate the two cases. */
/* Nmn means "m" repetitions of the type of parameter "n". "m"
is a one-digit number. */
/* "n" is also treated as a single-digit number; the front end enforces
that (in non-cfront object code compatibility mode). cfront does
not, which leads to some ambiguities when "n" is followed by
a class name. */
if (get_char(p++, dctl) == 'N') {
/* Get the number of repetitions. */
p = get_single_digit_number(p, &nreps, dctl);
} else {
nreps = 1;
} /* if */
/* Get the parameter number. */
p = get_single_digit_number(p, &param_num, dctl);
if (param_num < 1 || param_num >= curr_param_num ||
param_pos[param_num] == NULL) {
/* Parameter number out of range. */
bad_mangled_name(dctl);
goto end_of_routine;
} /* if */
/* Produce "nreps" copies of parameter "param_num". */
for (; nreps > 0; nreps--) {
if (dctl->err_in_id) break; /* Avoid infinite loops on errors. */
if (curr_param_num < 10) param_pos[curr_param_num] = NULL;
(void)demangle_type(param_pos[param_num], dctl);
if (nreps != 1) write_id_str(", ", dctl);
curr_param_num++;
} /* if */
} else {
/* A normal parameter. */
if (curr_param_num < 10) param_pos[curr_param_num] = p;
p = demangle_type(p, dctl);
curr_param_num++;
} /* if */
/* Stop after the last parameter. */
ch = get_char(p, dctl);
if (ch == '\0' || ch == 'e' || ch == '_' || ch == 'F') break;
write_id_str(", ", dctl);
} /* for */
} /* if */
if (get_char(p, dctl) == 'e') {
/* Ellipsis. */
if (any_params) write_id_str(", ", dctl);
write_id_str("...", dctl);
p++;
} /* if */
write_id_ch(')', dctl);
end_of_routine:
return p;
} /* demangle_function_parameters */
static a_const_char *skip_extern_C_indication(a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
ptr points to the character after the "F" of a function type. Skip over
and ignore an indication of extern "C" following the "F", if one is present.
Return a pointer to the character following the extern "C" indication.
There's no syntax for representing the extern "C" in the function type, so
just ignore it.
*/
{
if (get_char(ptr, dctl) == 'K') ptr++;
return ptr;
} /* skip_extern_C_indication */
static a_const_char *demangle_type_first_part(
a_const_char *ptr,
a_boolean under_lhs_declarator,
a_boolean need_trailing_space,
a_decode_control_block_ptr dctl)
/*
Demangle the type at ptr and output the specifier part and the part of the
declarator that precedes the name. Return a pointer to the character
position following what was demangled. If under_lhs_declarator is TRUE,
this type is directly under a type that uses a left-side declarator,
e.g., a pointer type. (That's used to control use of parentheses around
parts of the declarator.) If need_trailing_space is TRUE, put a space
at the end of the type first part (needed if the declarator part is
not empty, because it contains a name or a derived type).
*/
{
a_const_char *p = ptr, *qualp = p;
char kind, ext_kind;
/* Remove type qualifiers. */
p = remove_immediate_type_qualifiers(p, dctl);
kind = get_char(p, dctl);
if (kind == 'P' || kind == 'R' || kind == 'E' || kind == 'H') {
a_boolean need_space = TRUE;
/* Pointer, reference, rvalue reference, or C++/CLI pointer-like type.
For example, "Pc" is pointer to char. */
if (kind == 'H') {
/* Some kind of C++/CLI pointer-like type (handle, tracking reference,
interior_ptr, pin_ptr). */
p++;
ext_kind = get_char(p, dctl);
if (ext_kind == 'i') {
write_id_str("interior_ptr<", dctl);
need_space = FALSE;
} else if (ext_kind == 'p') {
write_id_str("pin_ptr<", dctl);
need_space = FALSE;
} /* if */
} /* if */
p = demangle_type_first_part(p+1, /*under_lhs_declarator=*/TRUE,
need_space, dctl);
/* Output "*" (pointer), "&" (reference), "&&" (rvalue reference),
"^" (handle), or "%" (tracking reference). */
if (kind == 'R') {
write_id_ch('&', dctl);
} else if (kind == 'E') {
write_id_str("&&", dctl);
} else if (kind == 'H') {
if (ext_kind == 'h') {
write_id_ch('^', dctl);
} else if (ext_kind == 't') {
write_id_ch('%', dctl);
} else if (ext_kind == 'i') {
write_id_ch('>', dctl);
} else if (ext_kind == 'p') {
write_id_ch('>', dctl);
} else {
bad_mangled_name(dctl);
} /* if */
} else {
write_id_ch('*', dctl);
} /* if */
/* Output the type qualifiers on the pointer, if any. */
(void)demangle_type_qualifiers(qualp, need_trailing_space, dctl);
} else if (kind == 'M') {
/* Pointer-to-member type, e.g., "M1Ai" is pointer to member of A of
type int. */
a_const_char *classp = p+1;
/* Skip over the class name. */
dctl->suppress_id_output++;
p = demangle_type_name(classp, dctl);
dctl->suppress_id_output--;
p = demangle_type_first_part(p, /*under_lhs_declarator=*/TRUE,
/*need_trailing_space=*/TRUE, dctl);
/* Output Classname::*. */
(void)demangle_type_name(classp, dctl);
write_id_str("::*", dctl);
/* Output the type qualifiers on the pointer, if any. */
(void)demangle_type_qualifiers(qualp, need_trailing_space, dctl);
} else if (kind == 'F') {
/* Function type, e.g., "Fii_f" is function(int, int) returning float.
The return type is not present for top-level function types (except
for template functions). */
p++;
/* An optional ref-qualifier is indicated if the 'F' is followed by
an underscore. Skip it on this pass. */
if (get_char(p, dctl) == '_' &&
(get_char(p+1, dctl) == 'R' || get_char(p+1, dctl) == 'E')) {
p += 2;
} /* if */
/* An optional exception specification. Skip it on this pass. */
if (get_char(p, dctl) == 'D' &&
(get_char(p+1, dctl) == 'o' || get_char(p+1, dctl) == 'O')) {
if (get_char(p+1, dctl) == 'O') {
dctl->suppress_id_output++;
p = demangle_constant(p+2, /*suppress_address_of=*/FALSE,
/*need_parens=*/FALSE, dctl);
dctl->suppress_id_output--;
} else {
p += 2;
} /* if */
} /* if */
p = skip_extern_C_indication(p, dctl);
/* Skip over the parameter types without outputting anything. */
dctl->suppress_id_output++;
p = demangle_function_parameters(p, dctl);
dctl->suppress_id_output--;
if (get_char(p, dctl) == '_' && get_char(p+1, dctl) != '_') {
/* The return type is present. */
p = demangle_type_first_part(p+1, /*under_lhs_declarator=*/FALSE,
/*need_trailing_space=*/TRUE, dctl);
} /* if */
/* This is a right-side declarator, so if it's under a left-side declarator
parentheses are needed. */
if (under_lhs_declarator) write_id_ch('(', dctl);
} else if (kind == 'A') {
/* Array type, e.g., "A10_i" is array[10] of int. */
p++;
if (get_char(p, dctl) == '_') {
/* Length is specified by a constant expression based on template
parameters. Ignore the expression. */
p++;
dctl->suppress_id_output++;
p = demangle_constant(p, /*suppress_address_of=*/FALSE,
/*need_parens=*/FALSE, dctl);
dctl->suppress_id_output--;
} else if (get_char(p, dctl) == 'O') {
/* Length is specified as an expression. */
dctl->suppress_id_output++;
p = demangle_expression(p, /*need_parens=*/FALSE, dctl);
dctl->suppress_id_output--;
} else {
/* Normal constant number of elements. */
/* Skip the array size. */
while (isdigit((unsigned char)get_char(p, dctl))) p++;
} /* if */
p = advance_past_underscore(p, dctl);
/* Process the element type. */
p = demangle_type_first_part(p, /*under_lhs_declarator=*/FALSE,
/*need_trailing_space=*/TRUE, dctl);
/* This is a right-side declarator, so if it's under a left-side declarator
parentheses are needed. */
if (under_lhs_declarator) write_id_ch('(', dctl);
} else if (kind == 'D') {
/* The 'D' is used as an "escape" character. The following character
determines the actual action to be taken. */
p++;
kind = get_char(p, dctl);
if (kind == 'p') {
/* A pack expansion. */
p = demangle_type_first_part(p+1, /*under_lhs_declarator=*/FALSE,
/*need_trailing_space=*/FALSE, dctl);
} else {
bad_mangled_name(dctl);
} /* if */
} else {
/* No declarator part to process. Handle the specifier type. */
p = demangle_type_specifier(qualp, dctl);
if (need_trailing_space) write_id_ch(' ', dctl);
} /* if */
return p;
} /* demangle_type_first_part */
static void demangle_type_second_part(
a_const_char *ptr,
a_boolean under_lhs_declarator,
a_decode_control_block_ptr dctl)
/*
Demangle the type at ptr and output the part of the declarator that follows the
name. This routine does not return a pointer to the character position
following what was demangled; it is assumed that the caller will save
that from the call of demangle_type_first_part, and it saves a lot of
time if this routine can avoid scanning the specifiers again.
If under_lhs_declarator is TRUE, this type is directly under a type that
uses a left-side declarator, e.g., a pointer type. (That's used to control
use of parentheses around parts of the declarator.)
*/
{
a_const_char *p = ptr, *qualp = p;
char kind;
/* Remove type qualifiers. */
p = remove_immediate_type_qualifiers(p, dctl);
kind = get_char(p, dctl);
if (kind == 'P' || kind == 'R' || kind == 'E' || kind == 'H') {
/* Pointer, reference, rvalue reference, or C++/CLI pointer-like type.
For example, "Pc" is pointer to char. */
/* If it's a C++/CLI pointer-like type, there's a second character after
the "H", but we ignore that here. */
if (kind == 'H') p++;
demangle_type_second_part(p+1, /*under_lhs_declarator=*/TRUE, dctl);
} else if (kind == 'M') {
/* Pointer-to-member type, e.g., "M1Ai" is pointer to member of A of
type int. */
/* Advance over the class name. */
dctl->suppress_id_output++;
p = demangle_type_name(p+1, dctl);
dctl->suppress_id_output--;
demangle_type_second_part(p, /*under_lhs_declarator=*/TRUE, dctl);
} else if (kind == 'F') {
a_const_char *ref_qual, *save_noexcept_constant = NULL;
a_boolean is_noexcept = FALSE;
/* Function type, e.g., "Fii_f" is function(int, int) returning float.
The return type is not present for top-level function types (except
for template functions). */
/* This is a right-side declarator, so if it's under a left-side declarator
parentheses are needed. */
if (under_lhs_declarator) write_id_ch(')', dctl);
p++;
/* An optional ref-qualifier is indicated if the 'F' is followed by
an underscore. Emit the ref-qualifier at the end of the type. */
p = demangle_ref_qualifiers(p, &ref_qual, dctl);
/* An optional exception specification. */
if (get_char(p, dctl) == 'D' &&
(get_char(p+1, dctl) == 'o' || get_char(p+1, dctl) == 'O')) {
if (get_char(p+1, dctl) == 'O') {
dctl->suppress_id_output++;
save_noexcept_constant = p+2;
p = demangle_constant(save_noexcept_constant,
/*suppress_address_of=*/FALSE,
/*need_parens=*/FALSE, dctl);
dctl->suppress_id_output--;
} else {
is_noexcept = TRUE;
p += 2;
} /* if */
} /* if */
p = skip_extern_C_indication(p, dctl);
/* Put out the parameter types. */
p = demangle_function_parameters(p, dctl);
/* Put out any cv-qualifiers (member functions). */
/* Note that such things could come up on nonmember functions in the
presence of typedefs. In such a case what we generate here will not
be valid C, but it's a reasonable representation of the mangled
type, and there's no way of getting the typedef name in there,
so let it be. */
if (*qualp != 'F') {
write_id_ch(' ', dctl);
(void)demangle_type_qualifiers(qualp, /*trailing_space=*/FALSE, dctl);
} /* if */
if (get_char(p, dctl) == '_' && get_char(p+1, dctl) != '_') {
/* Process the return type. */
demangle_type_second_part(p+1, /*under_lhs_declarator=*/FALSE, dctl);
} /* if */
if (ref_qual != NULL) write_id_str(ref_qual, dctl);
if (is_noexcept) {
write_id_str(" noexcept", dctl);
} else if (save_noexcept_constant != NULL) {
write_id_str(" noexcept(", dctl);
(void)demangle_constant(save_noexcept_constant,
/*suppress_address_of=*/FALSE,
/*need_parens=*/FALSE, dctl);
write_id_ch(')', dctl);
} /* if */
} else if (kind == 'A') {
/* Array type, e.g., "A10_i" is array[10] of int. */
/* This is a right-side declarator, so if it's under a left-side declarator
parentheses are needed. */
if (under_lhs_declarator) write_id_ch(')', dctl);
write_id_ch('[', dctl);
p++;
if (get_char(p, dctl) == '_') {
/* Length is specified by a constant expression based on template
parameters. */
p++;
p = demangle_constant(p, /*suppress_address_of=*/FALSE,
/*need_parens=*/FALSE, dctl);
} else if (get_char(p, dctl) == 'O') {
/* Length is specified as an expression. */
p = demangle_expression(p, /*need_parens=*/FALSE, dctl);
} else {
/* Normal constant number of elements. */
if (get_char(p, dctl) == '0' && get_char(p+1, dctl) == '_') {
/* Size is zero, so do not put out a size (the result is "[]"). */
p++;
} else {
/* Put out the array size. */
while (isdigit((unsigned char)get_char(p, dctl))) {
write_id_ch(*p++, dctl);
} /* while */
} /* if */
} /* if */
p = advance_past_underscore(p, dctl);
write_id_ch(']', dctl);
/* Process the element type. */
demangle_type_second_part(p, /*under_lhs_declarator=*/FALSE, dctl);
} else if (kind == 'D') {
/* The 'D' is used as an "escape" character. The following character
determines the actual action to be taken. */
p++;
kind = get_char(p, dctl);
if (kind == 'p') {
/* A pack expansion. */
p++;
write_id_str("...", dctl);
demangle_type_second_part(p, /*under_lhs_declarator=*/FALSE, dctl);
} else {
bad_mangled_name(dctl);
} /* if */
} else {
/* No declarator part to process. No need to scan the specifiers type --
it was done by demangle_type_first_part. */
} /* if */
} /* demangle_type_second_part */
static a_const_char *demangle_type(a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Demangle the type at ptr and output the demangled form. Return a pointer to
the character position following what was demangled.
*/
{
a_const_char *p;
/* Generate the specifier part of the type. */
p = demangle_type_first_part(ptr, /*under_lhs_declarator=*/FALSE,
/*need_trailing_space=*/FALSE, dctl);
/* Generate the declarator part of the type. */
demangle_type_second_part(ptr, /*under_lhs_declarator=*/FALSE, dctl);
return p;
} /* demangle_type */
static a_const_char *demangle_identifier_with_preceding_length(
a_const_char *ptr,
a_boolean suppress_parent_and_local_info,
a_decode_control_block_ptr dctl)
/*
Demangle the identifier at ptr and output the demangled form. The
identifier is preceded by a length. Return a pointer to the character
position following what was demangled. An identifier can include template
argument, parent, and function-local information.
If suppress_parent_and_local_info is TRUE, do not output parent and
function-local information if present (but do scan over it).
*/
{
a_const_char *p = ptr, *prev_end;
unsigned long nchars;
p = get_length(p, &nchars, &prev_end, dctl);
dctl->mangling_nesting_level++;
p = full_demangle_identifier(p, nchars, suppress_parent_and_local_info,
dctl);
dctl->mangling_nesting_level--;
dctl->end_of_name = prev_end;
return p;
} /* demangle_identifier_with_preceding_length */
static a_const_char *full_demangle_identifier(
a_const_char *ptr,
unsigned long nchars,
a_boolean suppress_parent_and_local_info,
a_decode_control_block_ptr dctl)
/*
Demangle the identifier at ptr and output the demangled form. Return
a pointer to the character position following what was demangled.
If nchars > 0, take no more than that many characters.
If suppress_parent_and_local_info is TRUE, do not output parent
and function-local information if present (but do scan over it).
An identifier can include template argument, parent, and function-local
information.
*/
{
a_const_char *p = ptr, *pname, *end_ptr, *function_local_end_ptr = NULL;
a_const_char *final_specialization, *end_ptr_first_scan, *prev_end = NULL;
char ch;
a_const_char *oname;
a_boolean is_function = TRUE;
a_template_param_block
temp_par_info;
a_boolean is_externalized_static = FALSE;
a_boolean has_function_local_info = FALSE;
unsigned long instance;
clear_template_param_block(&temp_par_info);
if (nchars != 0) {
prev_end = dctl->end_of_name;
dctl->end_of_name = ptr + nchars;
} /* if */
if (start_of_id_is("__STF__", ptr, dctl)) {
/* Static function made external by addition of prefix "__STF__" and
suffix of module id. */
is_externalized_static = TRUE;
/* Advance past __STF__. */
ptr += 7;
if (nchars != 0) nchars -= 7;
p = ptr;
} /* if */
/* Scan through the name (the first part of the mangled name) without
generating output, to see what's beyond it. Special processing is
necessary for names of constructors, conversion routines, etc. */
/* If the name has a specialization indication in it (which can happen for
function names), note that fact. */
temp_par_info.set_final_specialization = TRUE;
dctl->suppress_id_output++;
p = demangle_name(ptr, nchars, /*stop_on_underscores=*/TRUE,
(unsigned long *)NULL,
(char *)NULL, &temp_par_info,
(a_boolean *)NULL, dctl);
dctl->suppress_id_output--;
final_specialization = temp_par_info.final_specialization;
clear_template_param_block(&temp_par_info);
temp_par_info.final_specialization = final_specialization;
if (get_char(p, dctl) == '\0') {
/* There is no mangled part of the name. This happens for strange
cases like
extern "C" int operator +(A, A);
which gets mangled as "__pl". Just write out the name and stop. */
end_ptr = demangle_name(ptr, nchars,
/*stop_on_underscores=*/TRUE,
(unsigned long *)NULL,
(char *)NULL,
(a_template_param_block_ptr)NULL,
(a_boolean *)NULL, dctl);
} else {
/* There's more. There should be a "__" between the name and the
additional mangled information. */
if (get_char(p, dctl) != '_' || get_char(p+1, dctl) != '_') {
bad_mangled_name(dctl);
end_ptr = p;
goto end_of_routine;
} /* if */
end_ptr = p + 2;
/* Now ptr points to the original-name part of the mangled name, and
end_ptr points to the mangled-name part at the end.
f__1AFv
^---- end_ptr
^------- ptr
The mangled-name part is
(a) A class name for a static data member.
(b) A class name followed by "F" followed by the encoding for the
parameter types for a member function.
(c) "F" followed by the encoding for the parameter types for a
nonmember function.
(d) "L" plus a local block number, followed by the mangled function
name, for a function-local entity.
Members of namespaces are encoded similarly. */
p = end_ptr;
pname = NULL;
if (suppress_parent_and_local_info) dctl->suppress_id_output++;
ch = get_char(end_ptr, dctl);
if (ch == 'L') {
unsigned long nchars2 = nchars;
/* The name of an entity within a function, mangled on promotion out
of the function. For example, "i__L1__f__Fv" for "i" from block 1
of function "f(void)". Note that this is not the same mangling
used by cfront (in the cfront scheme, the __L1 is at the end, and
the number is different). */
/* Set a length for the name without the function-local indication,
for the processing in the rest of this routine. */
nchars = (unsigned long)((p - 2) - ptr);
/* Demangle the function name and block number. */
p++; /* Points to the block number following "__L". */
if (nchars2 != 0) nchars2 -= (unsigned long)(p - ptr);
function_local_end_ptr =
demangle_function_local_indication(p, nchars2, &instance, dctl);
has_function_local_info = TRUE;
p = end_ptr = ptr + nchars;
is_function = FALSE;
/* Go on to demangle the name of the local entity. */
} else if (ch != 'F') {
/* A class (or namespace) name must be next. */
/* Remember the location of the parent entity name. */
pname = end_ptr;
/* Scan over the class name, producing no output, and remembering the
position of the final specialization, if any. If we already
found a specialization on the function name, it's the final one
and we shouldn't change it. */
dctl->suppress_id_output++;
if (temp_par_info.final_specialization == NULL) {
temp_par_info.set_final_specialization = TRUE;
} /* if */
end_ptr = full_demangle_type_name(pname, /*base_name_only=*/FALSE,
&temp_par_info,
/*is_destructor_name=*/FALSE,
dctl);
temp_par_info.set_final_specialization = FALSE;
dctl->suppress_id_output--;
/* If the name ends here, this is a simple member (e.g., a static
data member). */
ch = get_char(end_ptr, dctl);
if (ch == '\0' ||
(ch == '_' && get_char(end_ptr+1, dctl) == '_')) {
is_function = FALSE;
} /* if */
} /* if */
if (suppress_parent_and_local_info) dctl->suppress_id_output--;
oname = NULL;
if (is_function) {
/* "S" here means a static member function (ignore). */
if (get_char(end_ptr, dctl) == 'S') end_ptr++;
/* "O" here means the base class of a function that this function
explicitly overrides (a Microsoft extension) is next. */
if (get_char(end_ptr, dctl) == 'O') {
/* Skip over the class name, producing no output. Remember its
position for later output. */
oname = ++end_ptr;
dctl->suppress_id_output++;
end_ptr = demangle_type_name(oname, dctl);
dctl->suppress_id_output--;
} /* if */
/* Write the specifier part of the type. */
end_ptr_first_scan =
demangle_type_first_part(end_ptr,
/*under_lhs_declarator=*/FALSE,
/*need_trailing_space=*/TRUE, dctl);
} /* if */
temp_par_info.nesting_level = 0;
if (pname != NULL &&
!suppress_parent_and_local_info) {
/* Write the parent class or namespace qualifier. */
if (temp_par_info.final_specialization != NULL) {
/* Up to the final specialization, put out actual template arguments
for specializations. */
temp_par_info.actual_template_args_until_final_specialization = TRUE;
} /* if */
(void)full_demangle_type_name(pname, /*base_name_only=*/FALSE,
&temp_par_info,
/*is_destructor_name=*/FALSE,
dctl);
/* Force template parameter information out on the function even if
it is specialized. */
temp_par_info.actual_template_args_until_final_specialization = FALSE;
write_id_str("::", dctl);
} /* if */
/* Write the name of the member. */
(void)demangle_name(ptr, nchars, /*stop_on_underscores=*/TRUE,
(unsigned long *)NULL,
pname, &temp_par_info,
(a_boolean *)NULL, dctl);
if (oname != NULL) {
/* Put out the name of the class of the function explicitly overridden,
if noted above. */
write_id_str(" [overriding function in ", dctl);
(void)demangle_type_name(oname, dctl);
write_id_str("] ", dctl);
} /* if */
if (is_function) {
/* Write the declarator part of the type. */
demangle_type_second_part(end_ptr, /*under_lhs_declarator=*/FALSE,
dctl);
end_ptr = end_ptr_first_scan;
} /* if */
if (!temp_par_info.use_old_form_for_template_output &&
temp_par_info.nesting_level != 0) {
/* Put out correspondences for template parameters, e.g, "T=int". */
temp_par_info.nesting_level = 0;
temp_par_info.first_correspondence = TRUE;
temp_par_info.output_only_correspondences = TRUE;
/* Output is suppressed in general, and turned on only where
appropriate. */
dctl->suppress_id_output++;
if (pname != NULL) {
/* Write the parent class or namespace qualifier. */
if (temp_par_info.final_specialization != NULL) {
/* Up to the final specialization, put out actual template arguments
for specializations. */
temp_par_info.actual_template_args_until_final_specialization = TRUE;
} /* if */
(void)full_demangle_type_name(pname, /*base_name_only=*/FALSE,
&temp_par_info,
/*is_destructor_name=*/FALSE,
dctl);
} /* if */
/* Force template parameter information out on the function even if
it is specialized. */
temp_par_info.actual_template_args_until_final_specialization = FALSE;
/* Write the name of the member. */
(void)demangle_name(ptr, nchars, /*stop_on_underscores=*/TRUE,
(unsigned long *)NULL,
pname, &temp_par_info,
(a_boolean *)NULL, dctl);
dctl->suppress_id_output--;
if (!temp_par_info.first_correspondence) {
/* End the list of correspondences. */
write_id_ch(']', dctl);
} /* if */
} /* if */
} /* if */
end_of_routine:
/* If the identifier had local function information, write the instance
number now. */
if (has_function_local_info) emit_instance(instance, dctl);
/* When a function-local indication is scanned, end_ptr has been set
to the end of the local entity name, and needs to be set to after the
function-local indication at the end of the whole name. */
if (function_local_end_ptr != NULL) end_ptr = function_local_end_ptr;
if (is_externalized_static) {
/* Advance over the module id part of the name. */
while (get_char(end_ptr, dctl) != '\0') end_ptr++;
} /* if */
if (prev_end != NULL) dctl->end_of_name = prev_end;
return end_ptr;
} /* full_demangle_identifier */
static a_boolean is_mangled_type_name(a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Return TRUE if the encoding beginning at ptr appears to be a mangled
type name. This is used to distinguish a local mangled non-nested
type name with template arguments (e.g., __15MyTemp__tm__2_i) from a
cfront-style local name (e.g., __2name); the character passed in is
the one after the double underscore.
*/
{
a_boolean is_type_name = FALSE;
a_const_char *p = ptr;
if (isdigit((unsigned char)get_char(p, dctl))) {
/* Skip over the number. */
do { p++; } while (isdigit((unsigned char)get_char(p, dctl)));
/* The next character is typically alphabetic. */
if (isalpha((unsigned char)get_char(p, dctl))) {
/* This doesn't have to be a full recognizer; it just has to distinguish
the two cases given above. To do that, look for the double underscore
that must appear in a mangled name that has template arguments. */
for (p++; get_char(p, dctl) != '\0'; p++) {
if (get_char(p, dctl) == '_' && get_char(p+1, dctl) == '_') {
is_type_name = TRUE;
break;
} /* if */
} /* for */
} else if (get_char(p, dctl) == '_' &&
get_char(p+1, dctl) == '_' &&
get_char(p+2, dctl) == 'U' &&
(get_char(p+3, dctl) == 't' ||
get_char(p+3, dctl) == 'l' ||
get_char(p+3, dctl) == 'm' ||
get_char(p+3, dctl) == 'd')) {
/* An unnamed type or lambda. */
is_type_name = TRUE;
} /* if */
} /* if */
return is_type_name;
} /* is_mangled_type_name */
static a_const_char *demangle_static_variable_name(
a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Demangle the name of a static variable promoted to being external by
addition of a prefix "__STV__" and a suffix of a module id. Just put out
the part in the middle, which is the original name.
*/
{
a_const_char *start_ptr;
ptr += 7; /* Move to after "__STV__". */
/* Copy the name until "__". */
start_ptr = ptr;
while (get_char(ptr, dctl) != '_' ||
get_char(ptr+1, dctl) != '_' ||
ptr == start_ptr) {
if (get_char(ptr, dctl) == '\0') {
bad_mangled_name(dctl);
break;
} /* if */
write_id_ch(*ptr, dctl);
ptr++;
} /* while */
/* Advance over the module id part of the name. */
while (get_char(ptr, dctl) != '\0') ptr++;
return ptr;
} /* demangle_static_variable_name */
static a_const_char *demangle_local_name(a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Demangle the local name at ptr and output the demangled form. Return
a pointer to the character position following what was demangled.
This demangles the "__nn_mm_name" form produced by the C-generating
back end. This is not something visible unless the C-generating back end
is used, and it's a local name, which is ordinarily outside the charter
of these demangling routines, but it's an easy and common case, so...
Also handles the cfront-style __nnName form.
*/
{
a_const_char *p = ptr+2;
/* Check for the initial two numbers and underscores. The caller checked
for the two initial underscores and the digit following that. */
do { p++; } while (isdigit((unsigned char)get_char(p, dctl)));
if (isalpha((unsigned char)get_char(p, dctl))) {
/* Cfront-style local name, like "__2name". */
} else {
if (get_char(p, dctl) != '_') {
bad_mangled_name(dctl);
goto end_of_routine;
} /* if */
p++;
if (!isdigit((unsigned char)get_char(p, dctl))) {
bad_mangled_name(dctl);
goto end_of_routine;
} /* if */
do { p++; } while (isdigit((unsigned char)get_char(p, dctl)));
if (get_char(p, dctl) != '_') {
bad_mangled_name(dctl);
goto end_of_routine;
} /* if */
p++;
} /* if */
/* Copy the rest of the string to output. */
while (get_char(p, dctl) != '\0') {
write_id_ch(*p, dctl);
p++;
} /* while */
end_of_routine:
return p;
} /* demangle_local_name */
static a_const_char *uncompress_mangled_name(a_const_char *id,
a_decode_control_block_ptr dctl)
/*
Uncompress the compressed mangled name beginning at id. Return the
address of the uncompressed name.
*/
{
a_const_char *uncompressed_name = id, *src_end = dctl->end_of_name;
unsigned long length;
/* Advance past "__CPR". */
id += 5;
/* Accumulate the length of the uncompressed name. */
if (!isdigit((unsigned char)*id)) {
bad_mangled_name(dctl);
goto end_of_routine;
} /* if */
id = get_number(id, &length, dctl);
/* Check for the two underscores following the length. */
if (id[0] != '_' || id[1] != '_') {
bad_mangled_name(dctl);
goto end_of_routine;
} /* if */
/* Save the uncompressed length so it can be used later in telling the
caller how big a buffer is required. */
dctl->uncompressed_length = length;
id += 2;
if (length+1 >= dctl->output_id_size) {
/* The buffer supplied by the caller is too small to contain the
uncompressed name. */
dctl->output_overflow_err = TRUE;
goto end_of_routine;
} else {
a_const_char *src, *dst_end = dctl->output_id+dctl->output_id_size;
char *dst;
/* Uncompress to the end of the buffer supplied by the caller, then
do the demangling in the space remaining at the beginning. */
uncompressed_name = dst_end-(length+1);
dctl->output_id_size -= length+1;
dst = (char *)uncompressed_name;
for (src = id; *src != '\0';) {
char ch = *src++;
if (ch != 'J') {
/* Just copy this character. */
if (dst >= dst_end) {
/* Overflowed buffer (probably malformed input). */
bad_mangled_name(dctl);
goto end_of_routine;
} /* if */
*dst++ = ch;
} else {
if (*src == 'J') {
/* "JJ" indicates a simple "J". */
/* Simple "J". */
if (dst >= dst_end) {
/* Overflowed buffer (probably malformed input). */
bad_mangled_name(dctl);
goto end_of_routine;
} /* if */
*dst++ = 'J';
} else {
/* "JnnnJ" indicates a repetition of a string that appeared
earlier, at position "nnn". */
unsigned long pos, prev_len;
a_const_char *prev_str, *prev_str2, *prev_end;
dctl->end_of_name = src_end;
src = get_number(src, &pos, dctl);
if (*src != 'J' || pos > length) {
bad_mangled_name(dctl);
goto end_of_routine;
} /* if */
prev_str = uncompressed_name+pos;
if (!isdigit(*prev_str)) {
bad_mangled_name(dctl);
goto end_of_routine;
} /* if */
/* Get the length of the repeated string. */
dctl->end_of_name = uncompressed_name + length;
prev_str2 = get_length(prev_str, &prev_len, &prev_end, dctl);
/* Copy the repeated string to the uncompressed output. */
prev_str2 += prev_len;
if (dst+prev_len >= dst_end) {
/* Overflowed buffer (probably malformed input). */
bad_mangled_name(dctl);
goto end_of_routine;
} /* if */
while (prev_str < prev_str2) *dst++ = *prev_str++;
} /* if */
/* Advance past the final "J". */
src++;
} /* if */
} /* for */
if (dst - uncompressed_name != length) {
/* The length didn't come out right. */
bad_mangled_name(dctl);
} /* if */
if (dst >= dst_end) {
/* Overflowed buffer (probably malformed input). */
bad_mangled_name(dctl);
goto end_of_routine;
} /* if */
/* Add the final null. */
*dst++ = '\0';
dctl->end_of_name = uncompressed_name + length;
} /* if */
end_of_routine:;
return uncompressed_name;
} /* uncompress_mangled_name */
void decode_identifier(a_const_char *id,
char *output_buffer,
sizeof_t output_buffer_size,
a_boolean *err,
a_boolean *buffer_overflow_err,
sizeof_t *required_buffer_size)
/*
Demangle the identifier id (which is null-terminated), and put the demangled
form (null-terminated) into the output_buffer provided by the caller.
output_buffer_size gives the allocated size of output_buffer. If there
is some error in the demangling process, *err will be returned TRUE.
In addition, if the error is that the output buffer is too small,
*buffer_overflow_err will (also) be returned TRUE, and *required_buffer_size
is set to the size of buffer required to do the demangling. Note that
if the mangled name is compressed, and the buffer size is smaller than
the size of the uncompressed mangled name, the size returned will be
enough to uncompress the name but not enough to produce the demangled form.
The caller must be prepared in that case to loop a second time (the
length returned the second time will be correct).
*/
{
a_const_char *end_ptr, *p;
a_decode_control_block control_block;
a_decode_control_block_ptr dctl = &control_block;
clear_control_block(dctl);
dctl->end_of_name = strchr(id, '\0');
dctl->output_id = output_buffer;
dctl->output_id_size = output_buffer_size;
if (start_of_id_is("__CPR", id, dctl)) {
/* Uncompress a compressed name. */
id = uncompress_mangled_name(id, dctl);
} /* if */
/* Check for special cases. */
if (dctl->output_overflow_err) {
/* Previous error (not enough room in the buffer to uncompress). */
} else if (dctl->err_in_id) {
/* Invalid compressed input. */
} else if (start_of_id_is("__vtbl__", id, dctl)) {
write_id_str("virtual function table for ", dctl);
/* The overall mangled name is one of
__vtbl__ <class mangling>
__vtbl__ <base class mangling> __ <class mangling>
__vtbl__ <base class mangling> __ <base class mangling>
__ <class mangling>
*/
end_ptr = demangle_vtbl_class_name(id+8, dctl);
while (start_of_id_is("__", end_ptr, dctl)) {
/* Further derived class. */
end_ptr += 2;
write_id_str(" in ", dctl);
end_ptr = demangle_vtbl_class_name(end_ptr, dctl);
} /* while */
} else if (start_of_id_is("__CBI__", id, dctl)) {
write_id_str("can-be-instantiated flag for ", dctl);
end_ptr = demangle_identifier(id+7, dctl);
} else if (start_of_id_is("__DNI__", id, dctl)) {
write_id_str("do-not-instantiate flag for ", dctl);
end_ptr = demangle_identifier(id+7, dctl);
} else if (start_of_id_is("__TIR__", id, dctl)) {
write_id_str("template-instantiation-request flag for ", dctl);
end_ptr = demangle_identifier(id+7, dctl);
} else if (start_of_id_is("__LSG__", id, dctl)) {
write_id_str("initialization guard variable for ", dctl);
end_ptr = demangle_identifier(id+7, dctl);
} else if (start_of_id_is("__THI__", id, dctl)) {
write_id_str("thread_local initialization routine for ", dctl);
end_ptr = demangle_identifier(id+7, dctl);
} else if (start_of_id_is("__IFV__", id, dctl)) {
id += 7;
write_id_str("ifunc variable for ", dctl);
if (start_of_id_is("__IFC__", id, dctl)) {
/* This can be nested (and this routine isn't recursive). */
write_id_str("ifunc function for ", dctl);
id += 7;
} /* if */
end_ptr = demangle_identifier(id, dctl);
} else if (start_of_id_is("__RES__", id, dctl)) {
id += 7;
write_id_str("resolver function for ", dctl);
if (start_of_id_is("__IFC__", id, dctl)) {
/* This can be nested (and this routine isn't recursive). */
write_id_str("ifunc function for ", dctl);
id += 7;
} /* if */
end_ptr = demangle_identifier(id, dctl);
} else if (start_of_id_is("__IFC__", id, dctl)) {
write_id_str("ifunc function for ", dctl);
end_ptr = demangle_identifier(id+7, dctl);
} else if (start_of_id_is("__TGT__", id, dctl)) {
/* A "target" attribute: look for closing "__". */
a_const_char *target_attr, *target_attr_end;
id += 7;
target_attr = id;
while (id < dctl->end_of_name) {
if (start_of_id_is("__", id, dctl)) {
target_attr_end = id;
id += 2;
break;
} /* if */
id++;
} /* while */
if (id < dctl->end_of_name) {
end_ptr = demangle_identifier(id, dctl);
/* Emit the "target" attribute. Note that this isn't exactly in the
same format as the input (commas and periods have been replaced by
underscores), but it should convey the idea. */
write_id_str(" __attribute__((target(", dctl);
while (target_attr < target_attr_end) {
write_id_ch(*target_attr++, dctl);
} /* if */
write_id_str(")))", dctl);
} else {
bad_mangled_name(dctl);
end_ptr = id;
} /* if */
} else if (start_of_id_is("__TWR__", id, dctl)) {
write_id_str("thread_local wrapper for ", dctl);
end_ptr = demangle_identifier(id+7, dctl);
} else if (start_of_id_is("__TID_", id, dctl)) {
write_id_str("type identifier for ", dctl);
end_ptr = demangle_type(id+6, dctl);
} else if (start_of_id_is("__T_", id, dctl)) {
write_id_str("typeinfo for ", dctl);
end_ptr = demangle_type(id+4, dctl);
} else if (start_of_id_is("__VFE__", id, dctl)) {
write_id_str("surrogate in class ", dctl);
p = demangle_type(id+7, dctl);
if (get_char(p, dctl) != '_' || get_char(p+1, dctl) != '_') {
bad_mangled_name(dctl);
end_ptr = p;
} else {
write_id_str(" for ", dctl);
end_ptr = demangle_identifier(p+2, dctl);
} /* if */
} else if (start_of_id_is("__Q", id, dctl) ||
(start_of_id_is("__", id, dctl) &&
is_mangled_type_name(id+2, dctl))) {
/* Mangled type name. */
end_ptr = demangle_type_name(id+2, dctl);
} else if (start_of_id_is("__STV__", id, dctl)) {
/* Static variable made external by addition of prefix "__STV__" and
suffix of module id. */
end_ptr = demangle_static_variable_name(id, dctl);
} else if (start_of_id_is("__", id, dctl) && isdigit((unsigned char)id[2])) {
/* Local variable mangled by the C-generating back end: __nn_mm_name,
where "nn" and "mm" are decimal integers. */
end_ptr = demangle_local_name(id, dctl);
} else {
/* Normal case: function name, static data member name, or
name of type or variable promoted out of function. */
end_ptr = demangle_identifier(id, dctl);
} /* if */
if (dctl->output_overflow_err) {
dctl->err_in_id = TRUE;
} else {
/* Add a terminating null. */
dctl->output_id[dctl->output_id_len] = 0;
} /* if */
/* Make sure the whole identifier was taken. */
if (!dctl->err_in_id && *end_ptr != '\0') bad_mangled_name(dctl);
*err = dctl->err_in_id;
*buffer_overflow_err = dctl->output_overflow_err;
*required_buffer_size = dctl->output_id_len + 1; /* +1 for final null. */
/* If the name is compressed, we need room for the uncompressed
form, and a null, in the buffer. */
if (dctl->uncompressed_length != 0) {
*required_buffer_size += dctl->uncompressed_length+1;
} /* if */
} /* decode_identifier */
#else /* IA64_ABI */
/*
Start of demangling code for IA-64 ABI.
*/
/*
TRUE if the bugs in the g++ 3.2 implementation of the IA-64 ABI should
be emulated. Can be changed by a command line option.
*/
a_boolean emulate_gnu_abi_bugs = DEFAULT_EMULATE_GNU_ABI_BUGS;
/*
TRUE if the host integer representation is little-endian.
External because it's declared extern in host_envir.h.
*/
a_boolean host_little_endian;
/*
Bits used to represent cv-qualifiers in a bit set.
*/
typedef int a_cv_qualifier_set;
#define CVQ_NONE ((a_cv_qualifier_set)0)
#define CVQ_CONST ((a_cv_qualifier_set)0x1)
#define CVQ_VOLATILE ((a_cv_qualifier_set)0x2)
#define CVQ_RESTRICT ((a_cv_qualifier_set)0x4)
/*
Values used to represent an optional ref-qualifier.
*/
typedef int a_ref_qualifier;
#define REFQ_NONE ((a_ref_qualifier)0)
#define REFQ_LVALUE ((a_ref_qualifier)0x1)
#define REFQ_RVALUE ((a_ref_qualifier)0x2)
/*
Information about a function that has to be preserved from the
time of scanning of the name (e.g., in a <nested-name>) until later use
in processing the <bare-function-type>.
*/
typedef struct a_func_block {
a_boolean no_return_type;
/* TRUE if the function is one that will not have
a return type encoded in the function type. */
a_cv_qualifier_set
cv_quals;
/* If the function is a cv-qualified member function,
the set of cv-qualifiers. 0 otherwise. */
a_ref_qualifier
ref_qual;
/* If the function is a ref-qualified member function,
the ref-qualifier. 0 otherwise. */
char ctor_dtor_kind;
/* If the function is a constructor or destructor,
the character from the mangled name identifying its
kind, e.g., '2' for a subobject constructor/
destructor. ' ' if the function is not a
constructor or destructor. */
} a_func_block;
/*
Information about an entity in the mangled name that may be reused
by referring back to it by number as a "substitution".
*/
/* Code for type of syntactic object substituted for: */
typedef enum a_substitution_kind {
subk_unscoped_template_name,
/* An <unscoped-template-name>. */
subk_prefix, /* A <prefix>. */
subk_template_prefix, /* A <template-prefix>. */
subk_type, /* A <type>. */
subk_template_template_param
/* A <template-template-param>. */
} a_substitution_kind;
typedef struct a_substitution {
a_const_char *start; /* First character of the encoding of the entity. */
a_substitution_kind
kind; /* Kind of entity. */
unsigned long num_levels;
/* For subk_prefix and subk_template_prefix, the
number of levels of the prefix included. That is,
is the substitution A:: or A::B:: or A::B::C::.
For the subk_template_prefix case, the count
is the number of complete levels (name plus
optional template argument list) that precede
the final name (and no template argument list)
that is part of the substitution. (Therefore,
the count could be zero.) */
a_boolean parse_template_args;
/* The value of the parse_template_args boolean
at the time the subk_type substitution is recorded
(so that value can be used on subsequent
demanglings of that substitution string). Ignored
for other substitution kinds. */
} a_substitution;
static a_substitution
*substitutions = NULL;
/* A dynamically allocated array. substitutions[n]
gives the meaning of the substitution numbered
"n". */
static unsigned long
num_substitutions = 0;
/* The number of substitutions currently defined, i.e.,
the number of elements of the array that have
been set. */
static unsigned long
allocated_substitutions = 0;
/* The allocated size of the array, as a number of
elements. */
static char *ud_suffix_buffer = NULL;
/* A dynamically allocated buffer into which
ud-suffixes are copied (when demangling literal
operators). */
static unsigned long
ud_suffix_buffer_length = 0;
/* The allocated length of ud_suffix_buffer. */
static a_const_char *demangle_type_first_part(
a_const_char *ptr,
a_cv_qualifier_set cv_quals,
a_boolean under_lhs_declarator,
a_boolean need_trailing_space,
a_boolean parse_template_args,
a_decode_control_block_ptr dctl);
static void demangle_type_second_part(
a_const_char *ptr,
a_cv_qualifier_set cv_quals,
a_boolean under_lhs_declarator,
a_decode_control_block_ptr dctl);
static a_const_char *full_demangle_type(
a_const_char *ptr,
a_boolean parse_template_args,
a_boolean is_pack_expansion,
a_decode_control_block_ptr dctl);
static a_const_char *demangle_simple_id(a_const_char *ptr,
a_decode_control_block_ptr dctl);
/*
Macro to invoke full_demangle_type in the usual case where parse_template_args
is TRUE and is_pack_expansion is FALSE.
*/
#define demangle_type(ptr, dctl) \
full_demangle_type(ptr, /*parse_template_args=*/TRUE, \
/*is_pack_expansion=*/FALSE, dctl)
static a_const_char *demangle_template_args(a_const_char *ptr,
a_decode_control_block_ptr dctl);
/*
Bit mask used to determine which portion(s) of a <name> should
be emitted by demangle_name. For most cases, DNO_ALL is correct, but in
cases where a <name> is scanned more than once, different portions of the
name may be emitted on different passes.
*/
typedef int a_demangle_name_option;
#define DNO_NONE ((a_demangle_name_option)0)
#define DNO_EXTERNALIZATION \
((a_demangle_name_option)0x1)
#define DNO_NAME ((a_demangle_name_option)0x2)
#define DNO_ALL (DNO_EXTERNALIZATION | DNO_NAME)
static a_const_char *demangle_name(a_const_char *ptr,
a_func_block *func_block,
a_demangle_name_option options,
a_decode_control_block_ptr dctl);
static a_const_char *demangle_unresolved_name(a_const_char *ptr,
a_decode_control_block_ptr dctl);
static a_const_char *demangle_expression(a_const_char *ptr,
a_decode_control_block_ptr dctl);
static a_const_char *demangle_encoding(
a_const_char *ptr,
a_boolean include_func_params,
a_decode_control_block_ptr dctl);
static a_const_char *demangle_nested_name_components(
a_const_char *ptr,
unsigned long num_levels,
a_boolean *is_no_return_name,
a_boolean *has_templ_arg_list,
char *ctor_dtor_kind,
a_const_char **last_component_name,
a_decode_control_block_ptr dctl);
static a_const_char *demangle_unscoped_name(
a_const_char *ptr,
a_func_block *func_block,
a_decode_control_block_ptr dctl);
static a_const_char *demangle_unqualified_name(
a_const_char *ptr,
a_boolean *is_no_return_name,
a_decode_control_block_ptr dctl);
static a_const_char *demangle_template_param(a_const_char *ptr,
a_decode_control_block_ptr dctl);
static void output_cv_qualifiers(a_cv_qualifier_set cv_quals,
a_boolean trailing_space,
a_decode_control_block_ptr dctl);
static void clear_func_block(a_func_block *func_block)
/*
Clear a function information block to default values.
*/
{
func_block->no_return_type = FALSE;
func_block->cv_quals = 0;
func_block->ref_qual = REFQ_NONE;
func_block->ctor_dtor_kind = ' ';
} /* clear_func_block */
static a_const_char *get_number(a_const_char *p,
long *num,
a_decode_control_block_ptr dctl)
/*
Accumulate a number starting at position p and return its value in *num.
Return a pointer to the character position following the number.
A negative number is indicated by a leading "n".
*/
{
long n = 0;
a_boolean negative = FALSE;
if (*p == 'n') {
negative = TRUE;
p++;
} /* if */
if (!isdigit((unsigned char)*p)) {
bad_mangled_name(dctl);
} else {
do {
n = n*10 + (*p - '0');
p++;
} while (isdigit((unsigned char)*p));
} /* if */
if (negative) n = -n;
*num = n;
return p;
} /* get_number */
static void record_substitutable_entity(
a_const_char *start,
a_substitution_kind kind,
unsigned long num_levels,
a_boolean parse_template_args,
a_decode_control_block_ptr dctl)
/*
Record the entity whose mangled name starts at "start", and whose
kind (of syntax term) is given by "kind", as a potentially
substitutable entity, one that can be used again by referencing
it in a later substitution. num_levels gives added information
for the subk_prefix and subk_template_prefix cases.
For subk_type cases, the value of parse_template_args is stored with
the substitution information (so that it can be used when demangling
the type when it is used as a substitution).
*/
{
/* Do not record anything if we are suppressing recording of substitutions.
One case in which that is true is when an error has been detected. */
if (!dctl->suppress_substitution_recording) {
unsigned long number = num_substitutions++;
a_substitution *subp;
if (num_substitutions > allocated_substitutions) {
/* Need to allocate or extend the substitutions array. */
true_size_t new_size;
allocated_substitutions += 500;
new_size = allocated_substitutions*sizeof(a_substitution);
if (substitutions == NULL) {
substitutions = (a_substitution*)malloc(new_size);
} else {
substitutions = (a_substitution*)realloc(substitutions, new_size);
} /* if */
if (substitutions == NULL) {
bad_mangled_name(dctl);
return;
} /* if */
} /* if */
subp = &substitutions[number];
subp->start = start;
subp->kind = kind;
subp->num_levels = num_levels;
subp->parse_template_args = parse_template_args;
} /* if */
} /* record_substitutable_entity */
static a_const_char *demangle_substitution(
a_const_char *ptr,
int type_pass_num,
a_cv_qualifier_set cv_quals,
a_boolean under_lhs_declarator,
a_boolean need_trailing_space,
a_const_char **last_component_name,
a_const_char **substitution,
a_decode_control_block_ptr dctl)
/*
Demangle an IA-64 <substitution> and output the demangled form.
Return a pointer to the character position following what was demangled.
A <substitution> repeats a construct previously encoded in the
mangled name by referring to it by number, as a way to reduce the
size of mangled names. There are also some predefined substitutions,
for entities from the standard library that are likely to come up
often. The syntax is:
<substitution> ::= S <seq-id> _
::= S_
<substitution> ::= St # ::std::
<substitution> ::= Sa # ::std::allocator
<substitution> ::= Sb # ::std::basic_string
<substitution> ::= Ss # ::std::basic_string < char,
::std::char_traits<char>,
::std::allocator<char> >
<substitution> ::= Si # ::std::basic_istream<char, std::char_traits<char> >
<substitution> ::= So # ::std::basic_ostream<char, std::char_traits<char> >
<substitution> ::= Sd # ::std::basic_iostream<char, std::char_traits<char> >
When the substitution is a type, type_pass_num indicates whether to
do the first-part (1) or second-part (2) processing. type_pass_num == 0
means do both parts, i.e., the whole type. cv_quals,
under_lhs_declarator, and need_trailing_space give extra information
to be passed through to the type demangling routines in that case. If
last_component_name is non-NULL, and the substitution decoded is a
prefix of a nested name, a pointer to the encoding for the last
component of the nested name is returned in *last_component_name. It
will not be a substitution. This is needed for generating the names
of constructors and destructors. If substitution is non-NULL, *substitution
is set to the point in the mangled name where the substitution source
occurs (in case the caller needs to examine it -- for example to see what
type the substitution represents).
*/
{
char ch2 = ptr[1];
if (last_component_name != NULL) *last_component_name = NULL;
if (substitution != NULL) *substitution = NULL;
if (islower((unsigned char)ch2)) {
/* Predefined substitution. */
a_const_char *str = "";
a_const_char *last_name = "";
if (ch2 == 't') {
str = "std";
last_name = "3std";
} else if (ch2 == 'a') {
str = "std::allocator";
last_name = "9allocator";
} else if (ch2 == 'b') {
str = "std::basic_string";
last_name = "12basic_string";
} else if (ch2 == 's') {
str =
"std::basic_string<char, std::char_traits<char>, std::allocator<char>>";
last_name = "12basic_string";
} else if (ch2 == 'i') {
str = "std::basic_istream<char, std::char_traits<char>>";
last_name = "13basic_istream";
} else if (ch2 == 'o') {
str = "std::basic_ostream<char, std::char_traits<char>>";
last_name = "13basic_ostream";
} else if (ch2 == 'd') {
str = "std::basic_iostream<char, std::char_traits<char>>";
last_name = "14basic_iostream";
} /* if */
/* Output nothing if we want only the second-pass output. */
if (type_pass_num != 2) {
output_cv_qualifiers(cv_quals, TRUE, dctl);
write_id_str(str, dctl);
} /* if */
ptr += 2;
if (last_component_name != NULL) *last_component_name = last_name;
} else {
/* Not a predefined substitution. Convert the base-36 sequence number. */
uint32_t number = 0;
a_substitution *subp;
a_const_char *p;
ptr++;
if (ch2 != '_') {
do {
static a_const_char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
number *= 36;
if (*ptr == '\0') {
bad_mangled_name(dctl);
break;
} /* if */
p = strchr(digits, *ptr);
if (p == NULL) {
bad_mangled_name(dctl);
break;
} /* if */
number += (uint32_t)(p - digits);
ptr++;
} while (*ptr != '_');
number++;
} /* if */
if (number >= num_substitutions) {
bad_mangled_name(dctl);
} else {
a_func_block func_block;
ptr = advance_past_underscore(ptr, dctl);
subp = &substitutions[number];
p = subp->start;
if (substitution != NULL) *substitution = p;
/* Rescan the encoding for the entity, outputting the demangled form
again at the current output position. Don't record substitutions
when rescanning. */
dctl->suppress_substitution_recording++;
if (type_pass_num == 2 && subp->kind != subk_type) {
/* If we're expecting a type, and we want the second-pass
processing, and we get something other than a type, ignore it.
It's a type specifier, and we don't put those out on the
second pass. */
} else {
switch (subp->kind) {
case subk_unscoped_template_name:
if (type_pass_num == 1 || type_pass_num == 0) {
/* Emit any cv-qualifiers that are applicable to this
substitution. */
output_cv_qualifiers(cv_quals, TRUE, dctl);
} /* if */
(void)demangle_unscoped_name(p, &func_block, dctl);
break;
case subk_prefix:
case subk_template_prefix:
{ a_boolean is_no_return_name, has_templ_arg_list;
char ctor_dtor_kind;
output_cv_qualifiers(cv_quals, TRUE, dctl);
/* Take the right number of levels of the name. Note that a
substitution counts as one level even if it represents
several. */
if (subp->num_levels > 0) {
p = demangle_nested_name_components(p,
subp->num_levels,
&is_no_return_name,
&has_templ_arg_list,
&ctor_dtor_kind,
last_component_name,
dctl);
} /* if */
if (subp->kind == subk_template_prefix) {
/* For the template prefix case, take one more
<unqualified-name>. */
if (subp->num_levels > 0) write_id_str("::", dctl);
p = demangle_unqualified_name(p, &is_no_return_name, dctl);
} /* if */
}
break;
case subk_type:
if (type_pass_num == 1 || type_pass_num == 0) {
/* Make sure to use the value of parse_template_args that
was in effect when the type substitution was first
recorded. */
(void)demangle_type_first_part(p, cv_quals,
under_lhs_declarator,
need_trailing_space,
subp->parse_template_args,
dctl);
} /* if */
if (type_pass_num == 2 || type_pass_num == 0) {
demangle_type_second_part(p, cv_quals,
under_lhs_declarator, dctl);
} /* if */
break;
case subk_template_template_param:
(void)demangle_template_param(p, dctl);
break;
default:
bad_mangled_name(dctl);
} /* switch */
} /* if */
dctl->suppress_substitution_recording--;
} /* if */
} /* if */
return ptr;
} /* demangle_substitution */
/*
Bit mask used to determine which portion(s) of a <bare-function-type> should
be emitted. The <bare-function-type> contains an optional return type
as well as one or more parameter types.
*/
typedef int a_bare_function_type_option;
#define BFT_NONE ((a_bare_function_type_option)0)
#define BFT_RETURN ((a_bare_function_type_option)0x1)
#define BFT_PARAMS ((a_bare_function_type_option)0x2)
static a_const_char *demangle_bare_function_type(
a_const_char *ptr,
a_boolean no_return_type,
a_bare_function_type_option options,
a_decode_control_block_ptr dctl)
/*
Demangle an IA-64 <bare-function-type> and output selected pieces of the
demangled form. Return a pointer to the character position following what was
demangled. A <bare-function-type> encodes the return and parameter types of a
function type without the surrounding F/E delimiters. It is used in cases
where the only possibility is a function type, e.g., in a top-level encoding
for a function. The syntax is:
<bare-function-type> ::= <signature type>+
# types are possible return type, then parameter types
That is, the <bare-function-type> is one or more type encodings.
no_return_type is TRUE if the return type is not present in the mangled
encoding. The pieces of the <bare-function-type> that are emitted are
controlled by the options bit mask; when BFT_RETURN is specified the return
type (and a space character) is emitted, when BFT_PARAMS is specified the
parameter types (with surrounding "( )") are emitted. In all cases, the
returned value reflects the end position of <bare-function-type> (regardless of
what portion(s) of it were emitted).
*/
{
/* Stop on a (possibly ref-qualified) "E" or end of the input. */
#define end_of_param_list(p) (*(p) == 'E' || *(p) == '\0' || \
((*(p) == 'R' || *(p) == 'O') && \
*((p)+1) == 'E'))
/* Handle the return type first. */
if ((options & BFT_RETURN) == 0) dctl->suppress_id_output++;
if (!no_return_type) {
/* A return type is present and must be scanned. */
ptr = demangle_type(ptr, dctl);
write_id_ch(' ', dctl);
} /* if */
if ((options & BFT_RETURN) == 0) dctl->suppress_id_output--;
/* The remaining portion is the parameter type(s). */
if ((options & BFT_PARAMS) == 0) dctl->suppress_id_output++;
write_id_ch('(', dctl);
if (end_of_param_list(ptr)) {
/* Error, there are no parameter types (there's supposed to be at
least a "v" for a void parameter list). This is likely caused
by absence of a return type when one is expected. */
bad_mangled_name(dctl);
} else if (*ptr == 'v' && end_of_param_list(ptr+1)) {
/* An empty parameter list is encoded as a single void type.
Put out just "()" for that case. */
ptr++;
} else {
for (;;) {
if (*ptr == 'z') {
/* Encoding for ellipsis. */
write_id_str("...", dctl);
ptr++;
if (!end_of_param_list(ptr)) {
bad_mangled_name(dctl);
break;
} /* if */
} else {
/* Normal type, not an ellipsis. */
ptr = demangle_type(ptr, dctl);
} /* if */
/* Stop on a (possibly ref-qualified) "E" or at the end of the input. */
if (end_of_param_list(ptr)) break;
/* Stop on an error. */
if (dctl->err_in_id) break;
/* Continuing, so we need a comma between parameter types. */
write_id_str(", ", dctl);
} /* if */
} /* if */
write_id_ch(')', dctl);
if ((options & BFT_PARAMS) == 0) dctl->suppress_id_output--;
return ptr;
#undef end_of_param_list
} /* demangle_bare_function_type */
static a_const_char *get_cv_qualifiers(a_const_char *ptr,
a_cv_qualifier_set *cv_quals)
/*
Advance over any cv-qualifiers (const/volatile) at the indicated location
and return in *cv_quals a bit set indicating the qualifiers encountered.
Return a pointer to the character position following what was demangled.
Note that the IA-64 ABI defines a general-purpose "vendor extended type
qualifier" that is not implemented (as yet). Certain vendor extended type
qualifiers are however used by the front end to represent C++/CLI declarators;
those are handled in the declarator processing rather than here
(order-insensitive vendor extended type qualifiers should be handled here,
but currently the front end doesn't use any).
*/
{
*cv_quals = 0;
for (;; ptr++) {
if (*ptr == 'K') {
*cv_quals |= CVQ_CONST;
} else if (*ptr == 'V') {
*cv_quals |= CVQ_VOLATILE;
} else if (*ptr == 'r') {
*cv_quals |= CVQ_RESTRICT;
} else {
break;
} /* if */
} /* for */
return ptr;
} /* get_cv_qualifiers */
static a_const_char *get_ref_qualifier(a_const_char *ptr,
a_ref_qualifier *ref_qual)
/*
Advance over a ref-qualifier (lvalue/rvalue) at the indicated location
and return in *ref_qual a value indicating the ref-qualifier encountered.
Return a pointer to the character position following what was demangled.
*/
{
*ref_qual = REFQ_NONE;
if (*ptr == 'R') {
*ref_qual = REFQ_LVALUE;
ptr++;
} else if (*ptr == 'O') {
*ref_qual = REFQ_RVALUE;
ptr++;
} /* if */
return ptr;
} /* get_ref_qualifier */
static a_boolean is_vendor_extended_declarator(a_const_char *ptr)
/*
Returns TRUE if the location pointed to by ptr contains an EDG-specific vendor
extended type qualifier and the extension is being used as a declarator.
Note that these vendor extended type qualifiers are treated as order-sensitive.
*/
{
return (start_of_id_is("U8__handle", ptr) ||
start_of_id_is("U8__trkref", ptr) ||
start_of_id_is("U14__interior_ptr", ptr) ||
start_of_id_is("U9__pin_ptr", ptr));
} /* is_vendor_extended_declarator */
static a_const_char *demangle_vector_size_qualifier(
a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Demangle the GNU vector_size qualifier if it appears at the indicated
location. Return a pointer to the character position following what was
demangled.
*/
{
if (start_of_id_is("U8__vector", ptr)) {
ptr += 10;
write_id_str("__attribute__((vector_size(?))) ", dctl);
} /* for */
return ptr;
} /* demangle_vector_size_qualifier */
static void output_cv_qualifiers(a_cv_qualifier_set cv_quals,
a_boolean trailing_space,
a_decode_control_block_ptr dctl)
/*
Output any cv-qualifiers (const/volatile) in the bit set cv_quals.
If trailing_space is TRUE, add a space at the end if any qualifiers were
put out.
*/
{
a_boolean any_previous = FALSE;
if (cv_quals & CVQ_CONST ) {
write_id_str("const", dctl);
any_previous = TRUE;
} /* if */
if (cv_quals & CVQ_VOLATILE) {
if (any_previous) write_id_ch(' ', dctl);
write_id_str("volatile", dctl);
any_previous = TRUE;
} /* if */
if (cv_quals & CVQ_RESTRICT) {
if (any_previous) write_id_ch(' ', dctl);
write_id_str("restrict", dctl);
any_previous = TRUE;
} /* if */
if (any_previous && trailing_space) write_id_ch(' ', dctl);
} /* output_cv_qualifiers */
static void output_ref_qualifier(a_ref_qualifier ref_qual,
a_decode_control_block_ptr dctl)
/*
Output a ref-qualifier (lvalue/rvalue) if ref_qual indicates there is one.
*/
{
if (ref_qual == REFQ_LVALUE) {
write_id_str("&", dctl);
} else if (ref_qual & REFQ_RVALUE) {
write_id_str("&&", dctl);
} /* if */
} /* output_ref_qualifier */
static a_const_char *demangle_template_param(a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Demangle an IA-64 <template-param> and output the demangled form. Return
a pointer to the character position following what was demangled.
A <template-param> encodes a reference to a template parameter.
The syntax is:
<template-param> ::= T_ # first template parameter
::= T <parameter-2 non-negative number> _
*/
{
long num = 1;
char buffer[50];
/* Advance past the "T". */
ptr++;
if (*ptr != '_') {
ptr = get_number(ptr, &num, dctl);
if (num < 0) {
bad_mangled_name(dctl);
num = 0;
} else {
num += 2;
} /* if */
} /* if */
ptr = advance_past_underscore(ptr, dctl);
(void)sprintf(buffer, "T%ld", num);
write_id_str(buffer, dctl);
return ptr;
} /* demangle_template_param */
static a_const_char *demangle_parameter_reference(
a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Demangle an IA-64 <function-param> and output the demangled form.
Function parameter placeholders are needed for late-specified return types.
Return a pointer to the character position following what was demangled.
A <function-param> encodes a reference to a function parameter.
The syntax is:
<function-param> ::= fpT # "this"
::= fp <top-level CV-qualifiers> _
# L == 0, first parameter
::= fp <top-level CV-qualifiers>
<parameter-2 non-negative number> _
# L == 0, second and later parameters
::= fL <L-1 non-negative number> p
<top-level CV-qualifiers> _
# L > 0, first parameter
::= fL <L-1 non-negative number> p
<top-level CV-qualifiers>
<parameter-2 non-negative number> _
# L > 0, second and later parameters
*/
{
long num = 1, level = -1;
char buffer[50];
a_cv_qualifier_set cv_quals;
/* Advance past the "f". */
ptr++;
if (*ptr == 'L') {
/* Get the optional level information. */
ptr++;
ptr = get_number(ptr, &level, dctl);
if (level < 0) {
bad_mangled_name(dctl);
goto end_of_routine;
} else {
level += 1;
} /* if */
} /* if */
if (*ptr != 'p') {
bad_mangled_name(dctl);
goto end_of_routine;
} /* if */
ptr++;
if (*ptr == 'T') {
/* Implicit "this" in trailing return type. */
ptr++;
write_id_str("this", dctl);
} else {
if (*ptr != '_' && !isdigit((unsigned char)*ptr)) {
/* Optional cv-qualifiers. */
ptr = get_cv_qualifiers(ptr, &cv_quals);
output_cv_qualifiers(cv_quals, /*trailing_space=*/TRUE, dctl);
} /* if */
if (*ptr != '_') {
/* Parameter number. */
ptr = get_number(ptr, &num, dctl);
if (num < 0) {
bad_mangled_name(dctl);
goto end_of_routine;
} else {
num += 2;
} /* if */
} /* if */
ptr = advance_past_underscore(ptr, dctl);
write_id_str("param#", dctl);
if (level == -1) {
(void)sprintf(buffer, "%ld", num);
} else {
(void)sprintf(buffer, "%ld[up %ld level%s]", num, level,
level > 1 ? "s" : "");
} /* if */
write_id_str(buffer, dctl);
} /* if */
end_of_routine:
return ptr;
} /* demangle_parameter_reference */
/* Forward reference. */
static a_const_char *demangle_source_name(
a_const_char *ptr,
a_boolean is_module_id,
a_decode_control_block_ptr dctl);
/*
Macro to determine if the character string pointed to by "p" is a
<builtin-type>. <builtin-type>s are a single lower-case letter or two
characters starting with the character "D". Exceptions to this rule are
the mangling for decltype (i.e., "DT" and "Dt") as well as the EDG extension
for typeof (i.e., "DY" and "Dy"), __underlying_type (i.e., "Du") and pack
expansions (i.e., "Dp"). The lower case letter "r" is used in <CV-qualifiers>
for "restrict" and is not a <builtin-type>.
*/
#define is_builtin_type(p) \
((islower((unsigned char)*(p)) && \
*(p) != 'r') || \
(*(p) == 'D' && \
!((p)[1] == 'p' || (p)[1] == 'u' || \
(p)[1] == 'T' || (p)[1] == 't' || \
(p)[1] == 'Y' || (p)[1] == 'y')))
/*
Macro to determine if the type pointed to by "p" needs a substitution
recorded for it. <builtin-type>s are not recorded with the exception of
vendor extended types which are recorded.
*/
#define record_substitution_for_type(p) (!(is_builtin_type(p)) || *(p) == 'u')
static a_const_char *demangle_type_specifier(
a_const_char *ptr,
a_boolean parse_template_args,
a_decode_control_block_ptr dctl)
/*
Demangle the type at ptr and output the specifier part. Return a pointer
to the character position following what was demangled. The syntax is:
<type> ::= <builtin-type>
::= <class-enum-type>
::= <template-param>
::= <template-template-param> <template-args>
::= Dp <type> # pack expansion of (C++11)
::= Dt <expression> E # decltype of an id-expression or class member
# access (C++11)
::= DT <expression> E # decltype of an expression (C++11)
::= Dy <type> E # typeof(type) (EDG extension)
::= DY <expression> E # typeof(expression) (EDG extension)
::= Du <type> E # __underlying_type(type) (EDG extension)
Other parts of <type> are handled in demangle_type_first_part and
demangle_type_second_part. In particular, substitutions are handled
at that level. cv-qualifiers have been handled by the caller.
If parse_template_args is TRUE then any <template-args> in the type should be
parsed as part of the type. parse_template_args is FALSE when parsing the
<type> of a conversion function operator-name (the <template-args> are
demangled as part of the template function instead).
*/
{
a_const_char *p = ptr, *s;
/* Builtin type encodings are typically lower-case (with some exceptions).
Names begin with a digit or an upper-case letter. */
if (!is_builtin_type(p)) {
if (*p == 'T') {
/* A template parameter, possibly a template template parameter. */
a_const_char *tstart = p;
p = demangle_template_param(p, dctl);
if (*p == 'I' && parse_template_args) {
/* A <template-args> list. */
/* Record the template template parameter as a potential
substitution. */
record_substitutable_entity(tstart, subk_template_template_param, 0L,
/*parse_template_args=*/FALSE, dctl);
p = demangle_template_args(p, dctl);
} /* if */
} else if (*p == 'D' && p[1] == 'p') {
/* A pack expansion. */
p = full_demangle_type(p+2, /*parse_template_args=*/TRUE,
/*is_pack_expansion=*/TRUE, dctl);
} else if (*p == 'D' &&
(p[1] == 't' || p[1] == 'T')) {
/* decltype:
Dt <expression> E # decltype of an id-expression or class member
# access
DT <expression> E # decltype of an expression */
write_id_str("decltype(", dctl);
if (p[1] == 't') {
p = demangle_expression(p+2, dctl);
} else {
write_id_ch('(', dctl);
p = demangle_expression(p+2, dctl);
write_id_ch(')', dctl);
} /* if */
write_id_ch(')', dctl);
p = advance_past('E', p, dctl);
} else if (*p == 'D' &&
(p[1] == 'y' || p[1] == 'Y')) {
/* typeof:
This is an EDG extension to the IA-64 ABI spec to handle GNU typeof
(and GNU doesn't provide a mangling that we can follow):
<type> ::= Dy <type> E # typeof(type)
::= DY <expression> E # typeof(expression)
*/
write_id_str("typeof(", dctl);
if (p[1] == 'y') {
p = demangle_type(p+2, dctl);
} else {
p = demangle_expression(p+2, dctl);
} /* if */
write_id_ch(')', dctl);
p = advance_past('E', p, dctl);
} else if (*p == 'D' && p[1] == 'u') {
/* __underlying_type:
This is an EDG extension to the IA-64 ABI spec to handle
__underlying_type (a helper function used to implement the C++11
underlying_type type trait):
<type> ::= Du <type> E # __underlying_type(type)
*/
write_id_str("__underlying_type(", dctl);
p = demangle_type(p+2, dctl);
write_id_ch(')', dctl);
p = advance_past('E', p, dctl);
} else {
/* <class-enum-type>, i.e., <name> */
a_func_block func_block;
p = demangle_name(p, &func_block, /*options=*/DNO_ALL, dctl);
} /* if */
} else {
/* Builtin type. */
switch (*p++) {
case 'v':
s = "void";
break;
case 'w':
s = "wchar_t";
break;
case 'b':
s = "bool";
break;
case 'c':
s = "char";
break;
case 'a':
s = "signed char";
break;
case 'h':
s = "unsigned char";
break;
case 's':
s = "short";
break;
case 't':
s = "unsigned short";
break;
case 'i':
s = "int";
break;
case 'j':
s = "unsigned int";
break;
case 'l':
s = "long";
break;
case 'm':
s = "unsigned long";
break;
case 'x':
s = "long long";
break;
case 'y':
s = "unsigned long long";
break;
case 'n':
s = "__int128";
break;
case 'o':
s = "unsigned __int128";
break;
case 'f':
s = "float";
break;
case 'd':
s = "double";
break;
case 'e':
/* Also __float80 in some configurations. */
s = "long double";
break;
case 'g':
s = "__float128";
break;
case 'u':
/* A vendor extended type is specified as:
<builtin-type> ::= u <source-name> . */
p = demangle_source_name(p, /*is_module_id=*/FALSE, dctl);
s = "";
break;
case 'D':
/* Additional built-in types (too many to assign a single character
to each one). */
switch (*p++) {
case 'a':
s = "auto";
break;
case 'c':
s = "decltype(auto)";
break;
case 'n':
s = "std::nullptr_t";
break;
case 'N':
/* EDG extension for C++/CLI managed __nullptr. */
s = "__nullptr";
break;
case 's':
s = "char16_t";
break;
case 'i':
s = "char32_t";
break;
case 'v':
/* Vector type. Format is: "Dv<number>_<type>". Scan the type
portion twice in order to get the proper "vector_size" value
(by "multiplying" by sizeof(type) -- since the demangling
doesn't know the size of types). */
{ long num;
a_const_char *typep;
p = get_number(p, &num, dctl);
if (*p != '_') {
bad_mangled_name(dctl);
} else {
p++;
typep = p;
p = demangle_type(p, dctl);
write_id_str(" __attribute((vector_size(", dctl);
write_id_signed_number(num, dctl);
write_id_str("*sizeof(", dctl);
dctl->suppress_substitution_recording++;
(void)demangle_type(typep, dctl);
dctl->suppress_substitution_recording--;
write_id_str(")))) ", dctl);
} /* if */
s = "";
}
break;
default:
bad_mangled_name(dctl);
s = "";
} /* switch */
break;
case 'z': /* Ellipsis not handled here;
see demangle_bare_function_type. */
default:
bad_mangled_name(dctl);
s = "";
} /* switch */
write_id_str(s, dctl);
} /* if */
return p;
} /* demangle_type_specifier */
static a_const_char *skip_extern_C_indication(a_const_char *ptr)
/*
ptr points to the character after the "F" of a function type. Skip over
and ignore an indication of extern "C" following the "F", if one is present.
Return a pointer to the character following the extern "C" indication.
There's no syntax for representing the extern "C" in the function type, so
just ignore it.
*/
{
if (*ptr == 'Y') ptr++;
return ptr;
} /* skip_extern_C_indication */
static a_const_char *demangle_type_first_part(
a_const_char *ptr,
a_cv_qualifier_set cv_quals,
a_boolean under_lhs_declarator,
a_boolean need_trailing_space,
a_boolean parse_template_args,
a_decode_control_block_ptr dctl)
/*
Demangle the type at ptr and output the specifier part and the part of the
declarator that precedes the name. Return a pointer to the character
position following what was demangled. If under_lhs_declarator is TRUE,
this type is directly under a type that uses a left-side declarator,
e.g., a pointer type. (That's used to control use of parentheses around
parts of the declarator.) If need_trailing_space is TRUE, put a space
at the end of the specifiers part (needed if the declarator part is
not empty, because it contains a name or a derived type). cv_quals
indicates any previously-scanned cv-qualifiers that are to be considered
to be on top of the type. If parse_template_args is TRUE then any
<template-args> in the type should be parsed as part of the type.
*/
{
a_const_char *p = ptr, *qualp = p, *unqualp;
char kind;
a_cv_qualifier_set local_cv_quals;
a_boolean record_substitution = TRUE;
a_boolean record_cv_qual_substitution = TRUE;
/* Accumulate cv-qualifiers. */
p = get_cv_qualifiers(p, &local_cv_quals);
cv_quals |= local_cv_quals;
unqualp = p;
kind = *p;
if (kind == 'S' &&
/* "St" for "std::" is the beginning of a name, not a type. */
p[1] != 't') {
/* A substitution. */
p = demangle_substitution(p, 1, cv_quals,
under_lhs_declarator,
need_trailing_space,
(a_const_char **)NULL,
(a_const_char **)NULL,
dctl);
record_substitution = FALSE;
if (*p == 'I') {
/* A <template-args> list (the substitution must be a template). */
p = demangle_template_args(p, dctl);
record_substitution = TRUE;
} /* if */
} else if (kind == 'P' || kind == 'R' || kind == 'O' || kind == 'C' ||
(kind == 'U' && is_vendor_extended_declarator(p))) {
a_const_char *vendor_ext = NULL;
a_boolean need_space = TRUE;
/* Look for type qualifiers:
<type> ::= <CV-qualifiers> <type>
::= P <type> # pointer-to
::= R <type> # reference-to
::= O <type> # rvalue reference-to (C++11)
::= C <type> # complex pair (C 2000)
::= U <source-name> <type> # vendor extended type qualifier
*/
p++;
if (kind == 'U') {
/* This is a vendor extended type qualifier that is being used by the
front end to encode C++/CLI pointer-like types (i.e., handles,
tracking references, interior_ptrs, and pin_ptrs). This avoids adding
EDG-specific manglings for these entities that might be used in
subsequent IA-64 ABI revisions. Note that these extensions are
treated as "order-sensitive" for the purposes of substitutions. */
long num;
if (start_of_id_is("8__handle", p)) {
vendor_ext = "^";
} else if (start_of_id_is("8__trkref", p)) {
vendor_ext = "%";
} else if (start_of_id_is("14__interior_ptr", p)) {
write_id_str("interior_ptr<", dctl);
vendor_ext = ">";
need_space = FALSE;
} else if (start_of_id_is("9__pin_ptr", p)) {
write_id_str("pin_ptr<", dctl);
vendor_ext = ">";
need_space = FALSE;
} else {
bad_mangled_name(dctl);
} /* if */
/* Advance past the vendor string. */
p = get_number(p, &num, dctl);
p += num;
} /* if */
if (kind == 'C') {
write_id_str("_Complex ", dctl);
} /* if */
p = demangle_type_first_part(p, CVQ_NONE, /*under_lhs_declarator=*/TRUE,
need_space, parse_template_args, dctl);
if (kind == 'P') {
write_id_ch('*', dctl);
} else if (kind == 'R') {
write_id_ch('&', dctl);
} else if (kind == 'O') {
write_id_str("&&", dctl);
} else if (vendor_ext != NULL) {
write_id_str(vendor_ext, dctl);
} /* if */
/* Output the cv-qualifiers on the pointer, if any. */
output_cv_qualifiers(cv_quals, /*trailing_space=*/TRUE, dctl);
} else if (kind == 'M') {
/* Pointer-to-member type, M <class type> <member type>. */
a_const_char *classp = p+1;
/* Skip over the class name. */
/* Substitutions do get recorded on this scan. */
dctl->suppress_id_output++;
p = demangle_type(classp, dctl);
dctl->suppress_id_output--;
p = demangle_type_first_part(p, CVQ_NONE, /*under_lhs_declarator=*/TRUE,
/*need_trailing_space=*/TRUE,
parse_template_args, dctl);
/* Output Classname::*. */
dctl->suppress_substitution_recording++;
(void)demangle_type(classp, dctl);
dctl->suppress_substitution_recording--;
write_id_str("::*", dctl);
/* Output the cv-qualifiers on the pointer, if any. */
output_cv_qualifiers(cv_quals, /*trailing_space=*/TRUE, dctl);
} else if (kind == 'F' ||
(kind == 'D' &&
(p[1] == 'o' || p[1] == 'O'))) {
a_ref_qualifier dummy;
/* Function type, <exc-spec> F [Y] <bare-function-type> [<ref-qualifier>] E
where "Y" indicates extern "C" (and is ignored here). An <exc-spec>
looks like: [Do | DO <expression> E | Dw <type>* E ]. The front end
doesn't implement the "Dw" mangling, so the demangling is omitted. */
if (kind == 'D') {
/* An exception specification is present (skip it here). */
switch (p[1]) {
case 'o':
p += 2;
break;
case 'O':
dctl->suppress_id_output++;
p = demangle_expression(p+2, dctl);
dctl->suppress_id_output--;
p = advance_past('E', p, dctl);
break;
default:
bad_mangled_name(dctl);
break;
} /* switch */
} /* if */
p = skip_extern_C_indication(p+1);
/* Output the return type. */
p = demangle_type_first_part(p, CVQ_NONE, /*under_lhs_declarator=*/FALSE,
/*need_trailing_space=*/TRUE,
parse_template_args, dctl);
/* Skip over the parameter types without outputting anything. */
/* Substitutions do get recorded on this scan. */
p = demangle_bare_function_type(p, /*no_return_type=*/TRUE, BFT_NONE,
dctl);
/* Look for an optional ref-qualifier (and skip it in this pass). */
p = get_ref_qualifier(p, &dummy);
p = advance_past('E', p, dctl);
/* This is a right-side declarator, so if it's under a left-side declarator
parentheses are needed. */
if (under_lhs_declarator) write_id_ch('(', dctl);
/* Cv-qualifiers that are applied to function types are considered an
indivisible portion of the type and no substitution is made for the
unqualified type. */
record_cv_qual_substitution = FALSE;
} else if (kind == 'A') {
/* Array type,
A <positive dimension number> _ <element type>
A [ <dimension expression> ] _ <element type>
*/
p++;
if (!isdigit((unsigned char)*p)) {
if (*p != '_') {
/* Length is specified by an expression based on template
parameters. Ignore the expression. */
/* Substitutions do get recorded on this scan. */
dctl->suppress_id_output++;
p = demangle_expression(p, dctl);
dctl->suppress_id_output--;
} /* if */
} else {
/* Normal constant number of elements. */
/* Skip the array size. */
while (isdigit((unsigned char)*p)) p++;
} /* if */
p = advance_past_underscore(p, dctl);
/* Process the element type. */
p = demangle_type_first_part(p, CVQ_NONE, /*under_lhs_declarator=*/FALSE,
/*need_trailing_space=*/TRUE,
parse_template_args, dctl);
/* This is a right-side declarator, so if it's under a left-side declarator
parentheses are needed. */
if (under_lhs_declarator) write_id_ch('(', dctl);
} else {
/* No declarator part to process. Handle the specifier type. */
output_cv_qualifiers(cv_quals, /*trailing_space=*/TRUE, dctl);
p = demangle_vector_size_qualifier(p, dctl);
p = demangle_type_specifier(p, parse_template_args, dctl);
if (need_trailing_space) write_id_ch(' ', dctl);
if (!record_substitution_for_type(unqualp)) {
/* Do not record a substitution for (most) <builtin-type>s. */
record_substitution = FALSE;
} /* if */
} /* if */
if (record_substitution) {
/* Record the non-cv-qualified version of the type as a potential
substitution. */
record_substitutable_entity(unqualp, subk_type, 0L, parse_template_args,
dctl);
} /* if */
if (qualp != unqualp && record_cv_qual_substitution) {
/* The type is cv-qualified, so record another potential substitution
for the fully-qualified type. */
record_substitutable_entity(qualp, subk_type, 0L, parse_template_args,
dctl);
} /* if */
return p;
} /* demangle_type_first_part */
static void demangle_type_second_part(
a_const_char *ptr,
a_cv_qualifier_set cv_quals,
a_boolean under_lhs_declarator,
a_decode_control_block_ptr dctl)
/*
Demangle the type at ptr and output the part of the declarator that follows the
name. This routine does not return a pointer to the character position
following what was demangled; it is assumed that the caller will save
that from the call of demangle_type_first_part, and it saves a lot of
time if this routine can avoid scanning the specifiers again.
If under_lhs_declarator is TRUE, this type is directly under a type that
uses a left-side declarator, e.g., a pointer type. (That's used to control
use of parentheses around parts of the declarator.) cv_quals
indicates any previously-scanned cv-qualifiers that are to considered
to be on top of the type.
*/
{
a_const_char *p = ptr;
char kind;
a_cv_qualifier_set local_cv_quals;
/* Accumulate cv-qualifiers. */
p = get_cv_qualifiers(p, &local_cv_quals);
cv_quals |= local_cv_quals;
kind = *p;
if (kind == 'S' &&
/* "St" for "std::" is the beginning of a name, not a type. */
p[1] != 't') {
/* A substitution. */
p = demangle_substitution(p, 2, cv_quals,
under_lhs_declarator,
/*need_trailing_space=*/FALSE,
(a_const_char **)NULL,
(a_const_char **)NULL,
dctl);
/* No need to scan the <template-args> list if there is one --
that was done by demangle_type_first_part. */
} else if (kind == 'P' || kind == 'R' || kind == 'O' || kind == 'C' ||
(kind == 'U' && is_vendor_extended_declarator(p))) {
/* Look for type qualifiers:
<type> ::= <CV-qualifiers> <type>
::= P <type> # pointer-to
::= R <type> # reference-to
::= O <type> # rvalue reference-to (C++11)
::= C <type> # complex pair (C 2000)
::= U <source-name> <type> # vendor extended type qualifier
*/
p++;
if (kind == 'U') {
/* This is a vendor extended type qualifier that is being used
by the front end as a declarator; skip over it. */
dctl->suppress_id_output++;
p = demangle_source_name(p, /*is_module_id=*/FALSE, dctl);
dctl->suppress_id_output--;
} /* if */
demangle_type_second_part(p, CVQ_NONE, /*under_lhs_declarator=*/TRUE,
dctl);
} else if (kind == 'M') {
/* Pointer-to-member type, M <class type> <member type>. */
/* Advance over the class name. */
dctl->suppress_id_output++;
dctl->suppress_substitution_recording++;
p = demangle_type(p+1, dctl);
dctl->suppress_substitution_recording--;
dctl->suppress_id_output--;
demangle_type_second_part(p, CVQ_NONE, /*under_lhs_declarator=*/TRUE,
dctl);
} else if (kind == 'F' ||
(kind == 'D' &&
(p[1] == 'o' || p[1] == 'O' || p[1] == 'w'))) {
a_const_char *returnt, *exception_spec = NULL;
a_const_char *save_exception_expr = NULL;
a_ref_qualifier ref_qual;
/* Function type, <exc-spec> F [Y] <bare-function-type> [<ref-qualifier>] E
where "Y" indicates extern "C" (and is ignored here). An <exc-spec>
looks like: [Do | DO <expression> E | Dw <type>* E ]. The front end
doesn't implement the "Dw" mangling, so the demangling is omitted. */
if (kind == 'D') {
/* An exception specification is present. */
switch (p[1]) {
case 'o':
exception_spec = " noexcept";
p += 2;
break;
case 'O':
/* Emit the expression later (save a pointer to it), but skip it
now. */
save_exception_expr = p + 2;
dctl->suppress_id_output++;
p = demangle_expression(save_exception_expr, dctl);
dctl->suppress_id_output--;
p = advance_past('E', p, dctl);
break;
default:
bad_mangled_name(dctl);
break;
} /* switch */
} /* if */
/* This is a right-side declarator, so if it's under a left-side declarator
parentheses are needed. */
if (under_lhs_declarator) write_id_ch(')', dctl);
p = skip_extern_C_indication(p+1);
/* Put out the parameter types (the return type is skipped and not
output). */
returnt = p;
dctl->suppress_substitution_recording++;
p = demangle_bare_function_type(p, /*no_return_type=*/FALSE, BFT_PARAMS,
dctl);
dctl->suppress_substitution_recording--;
/* Get a ref-qualifier if there is one. */
p = get_ref_qualifier(p, &ref_qual);
p = advance_past('E', p, dctl);
/* Put out any cv-qualifiers (member functions). */
/* Note that such things could come up on nonmember functions in the
presence of typedefs. In such a case what we generate here will not
be valid C, but it's a reasonable representation of the mangled
type, and there's no way of getting the typedef name in there,
so let it be. */
if (cv_quals != 0) {
write_id_ch(' ', dctl);
output_cv_qualifiers(cv_quals, /*trailing_space=*/FALSE, dctl);
} /* if */
if (ref_qual != REFQ_NONE) {
/* Output a ref-qualifier, if any. */
write_id_ch(' ', dctl);
output_ref_qualifier(ref_qual, dctl);
} /* if */
/* Output the return type. */
demangle_type_second_part(returnt, CVQ_NONE,
/*under_lhs_declarator=*/FALSE, dctl);
if (exception_spec != NULL) {
write_id_str(exception_spec, dctl);
} else if (save_exception_expr != NULL) {
write_id_str(" noexcept(", dctl);
(void)demangle_expression(save_exception_expr, dctl);
write_id_ch(')', dctl);
} /* if */
} else if (kind == 'A') {
/* Array type,
A <positive dimension number> _ <element type>
A [ <dimension expression> ] _ <element type>
*/
/* This is a right-side declarator, so if it's under a left-side declarator
parentheses are needed. */
if (under_lhs_declarator) write_id_ch(')', dctl);
write_id_ch('[', dctl);
p++;
if (!isdigit((unsigned char)*p)) {
if (*p != '_') {
/* Length is specified by a constant expression based on template
parameters. */
dctl->suppress_substitution_recording++;
p = demangle_expression(p, dctl);
dctl->suppress_substitution_recording--;
} /* if */
} else {
/* Normal constant number of elements. */
/* Put out the array size. */
while (isdigit((unsigned char)*p)) write_id_ch(*p++, dctl);
} /* if */
p = advance_past_underscore(p, dctl);
write_id_ch(']', dctl);
/* Process the element type. */
demangle_type_second_part(p, CVQ_NONE, /*under_lhs_declarator=*/FALSE,
dctl);
} else {
/* No declarator part to process. No need to scan the specifiers type --
it was done by demangle_type_first_part. */
} /* if */
} /* demangle_type_second_part */
static a_const_char *full_demangle_type(
a_const_char *ptr,
a_boolean parse_template_args,
a_boolean is_pack_expansion,
a_decode_control_block_ptr dctl)
/*
Demangle an IA-64 <type> and output the demangled form. Return a pointer
to the character position following what was demangled. A <type> encodes
a type. The syntax is:
<type> ::= <builtin-type>
::= <function-type>
::= <class-enum-type>
::= <array-type>
::= <pointer-to-member-type>
::= <template-param>
::= <template-template-param> <template-args>
::= <substitution>
::= <CV-qualifiers> <type>
::= P <type> # pointer-to
::= R <type> # reference-to
<function-type> ::= F [Y] <bare-function-type> E
<class-enum-type> ::= <name>
<array-type> ::= A <positive dimension number> _ <element type>
::= A [<dimension expression>] _ <element type>
<pointer-to-member-type> ::= M <class type> <member type>
If parse_template_args is TRUE then any <template-args> in the type should be
parsed as part of the type. When is_pack_expansion is TRUE, emit an
indication that the type is a pack expansion.
*/
{
a_const_char *p;
/* Generate the specifier part of the type. */
p = demangle_type_first_part(ptr, CVQ_NONE, /*under_lhs_declarator=*/FALSE,
/*need_trailing_space=*/FALSE,
parse_template_args, dctl);
if (is_pack_expansion) {
/* Emit the pack expansion indication between processing the two type
parts so that function and pointer to member function types are handled
properly. */
write_id_str("...", dctl);
} /* if */
/* Generate the declarator part of the type. */
demangle_type_second_part(ptr, CVQ_NONE, /*under_lhs_declarator=*/FALSE,
dctl);
return p;
} /* full_demangle_type */
static a_const_char *get_operator_name(
a_const_char *ptr,
int *num_operands,
int *length,
a_const_char **close_str,
a_decode_control_block_ptr dctl)
/*
Demangle an IA-64 <operator-name> and return the demangled form.
Return NULL if the operator is invalid. An <operator-name> encodes
an operator in an expression or operator function name.
*num_operands is set to the number of operands expected by the operator.
*length is set to the mangled name length (2 except for vendor extended
operators). *close_str is set to a string that closes the operator,
if necessary, e.g., "]" for subscripting; it is set to "" if not needed.
Note that for the literal operator case (i.e., for the "li" mangled operator),
the string that is returned is in a temporary buffer (and a subsequent call
with another "li" mangled name will overwrite it), so the string should
be copied quickly.
*/
{
a_const_char *str = NULL;
*num_operands = 2;
*close_str = "";
*length = 0;
if (*ptr == '\0') {
bad_mangled_name(dctl);
} else {
char ch2 = ptr[1];
switch (*ptr) {
case 'a':
if (ch2 == 'a') {
str = "&&";
} else if (ch2 == 'd') {
str = "&";
*num_operands = 1;
} else if (ch2 == 'n') {
str = "&";
} else if (ch2 == 'N') {
str = "&=";
} else if (ch2 == 'S') {
str = "=";
} else if (ch2 == 't') {
/* alignof(type) -- newer mangling form */
str = "alignof(";
*num_operands = 0;
*close_str = ")";
} else if (ch2 == 'w') {
/* co_await */
str = "co_await";
*num_operands = 1;
} else if (ch2 == 'z') {
/* alignof(expression) -- newer mangling form */
str = "alignof(";
*close_str = ")";
*num_operands = 1;
} /* if */
break;
case 'c':
if (ch2 == 'c') {
str = "const_cast";
*num_operands = 1;
} else if (ch2 == 'l') {
str = "()";
*num_operands = 0; /* Call is variable-length. */
} else if (ch2 == 'm') {
str = ",";
} else if (ch2 == 'o') {
str = "~";
*num_operands = 1;
} else if (ch2 == 'v') {
str = "cast";
*num_operands = 1;
} /* if */
break;
case 'd':
if (ch2 == 'a') {
str = "delete[] ";
*num_operands = 1;
} else if (ch2 == 'c') {
str = "dynamic_cast";
*num_operands = 1;
} else if (ch2 == 'e') {
str = "*";
*num_operands = 1;
} else if (ch2 == 'l') {
str = "delete ";
*num_operands = 1;
} else if (ch2 == 's') {
str = ".*";
} else if (ch2 == 'v') {
str = "/";
} else if (ch2 == 'V') {
str = "/=";
} /* if */
break;
case 'e':
if (ch2 == 'o') {
str = "^";
} else if (ch2 == 'O') {
str = "^=";
} else if (ch2 == 'q') {
str = "==";
} /* if */
break;
case 'g':
if (ch2 == 'e') {
str = ">=";
} else if (ch2 == 't') {
str = ">";
} /* if */
break;
case 'i':
if (ch2 == 'x') {
str = "[";
*close_str = "]";
} /* if */
break;
case 'l':
if (ch2 == 'e') {
str = "<=";
} else if (ch2 == 'i') {
/* A literal operator is followed by a ud-suffix (with a pre-pended
length). Copy the ud-suffix into a buffer following the ""
for the literal operator (we need to concatenate '"" ' and
the ud-suffix). */
long ud_suffix_len;
a_const_char *ud_suffix_ptr;
ud_suffix_ptr = get_number(ptr+2, &ud_suffix_len, dctl);
/* Note that a literal operator can have zero, one, or two operands,
but we can't tell which case that is here, so consume zero
operands and let the operands be parsed by the "cl" mangling
for the implicit call to the literal operator. */
*num_operands = 0;
str = NULL;
if (!dctl->err_in_id) {
if (ud_suffix_len <= 0) {
bad_mangled_name(dctl);
} else {
if (ud_suffix_buffer == NULL) {
ud_suffix_buffer_length = 128;
ud_suffix_buffer = (char *)malloc(
(true_size_t)ud_suffix_buffer_length);
} else if (ud_suffix_len + 3 + 1 > ud_suffix_buffer_length) {
ud_suffix_buffer_length = ud_suffix_len + 3 + 1;
ud_suffix_buffer = (char *)realloc(ud_suffix_buffer,
(true_size_t)ud_suffix_buffer_length);
} /* if */
if (ud_suffix_buffer != NULL) {
if (strlen(ud_suffix_ptr) < ud_suffix_len) {
bad_mangled_name(dctl);
} else {
strcpy(ud_suffix_buffer, "\"\"");
strncpy(&ud_suffix_buffer[2], ud_suffix_ptr, ud_suffix_len);
ud_suffix_buffer[2 + ud_suffix_len] = '\0';
str = (a_const_char *)ud_suffix_buffer;
*length = ud_suffix_ptr + ud_suffix_len - ptr;
} /* if */
} else {
bad_mangled_name(dctl);
} /* if */
} /* if */
} /* if */
} else if (ch2 == 's') {
str = "<<";
} else if (ch2 == 'S') {
str = "<<=";
} else if (ch2 == 't') {
str = "<";
} /* if */
break;
case 'm':
if (ch2 == 'i') {
str = "-";
} else if (ch2 == 'I') {
str = "-=";
} else if (ch2 == 'l') {
str = "*";
} else if (ch2 == 'L') {
str = "*=";
} else if (ch2 == 'm') {
str = "--";
*num_operands = 1;
} /* if */
break;
case 'n':
if (ch2 == 'a') {
str = "new[] ";
} else if (ch2 == 'e') {
str = "!=";
} else if (ch2 == 'g') {
str = "-";
*num_operands = 1;
} else if (ch2 == 't') {
str = "!";
*num_operands = 1;
} else if (ch2 == 'w') {
str = "new ";
} else if (ch2 == 'x') {
str = "noexcept(";
*close_str = ")";
*num_operands = 1;
} /* if */
break;
case 'o':
if (ch2 == 'o') {
str = "||";
} else if (ch2 == 'r') {
str = "|";
} else if (ch2 == 'R') {
str = "|=";
} /* if */
break;
case 'p':
if (ch2 == 'l') {
str = "+";
} else if (ch2 == 'L') {
str = "+=";
} else if (ch2 == 'm') {
str = "->*";
} else if (ch2 == 'p') {
str = "++";
*num_operands = 1;
} else if (ch2 == 's') {
str = "+";
*num_operands = 1;
} else if (ch2 == 't') {
str = "->";
} /* if */
break;
case 'q':
if (ch2 == 'u') {
str = "?";
*num_operands = 3;
} /* if */
break;
case 'r':
if (ch2 == 'c') {
str = "reinterpret_cast";
*num_operands = 1;
} else if (ch2 == 'm') {
str = "%";
} else if (ch2 == 'M') {
str = "%=";
} else if (ch2 == 's') {
str = ">>";
} else if (ch2 == 'S') {
str = ">>=";
} /* if */
break;
case 's':
if (ch2 == 'c') {
str = "static_cast";
*num_operands = 1;
} else if (ch2 == 't') {
/* sizeof(type) */
str = "sizeof(";
*num_operands = 0;
*close_str = ")";
} else if (ch2 == 'z') {
/* sizeof(expression) */
str = "sizeof(";
*close_str = ")";
*num_operands = 1;
} /* if */
break;
case 't':
if (ch2 == 'e') {
/* typeid(expression) -- newer mangling form */
str = "typeid(";
*close_str = ")";
*num_operands = 1;
} else if (ch2 == 'i') {
/* typeid(type) -- newer mangling form */
str = "typeid(";
*close_str = ")";
*num_operands = 0;
} else if (ch2 == 'r') {
/* rethrow (no arguments) */
str = "throw";
*num_operands = 0;
} else if (ch2 == 'w') {
/* throw (expression) */
str = "throw ";
*num_operands = 1;
} /* if */
break;
case 'v':
/* Vendor extended operators. */
if (start_of_id_is("v18alignofe", ptr)) {
/* __alignof__(expr) -- older mangling form */
str = "__alignof__(";
*close_str = ")";
*num_operands = 1;
*length = 11;
} else if (start_of_id_is("v17alignof", ptr)) {
/* __alignof__(type) -- older mangling form */
str = "__alignof__(";
*close_str = ")";
*num_operands = 0;
*length = 10;
} else if (start_of_id_is("v19__uuidofe", ptr)) {
/* __uuidof(expr) */
str = "__uuidof(";
*close_str = ")";
*num_operands = 1;
*length = 12;
} else if (start_of_id_is("v18__uuidof", ptr)) {
/* __uuidof(type) */
str = "__uuidof(";
*close_str = ")";
*num_operands = 0;
*length = 11;
} else if (start_of_id_is("v17typeide", ptr)) {
/* typeid(expr) -- older mangling form */
str = "typeid(";
*close_str = ")";
*num_operands = 1;
*length = 10;
} else if (start_of_id_is("v16typeid", ptr)) {
/* typeid(type) -- older mangling form */
str = "typeid(";
*close_str = ")";
*num_operands = 0;
*length = 9;
} else if (start_of_id_is("v19clitypeid", ptr)) {
/* C++/CLI T::typeid. */
str = "::typeid";
*num_operands = 0;
*length = 12;
} else if (start_of_id_is("v23min", ptr)) {
/* GNU "<?" */
str = "<?";
*length = 6;
*num_operands = 2;
} else if (start_of_id_is("v23max", ptr)) {
/* GNU ">?" */
str = ">?";
*length = 6;
*num_operands = 2;
} else if (start_of_id_is("v18__real__", ptr)) {
/* __real(expr) */
str = "__real(";
*close_str = ")";
*length = 11;
*num_operands = 1;
} else if (start_of_id_is("v18__imag__", ptr)) {
/* __imag(expr) */
str = "__imag(";
*close_str = ")";
*length = 11;
*num_operands = 1;
} else if (start_of_id_is("v19clihandle", ptr)) {
/* C++/CLI handle-to */
str = "%";
*length = 12;
*num_operands = 1;
} else if (start_of_id_is("v112clisafe_cast", ptr)) {
/* C++/CLI safe_cast<T>() */
str = "safe_cast";
*length = 16;
*num_operands = 1;
} else if (start_of_id_is("9builtin", ptr+2)) {
/* Builtin operation. Name is
vN9builtinXX
^^-- Operation number
^------------ Number of operands (<= 9)
*/
static char builtin_name[] = "builtin-operation-XX";
str = builtin_name;
builtin_name[18] = ptr[10];
builtin_name[19] = ptr[11];
*length = 12;
*num_operands = ptr[1]-'0';
} else if (start_of_id_is("12clisubscript", ptr+2) &&
ptr[1] >= '0' && ptr[1] <= '9') {
/* C++/CLI subscript operation with variable number of operands
(<= 9). The caller handles this as a special case. */
str = "subscript";
*length = 16;
*num_operands = ptr[1]-'0';
} /* if */
break;
default:
break;
} /* switch */
if (*length == 0) *length = 2;
} /* if */
return str;
} /* get_operator_name */
static a_const_char *demangle_source_name(
a_const_char *ptr,
a_boolean is_module_id,
a_decode_control_block_ptr dctl)
/*
Demangle an IA-64 <source-name> and output the demangled form.
Return a pointer to the character position following what was demangled.
A <source-name> encodes an unqualified name as a length plus the
characters of the name. The syntax is:
<source-name> ::= <positive length number> <identifier>
<identifier> ::= <unqualified source code identifier>
If is_module_id is TRUE, the identifier is a module id string, which
begins with a (second) count that gives the length of the file name
part. Just put out the file name part (continue scanning, but do not
output the rest of the string). This is used for an EDG extension.
*/
{
long num;
a_boolean output_chars = TRUE;
ptr = get_number(ptr, &num, dctl);
if (num <= 0) {
bad_mangled_name(dctl);
} else if (is_module_id) {
/* A module id name (an EDG extension), which has the form
<length> _ <file-name-length> _ <file-name> <rest-of-module-id>
Only the file name part is put out. */
ptr = demangle_module_id(ptr, (unsigned long)num, (char *)NULL, dctl);
} else if (num >= 9 && start_of_id_is("_INTERNAL", ptr)) {
/* An EDG extension to individuate certain entities so they don't
collide with similarly named (or unnamed) entities in other
translation units. */
write_id_str("[local to ", dctl);
ptr = demangle_module_id(ptr+9, (unsigned long)num-9, ptr, dctl);
write_id_str("]", dctl);
} else {
if (num >= 11 && start_of_id_is("_GLOBAL__N_", ptr)) {
/* g++ uses names beginning with "_GLOBAL__N_" to identify unnamed
namespaces, and the EDG C++ Front End does also to be compatible
with that. */
write_id_str("<unnamed>", dctl);
output_chars = FALSE;
} /* if */
for (; num > 0; ptr++, num--) {
if (*ptr == '\0') {
/* The name string ends before enough characters have been
accumulated. */
bad_mangled_name(dctl);
break;
} else if (!isalnum((unsigned char)*ptr) && *ptr != '_' && *ptr != '$') {
/* Invalid character in identifier. */
/* g++ names for unnamed namespaces contain bad characters,
e.g., periods. */
if (output_chars) {
bad_mangled_name(dctl);
break;
} /* if */
} else if (output_chars) {
write_id_ch(*ptr, dctl);
} /* if */
} /* for */
} /* if */
return ptr;
} /* demangle_source_name */
static a_const_char *get_instance_number(a_const_char *p,
unsigned long *instance,
a_decode_control_block_ptr dctl)
/*
An underscore optionally preceded by a non-negative instance number is
expected at *p. Advance past the underscore. Return the instance number
(non-negative number plus two -- or one if no number is present) in *instance.
*/
{
*instance = 1;
if (isdigit((unsigned char)*p)) {
long num;
p = get_number(p, &num, dctl);
if (num < 0) {
bad_mangled_name(dctl);
} else {
*instance = num+2;
} /* if */
} /* if */
if (*p == '_') {
p += 1;
} else {
bad_mangled_name(dctl);
} /* if */
return p;
} /* get_instance_number */
static a_const_char *demangle_unnamed_type(a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Demangle an IA-64 <unnamed-type-name>. Return a pointer to the character
position following what was demangled.
<unnamed-type-name> ::= Ut [ <nonnegative number> ] _
::= <closure-type-name>
<closure-type-name> ::= Ul <lambda-sig> E [ <nonnegative number> ] _
<lambda-sig> ::= <parameter type>+
# Parameter types or "v" if the lambda has no parameters
*/
{
unsigned long instance;
if (*ptr == 'U' && ptr[1] == 't') {
/* An unnamed type has an optional instance number followed by an
underscore. */
ptr = get_instance_number(ptr+2, &instance, dctl);
if (!dctl->err_in_id) {
write_id_str("[unnamed type (instance ", dctl);
write_id_number(instance, dctl);
write_id_str(")]", dctl);
} /* if */
} else if (*ptr == 'U' && ptr[1] == 'l') {
/* A lambda has the encoding for the operator() bare function type (without
the return type) and an optional instance number followed by an
underscore. */
write_id_str("[lambda", dctl);
ptr = demangle_bare_function_type(ptr+2, /*no_return_type=*/TRUE,
BFT_PARAMS, dctl);
if (*ptr == 'E') {
ptr = get_instance_number(ptr+1, &instance, dctl);
if (!dctl->err_in_id) {
write_id_str(" (instance ", dctl);
write_id_number(instance, dctl);
write_id_str(")", dctl);
} /* if */
} else {
bad_mangled_name(dctl);
} /* if */
write_id_str("]", dctl);
} else {
bad_mangled_name(dctl);
} /* if */
return ptr;
} /* demangle_unnamed_type */
static a_const_char *demangle_abi_tag_attribute(
a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
GNU uses a 'B' suffix (ostensibly to <source-name>, but it appears in other
contexts as well) to indicate a list of "abi_tag" attributes -- decode that
list here. Note that the resulting "__attribute(...)" string doesn't appear
in the proper location in the demangled name (but it serves the purpose of
differentiating the unmangled name from those without the "abi_tag" attribute).
This is a GNU-specific extension that uses the same letter ('B') as an
EDG-specific extension used for the module id of an externalized name but they
don't collide as the EDG-specific use uses 'B' as a prefix while the
GNU-specific extension uses 'B' as a suffix.
*/
{
long num;
write_id_str("[abi:", dctl);
while (*ptr == 'B') {
ptr++;
ptr = get_number(ptr, &num, dctl);
if (num <= 0) {
bad_mangled_name(dctl);
break;
} else {
for (; num > 0; ptr++, num--) {
if (*ptr == '\0') {
bad_mangled_name(dctl);
break;
} else {
write_id_ch(*ptr, dctl);
} /* if */
} /* for */
if (*ptr == 'B') {
/* Another "abi_tag" attribute follows. */
write_id_ch(',', dctl);
} /* if */
} /* if */
} /* while */
write_id_ch(']', dctl);
return ptr;
} /* demangle_abi_tag_attribute */
static a_const_char *demangle_unqualified_name(
a_const_char *ptr,
a_boolean *is_no_return_name,
a_decode_control_block_ptr dctl)
/*
Demangle an IA-64 <unqualified-name> and output the demangled form.
Return a pointer to the character position following what was demangled.
An <unqualified-name> encodes a name that is not qualified, e.g.,
"f" rather than "A::f". The syntax is:
<unqualified-name> ::= <operator-name>
::= <ctor-dtor-name> # Not handled here
::= <source-name>
::= <unnamed-type-name>
::= DC <source-name>+ E # structured binding
Constructor and destructor names do not get here; see
demangle_nested_name_components. *is_no_return_name is returned TRUE
if the name is one that does not get a return type (e.g., a
conversion function). is_no_return_name can be NULL if the
caller does not need the value.
*/
{
if (is_no_return_name != NULL) *is_no_return_name = FALSE;
if (isdigit((unsigned char)*ptr)) {
/* A <source-name>, which has a length followed by the characters
of the identifier, as in "3abc". */
ptr = demangle_source_name(ptr, /*is_module_id=*/FALSE, dctl);
} else if (*ptr == 'U' &&
(ptr[1] == 't' ||
ptr[1] == 'l')) {
/* <unnamed-type-name> */
ptr = demangle_unnamed_type(ptr, dctl);
} else if (*ptr == 'D' && ptr[1] == 'C') {
/* A mangled name for a structured binding container. */
ptr += 2;
write_id_str("[structured binding for ", dctl);
while (*ptr != 'E' && *ptr != '\0') {
ptr = demangle_source_name(ptr, /*is_module_id=*/FALSE, dctl);
if (*ptr != 'E' && *ptr != '\0') write_id_ch(',', dctl);
} /* while */
if (*ptr != 'E') {
bad_mangled_name(dctl);
} else {
write_id_ch(']', dctl);
ptr++;
} /* if */
} else {
/* <operator-name> */
write_id_str("operator ", dctl);
if (*ptr == 'c' && ptr[1] == 'v') {
/* A conversion function. */
if (is_no_return_name != NULL) *is_no_return_name = TRUE;
/* A demangling ambiguity exists in the IA-64 ABI when parsing a
templated conversion operator. We can't differentiate between
these type productions in this case:
<type> ::= <template-param>
::= <template-template-param> <template-args>
For example, when presented with T_I1BIS4_IiEEEEv, should just the T_
be parsed (as a <template-param>) or should the entire T_I1BIS4_IiEEEE
be parsed (as a <template-template-param> <template-args>)?
We can't do a local retry here because the type may parse just
fine both ways and we only find out later that there is a problem when
a substitution number is too large. On the initial attempt, prefer
the <template-param> case (parse_template_args); on a
subsequent attempt (if the demangling fails), we'll try the other
case. */
ptr = full_demangle_type(ptr+2,
dctl->parse_template_args_after_conversion_operator,
/*is_pack_expansion=*/FALSE,
dctl);
dctl->contains_conversion_operator = TRUE;
} else {
/* Other operator function (not conversion function). */
int num_operands, length;
a_const_char *op_str, *close_str;
op_str = get_operator_name(ptr, &num_operands, &length, &close_str,
dctl);
if (op_str == NULL) {
bad_mangled_name(dctl);
} else {
write_id_str(op_str, dctl);
write_id_str(close_str, dctl);
ptr += length;
} /* if */
} /* if */
} /* if */
if (*ptr == 'B') {
/* A 'B' suffix for a <source-name> is a GNU-specific extension for
an abi_tag attribute. */
ptr = demangle_abi_tag_attribute(ptr, dctl);
} /* if */
return ptr;
} /* demangle_unqualified_name */
static unsigned char get_hex_digit(a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Convert a hexadecimal digit at ptr to an integral value, and return the
value.
*/
{
unsigned char value;
unsigned char ch = (unsigned char)ptr[0];
if (isdigit(ch)) {
value = (ch - '0');
} else if (isxdigit(ch) && islower(ch)) {
value = (ch - 'a' + 10);
} else {
bad_mangled_name(dctl);
value = 0;
} /* if */
return value;
} /* get_hex_digit */
static a_const_char *demangle_float_number(a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Demangle a floating point number as specified in an IA-64 float or complex
literal and output the demangled form. The floating point number is
terminated by either an E or underscore, and the return value will point
to the terminating character.
*/
{
sizeof_t i, length;
char *p;
union {
#if USE_LONG_DOUBLE_FOR_HOST_FP_VALUE
long double ld;
#endif /* USE_LONG_DOUBLE_FOR_HOST_FP_VALUE */
double d;
float f;
} x;
/* Zero the bits of x. */
#if USE_LONG_DOUBLE_FOR_HOST_FP_VALUE
x.ld = 0.0;
#else /* !USE_LONG_DOUBLE_FOR_HOST_FP_VALUE */
x.d = 0.0;
#endif /* USE_LONG_DOUBLE_FOR_HOST_FP_VALUE */
/* Determine the number of digits in the value by scanning to the
terminating "E" or "_". */
length = 0;
p = (char *)ptr;
while (*p != 'E' && *p != '_' && *p != '\0') {
length++;
p++;
} /* while */
if (length % 2 != 0) {
/* An odd number of bytes is an error. */
bad_mangled_name(dctl);
length -= 1;
} /* if */
/* Convert the length to a byte count. */
length /= 2;
if (length > sizeof(x)) {
/* Too many bytes is an error. */
bad_mangled_name(dctl);
length = sizeof(x);
} /* if */
/* Convert the right number of bytes. */
for (i = 0; i < length; i++, ptr+=2) {
unsigned char byte = get_hex_digit(ptr, dctl);
if (dctl->err_in_id) break;
byte = byte<<4 | get_hex_digit(ptr+1, dctl);
if (dctl->err_in_id) break;
if (host_little_endian) {
((unsigned char *)&x)[length-1-i] = byte;
} else {
((unsigned char *)&x)[i] = byte;
} /* if */
} /* for */
if (!dctl->err_in_id) {
/* Convert the floating-point value in x to a string. */
char str[60];
int ndig;
if (i <= sizeof(float)) {
#ifdef FLT_DIG
ndig = FLT_DIG;
#else /* !defined(FLT_DIG) */
ndig = 6;
#endif /* ifdef FLT_DIG */
(void)sprintf(str, "%.*g", ndig, x.f);
#if USE_LONG_DOUBLE_FOR_HOST_FP_VALUE
} else if (i > sizeof(double)) {
#ifdef LDBL_DIG
ndig = LDBL_DIG;
#else /* !defined(LDBL_DIG) */
ndig = 18;
#endif /* ifdef LDBL_DIG */
(void)sprintf(str, "%.*Lg", ndig, x.ld);
#endif /* USE_LONG_DOUBLE_FOR_HOST_FP_VALUE */
} else {
#ifdef DBL_DIG
ndig = DBL_DIG;
#else /* !defined(DBL_DIG) */
ndig = 15;
#endif /* ifdef DBL_DIG */
(void)sprintf(str, "%.*g", ndig, x.d);
} /* if */
/* Add trailing ".0" if no decimal point or exponent indication was put out
and the last character of the string is a digit (i.e., not
"inf" or "nan"). */
p = str + strlen(str) - 1;
if (strchr(str, '.') == NULL &&
strchr(str, 'e') == NULL &&
isdigit((unsigned char)*p)) {
p++;
*p++ = '.';
*p++ = '0';
*p++ = '\0';
} /* if */
write_id_str(str, dctl);
} /* if */
return ptr;
} /* demangle_float_number */
static a_const_char *demangle_float_literal(a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Demangle an IA-64 float literal and output the demangled form.
Return a pointer to the character position following what was demangled.
The syntax is:
<expr-primary> ::= L <type> <value float> E
<float> is the hexadecimal representation of the floating-point value,
high-order bytes first, using lower-case letters.
*/
{
/* Put parentheses around the type to make a cast. */
write_id_ch('(', dctl);
ptr = demangle_type(ptr+1, dctl);
write_id_ch(')', dctl);
if (!dctl->err_in_id) {
ptr = demangle_float_number(ptr, dctl);
if (!dctl->err_in_id) {
ptr = advance_past('E', ptr, dctl);
} /* if */
} /* if */
return ptr;
} /* demangle_float_literal */
static a_const_char *demangle_complex_literal(a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Demangle an IA-64 complex float literal and output the demangled form.
Return a pointer to the character position following what was demangled.
The syntax is:
<expr-primary> ::= L <type> <real-part float> _ <imag-part float> E
<float> is the hexadecimal representation of the floating-point value,
high-order bytes first, using lower-case letters.
*/
{
/* Put parentheses around the type to make a cast. */
write_id_ch('(', dctl);
ptr = demangle_type(ptr+1, dctl);
write_id_str(")(", dctl);
/* Emit the literal as ( <real> + <imag> i). */
if (!dctl->err_in_id) {
ptr = demangle_float_number(ptr, dctl);
if (!dctl->err_in_id) {
ptr = advance_past('_', ptr, dctl);
if (!dctl->err_in_id) {
write_id_ch('+', dctl);
ptr = demangle_float_number(ptr, dctl);
if (!dctl->err_in_id) {
write_id_str("i)", dctl);
if (!dctl->err_in_id) {
ptr = advance_past('E', ptr, dctl);
} /* if */
} /* if */
} /* if */
} /* if */
} /* if */
return ptr;
} /* demangle_complex_literal */
/*
Macro that returns TRUE if the character represents a floating point type.
*/
#define is_floating_point_type(ch) \
((ch) == 'd' || (ch) == 'e' || (ch) == 'f' || (ch) == 'g')
static a_const_char *demangle_expr_primary(a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Demangle an IA-64 literal or external name and output the demangled form.
Return a pointer to the character position following what was demangled.
The syntax is:
<expr-primary> ::= L <type> <value number> E # integer literal
::= L <type> <value float> E # floating literal
::= L <string type> E # string literal
::= L <nullptr type> E # nullptr literal (i.e., "LDnE")
::= L <type> <real-part float> _ <imag-part float> E
# complex floating point literal (C 2000)
::= L <mangled-name> E # external name
*/
{
a_const_char *sub = NULL;
if (ptr[1] == 'S') {
/* Most of the types used in literals are <builtin-type>s, so there are no
substitutions, but complex literals and string literals can have
substitutions, so watch out for these. */
dctl->suppress_id_output++;
(void)demangle_substitution(ptr+1, 0, CVQ_NONE,
/*under_lhs_declarator=*/FALSE,
/*need_trailing_space=*/FALSE,
(a_const_char **)NULL,
&sub,
dctl);
dctl->suppress_id_output--;
} /* if */
if (ptr[1] == '_') {
/* External name, L_Z <encoding> E. */
if (ptr[2] != 'Z') {
bad_mangled_name(dctl);
} else {
ptr = demangle_encoding(ptr+3, /*include_func_params=*/FALSE, dctl);
ptr = advance_past('E', ptr, dctl);
} /* if */
} else if (is_floating_point_type(ptr[1])) {
/* Float literal, L <type> <hex> E, where <hex> is the hexadecimal
representation of the value, high-order bytes first, with
lower-case hex letters. */
ptr = demangle_float_literal(ptr, dctl);
} else if ((ptr[1] == 'C' && is_floating_point_type(ptr[2])) ||
(sub != NULL &&
(sub[0] == 'C' && is_floating_point_type(sub[1])))) {
/* Complex floating point literal. */
ptr = demangle_complex_literal(ptr, dctl);
} else if (ptr[1] == 'D' &&
(ptr[2] == 'n' || ptr[2] == 'N') &&
ptr[3] == 'E') {
/* Recognize the literal for nullptr or __nullptr (the mangling is an
EDG extension for the C++/CLI managed __nullptr keyword). */
dctl->suppress_id_output++;
(void)demangle_type(ptr+1, dctl);
dctl->suppress_id_output--;
if (ptr[2] == 'N') {
write_id_str("__nullptr", dctl);
} else {
write_id_str("nullptr", dctl);
} /* if */
ptr += 4;
} else {
/* Integer literal, or string literal. */
/* Put parentheses around the type to make a cast. */
write_id_ch('(', dctl);
ptr = demangle_type(ptr+1, dctl);
write_id_ch(')', dctl);
if (*ptr == 'E') {
/* There's no value -- must have been a string literal. */
write_id_str("\"...\"", dctl);
} else {
/* Copy the literal value. "n" is translated to a "-". */
if (*ptr == 'n') {
write_id_ch('-', dctl);
ptr++;
} /* if */
/* g++ 3.2 puts out L1xE instead of L_Z1xE, which gets demangled
sort of okay in the g++ demangler because the name is treated
as a type and a cast is put out with nothing following it: (x) */
if (!isdigit((unsigned char)*ptr) && !emulate_gnu_abi_bugs) {
bad_mangled_name(dctl);
} else {
while (isdigit((unsigned char)*ptr)) {
write_id_ch(*ptr, dctl);
ptr++;
} /* while */
} /* if */
} /* if */
ptr = advance_past('E', ptr, dctl);
} /* if */
return ptr;
} /* demangle_expr_primary */
static a_const_char *demangle_expression_list_full(
a_const_char *ptr,
char stop_char,
char open_paren,
char close_paren,
a_decode_control_block_ptr dctl)
/*
Demangle zero or more expressions, terminated by stop_char. The expression
list output is enclosed by open_paren/close_paren and separated by commas.
Returns a pointer to the terminating character (unless an error occurs).
*/
{
a_boolean first_time = TRUE;
write_id_ch(open_paren, dctl);
while (*ptr != stop_char && !dctl->err_in_id) {
if (*ptr == '\0') {
bad_mangled_name(dctl);
break;
} /* if */
if (!first_time) {
write_id_str(", ", dctl);
} else {
first_time = FALSE;
} /* if */
ptr = demangle_expression(ptr, dctl);
} /* while */
write_id_ch(close_paren, dctl);
return ptr;
} /* demangle_expression_list_full */
static a_const_char *demangle_expression_list(
a_const_char *ptr,
char stop_char,
a_decode_control_block_ptr dctl)
/*
Demangle zero or more expressions, terminated by stop_char. The expression
list output is enclosed in parentheses and separated by commas. Returns a
pointer to the terminating character (unless an error occurs).
*/
{
return demangle_expression_list_full(ptr, stop_char, '(', ')', dctl);
} /* demangle_expression_list */
static a_const_char *demangle_initializer(
a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Demangle an <initializer> (or an 'E') starting at ptr.
<initializer> ::= pi <expression>* E # parenthesized initialization
<initializer> ::= il <expression>* E # braced-init list
*/
{
if (*ptr == 'E') {
ptr++;
} else {
if (*ptr == 'p' && ptr[1] == 'i') {
ptr = demangle_expression_list(ptr+2, 'E', dctl);
ptr = advance_past('E', ptr, dctl);
} else if (*ptr == 'i' && ptr[1] == 'l') {
ptr = demangle_expression_list_full(ptr+2, 'E', '{', '}', dctl);
ptr = advance_past('E', ptr, dctl);
} else {
bad_mangled_name(dctl);
} /* if */
} /* if */
return ptr;
} /* demangle_initializer */
static a_const_char *demangle_expression(a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Demangle an IA-64 <expression> and output the demangled form.
Return a pointer to the character position following what was demangled.
An <expression> encodes an expression (usually for a nontype
template argument value written in terms of template parameters or
trailing return types specified using decltype).
The syntax is:
<expression> ::= <unary operator-name> <expression>
::= <binary operator-name> <expression> <expression>
::= <ternary operator-name> <expression> <expression>
<expression>
::= cl <expression>+ E
# call
::= cp <simple-id> <expression>* E
# call (with ADL suppressed)
::= cv <type> <expression>
# conversion with one argument
::= cv <type> _ <expression>* E
# conversion with a different number of arguments
::= [gs] nw <expression>* _ <type> E
# new (expr-list) type
::= [gs] nw <expression>* _ <type> <initializer>
# new (expr-list) type (init)
::= [gs] na <expression>* _ <type> E
# new[] (expr-list) type
::= [gs] na <expression>* _ <type> <initializer>
# new[] (expr-list) type (init)
::= [gs] dl <expression>
# delete expression
::= [gs] da <expression>
# delete[] expression
::= pp_ <expression>
# prefix ++
::= mm_ <expression>
# prefix --
::= ti <type>
# typeid (type)
::= te <expression>
# typeid (expression)
::= dc <type> <expression>
# dynamic_cast<type> (expression)
::= sc <type> <expression>
# static_cast<type> (expression)
::= cc <type> <expression>
# const_cast<type> (expression)
::= rc <type> <expression>
# reinterpret_cast<type> (expression)
::= st <type>
# sizeof (a type)
::= at <type>
# alignof (a type)
::= <template-param>
::= <function-param>
::= dt <expression> <unresolved-name>
# expr.name
::= pt <expression> <unresolved-name>
# expr->name
::= ds <expression> <expression>
# expr.*expr
::= tw <expression>
# throw expression
::= tr
# throw with no operand (rethrow)
::= <unresolved-name>
# f(p), N::f(p), ::f(p),
# freestanding dependent name (e.g., T::x),
# objectless nonstatic member reference
::= sZ <template-param>
# size of a parameter pack
::= sZ <function-param>
# size of a function parameter pack
::= sp <expression>
# pack expansion
::= il <expression>* E
# initializer list
::= tl <type> <expression>* E
# typed initializer list
::= fl <binary operator-name> <expression>
# ( ... op pack )
::= fr <binary operator-name> <expression>
# ( pack op ... )
::= fL <binary operator-name> <expression>
# ( expr op ... op pack )
::= fR <binary operator-name> <expression>
# ( pack op ... op expr )
::= <expr-primary>
Also, these non-standard expressions (EDG-specific) are demangled:
::= gc _ <type> E
# gcnew type
::= gc _ <type> <initializer>
# gcnew type (init)
::= gc <expression>* _ <type> E
# gcnew array<type>(dims)
::= gc <expression>* _ <type> <initializer>
# gcnew array<type>(dims) {init}
*/
{
int num_operands, length;
a_const_char *op_str, *close_str;
if (*ptr == 'L') {
/* A literal or external name. */
ptr = demangle_expr_primary(ptr, dctl);
} else if (*ptr == 'T') {
/* A template parameter. */
ptr = demangle_template_param(ptr, dctl);
} else if (*ptr == 'f') {
if (ptr[1] == 'p' ||
(ptr[1] == 'L' && isdigit((unsigned char)ptr[2]))) {
/* A reference to a function parameter. */
ptr = demangle_parameter_reference(ptr, dctl);
} else {
/* A "fold expression". */
a_boolean unary, left;
a_const_char *op_str, *close_str;
int num_operands, length;
switch (ptr[1]) {
case 'l': unary = TRUE; left = TRUE; break;
case 'L': unary = FALSE; left = TRUE; break;
case 'r': unary = TRUE; left = FALSE; break;
case 'R': unary = FALSE; left = FALSE; break;
default:
bad_mangled_name(dctl);
break;
} /* switch */
ptr += 2;
op_str = get_operator_name(ptr, &num_operands, &length, &close_str,
dctl);
if (op_str == NULL) {
bad_mangled_name(dctl);
} else {
ptr += length;
write_id_ch('(', dctl);
if (unary) {
if (left) {
write_id_str("...", dctl);
write_id_str(op_str, dctl);
ptr = demangle_expression(ptr, dctl);
} else {
ptr = demangle_expression(ptr, dctl);
write_id_str(op_str, dctl);
write_id_str("...", dctl);
} /* if */
} else {
ptr = demangle_expression(ptr, dctl);
write_id_str(op_str, dctl);
write_id_str("...", dctl);
write_id_str(op_str, dctl);
ptr = demangle_expression(ptr, dctl);
} /* if */
write_id_ch(')', dctl);
} /* if */
} /* if */
} else if (*ptr == 'c' && ptr[1] == 'l') {
/* Call expression: "cl <expression>+ E" */
ptr += 2;
ptr = demangle_expression(ptr, dctl);
ptr = demangle_expression_list(ptr, 'E', dctl);
ptr = advance_past('E', ptr, dctl);
} else if (*ptr == 'c' && ptr[1] == 'p') {
/* Call expression (w/ADL suppressed): "cp <simple-id> <expression>* E" */
ptr += 2;
write_id_ch('(', dctl);
ptr = demangle_simple_id(ptr, dctl);
write_id_ch(')', dctl);
ptr = demangle_expression_list(ptr, 'E', dctl);
ptr = advance_past('E', ptr, dctl);
} else if (*ptr == 'c' && ptr[1] == 'v') {
/* Cast/conversion (with one type and zero or more arguments). When
exactly one expression is specified, emit "(T)expr", otherwise
emit T(expr). */
a_const_char *nptr;
a_boolean one_argument = FALSE;
/* Take a peek (without emitting the type, but recording substitutions)
to see which case we have. */
dctl->suppress_id_output++;
nptr = demangle_type(ptr+2, dctl);
dctl->suppress_id_output--;
if (!dctl->err_in_id && *nptr != '_') {
one_argument = TRUE;
write_id_ch('(', dctl);
} /* if */
/* Re-scan the type, this time emitting it (but not recording
substitutions). */
dctl->suppress_substitution_recording++;
ptr = demangle_type(ptr+2, dctl);
dctl->suppress_substitution_recording--;
if (!dctl->err_in_id) {
if (one_argument) {
/* Exactly one expression. */
write_id_ch(')', dctl);
ptr = demangle_expression(ptr, dctl);
} else {
/* Some number of expressions (other than one). */
if (*ptr != '_') {
bad_mangled_name(dctl);
} else {
ptr = demangle_expression_list(ptr+1, 'E', dctl);
ptr = advance_past('E', ptr, dctl);
} /* if */
} /* if */
} /* if */
} else if (*ptr == 'g' && ptr[1] == 's') {
/* global scope: "::". This prefix precedes new/delete operations as
well as the scope-resolution operator in <unresolved-name>. */
write_id_str("::", dctl);
ptr = demangle_expression(ptr+2, dctl);
} else if (*ptr == 'n' && (ptr[1] == 'w' || ptr[1] == 'a')) {
/* new or new[] */
if (ptr[1] == 'w') {
write_id_str("new ", dctl);
} else {
write_id_str("new[] ", dctl);
} /* if */
ptr+=2;
/* Optional placement expressions. */
if (*ptr != '_') {
ptr = demangle_expression_list(ptr, '_', dctl);
write_id_ch(' ', dctl);
} /* if */
ptr = advance_past('_', ptr, dctl);
if (!dctl->err_in_id) {
ptr = demangle_type(ptr, dctl);
if (!dctl->err_in_id) {
ptr = demangle_initializer(ptr, dctl);
} /* if */
} /* if */
} else if (*ptr == 'g' && ptr[1] == 'c') {
/* C++/CLI gcnew (EDG-specific mangling). */
ptr+=2;
write_id_str("gcnew ", dctl);
/* Optional array dimension expressions. */
if (*ptr != '_') {
a_const_char *optr = ptr, *ptr2;
/* We need the type before the dimension list, so suppress the list
to get to the type. */
dctl->suppress_id_output++;
dctl->suppress_substitution_recording++;
ptr2 = demangle_expression_list(ptr, '_', dctl);
dctl->suppress_id_output--;
dctl->suppress_substitution_recording--;
if (!dctl->err_in_id) {
ptr2 = advance_past('_', ptr2, dctl);
ptr = demangle_type(ptr2, dctl);
(void)demangle_expression_list(optr, '_', dctl);
write_id_ch(' ', dctl);
} /* if */
} else {
ptr = advance_past('_', ptr, dctl);
ptr = demangle_type(ptr, dctl);
} /* if */
if (!dctl->err_in_id) {
ptr = demangle_initializer(ptr, dctl);
} /* if */
} else if (*ptr == 'd' && ptr[1] == 't') {
/* expr.name */
write_id_ch('(', dctl);
ptr = demangle_expression(ptr+2, dctl);
if (!dctl->err_in_id) {
write_id_ch('.', dctl);
ptr = demangle_unresolved_name(ptr, dctl);
write_id_ch(')', dctl);
} /* if */
} else if (*ptr == 'p' && ptr[1] == 't') {
/* expr->name */
write_id_ch('(', dctl);
ptr = demangle_expression(ptr+2, dctl);
if (!dctl->err_in_id) {
write_id_str("->", dctl);
ptr = demangle_unresolved_name(ptr, dctl);
write_id_ch(')', dctl);
} /* if */
} else if (*ptr == 's' && ptr[1] == 'Z') {
/* Size of a parameter pack. */
write_id_str("sizeof...(", dctl);
ptr+=2;
if (*ptr == 'T') {
/* sizeof...(<template-param>) */
ptr = demangle_template_param(ptr, dctl);
} else if (*ptr == 'f' ) {
/* sizeof...(<function-param>) */
ptr = demangle_parameter_reference(ptr, dctl);
} else {
bad_mangled_name(dctl);
} /* if */
write_id_ch(')', dctl);
} else if (*ptr == 's' && ptr[1] == 'p') {
/* Pack expansion. */
ptr+=2;
ptr = demangle_expression(ptr, dctl);
write_id_str("...", dctl);
} else if ((*ptr == 't' || *ptr == 'i') && ptr[1] == 'l') {
/* Initializer list. */
if (*ptr == 't') {
/* A type is included. */
ptr = demangle_type(ptr+2, dctl);
} else {
ptr += 2;
} /* if */
if (!dctl->err_in_id) {
ptr = demangle_expression_list_full(ptr, 'E', '{', '}', dctl);
ptr = advance_past('E', ptr, dctl);
} /* if */
} else if ((op_str = get_operator_name(ptr, &num_operands, &length,
&close_str, dctl)) != NULL) {
/* An expression beginning with an operator name. */
/* As a heuristic, to avoid extraneous parentheses in the demangled output,
assume that any operator that has a closing string doesn't need
parentheses around it. */
a_boolean needs_parens = strcmp(close_str, "") == 0;
ptr += length;
if (needs_parens) write_id_ch('(', dctl);
if (strncmp(op_str, "builtin-operation-", 18) == 0) {
/* Builtin operation. Has a variable number of operands. */
int i;
write_id_str(op_str, dctl);
write_id_ch('(', dctl);
for (i = 1; i <= num_operands; i++) {
if (*ptr == 'T' && ptr[1] == 'O') {
/* "TO" indicates a type operand. */
ptr = demangle_type(ptr+2, dctl);
} else {
ptr = demangle_expression(ptr, dctl);
} /* if */
if (i != num_operands) write_id_str(", ", dctl);
} /* for */
write_id_ch(')', dctl);
} else if (strncmp(op_str, "subscript", 9) == 0) {
/* C++/CLI subscript operation. Has a variable number of operands. */
int i;
ptr = demangle_expression(ptr, dctl);
write_id_ch('[', dctl);
for (i = 2; i <= num_operands; i++) {
ptr = demangle_expression(ptr, dctl);
if (i != num_operands) write_id_str(", ", dctl);
} /* for */
write_id_ch(']', dctl);
} else if (num_operands == 1) {
char cast_close = 0;
/* Unary operations (old style cast is handled above). */
if (strcmp(op_str, "++") == 0 ||
strcmp(op_str, "--") == 0) {
if (*ptr == '_') {
/* Prefix version. */
ptr++;
} else {
/* Postfix version. */
close_str = op_str;
op_str = "";
} /* if */
} /* if */
write_id_str(op_str, dctl);
if (strcmp(op_str, "static_cast") == 0 ||
strcmp(op_str, "dynamic_cast") == 0 ||
strcmp(op_str, "const_cast") == 0 ||
strcmp(op_str, "reinterpret_cast") == 0 ||
strcmp(op_str, "safe_cast") == 0) {
/* New style cast. */
write_id_ch('<', dctl);
ptr = demangle_type(ptr, dctl);
write_id_str(">(", dctl);
cast_close = ')';
} /* if */
ptr = demangle_expression(ptr, dctl);
if (cast_close != 0) write_id_ch(cast_close, dctl);
} else if (num_operands == 2) {
/* Binary operations. */
ptr = demangle_expression(ptr, dctl);
write_id_str(op_str, dctl);
ptr = demangle_expression(ptr, dctl);
} else if (num_operands == 3) {
/* Ternary operations ("?"). */
ptr = demangle_expression(ptr, dctl);
write_id_str(op_str, dctl);
ptr = demangle_expression(ptr, dctl);
write_id_str(":", dctl);
ptr = demangle_expression(ptr, dctl);
} else {
/* Special cases: sizeof(type), __alignof__(type),
__uuidof(type), typeid(type), T::typeid, scope resolution "::",
throw (just the rethrow variety). */
if (strcmp(op_str, "sizeof(") == 0) {
/* sizeof(type). */
write_id_str(op_str, dctl);
ptr = demangle_type(ptr, dctl);
} else if (strcmp(op_str, "alignof(") == 0 ||
strcmp(op_str, "__alignof__(") == 0) {
/* __alignof__(type). */
write_id_str(op_str, dctl);
ptr = demangle_type(ptr, dctl);
} else if (strcmp(op_str, "__uuidof(") == 0) {
/* __uuidof(type). */
write_id_str(op_str, dctl);
ptr = demangle_type(ptr, dctl);
} else if (strcmp(op_str, "typeid(") == 0) {
/* typeid(type). */
write_id_str(op_str, dctl);
ptr = demangle_type(ptr, dctl);
} else if (strcmp(op_str, "::typeid") == 0) {
/* C++/CLI T::typeid. */
ptr = demangle_type(ptr, dctl);
write_id_str(op_str, dctl);
} else if (strcmp(op_str, "throw") == 0) {
/* throw. This handles the rethrow variety, throw-expression is
handled separately. */
write_id_str(op_str, dctl);
} else if (strncmp(op_str, "\"\"", 2) == 0) {
/* A literal operator. The EDG front end doesn't produce a mangled
name that should get here (i.e., there should be no "clli" mangled
names), but other compilers produce this mangling, so handle it
by inserting the implied operator keyword. */
write_id_str("operator ", dctl);
write_id_str(op_str, dctl);
} else {
bad_mangled_name(dctl);
} /* if */
} /* if */
write_id_str(close_str, dctl);
if (needs_parens) write_id_ch(')', dctl);
} else {
/* Assume it's an <unresolved-name>. */
ptr = demangle_unresolved_name(ptr, dctl);
} /* if */
return ptr;
} /* demangle_expression */
static a_const_char *demangle_template_args(a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Demangle an IA-64 <template-args> and output the demangled form.
Return a pointer to the character position following what was demangled.
A <template-args> encodes a template argument list. The syntax is:
<template-args> ::= I <template-arg>+ E
<template-arg> ::= <type> # type or template
::= X <expression> E # expression
::= <expr-primary> # simple expressions
::= J <template-arg>* E # argument pack
*/
{
a_boolean suppress = FALSE;
if (*ptr == 'J') {
/* In pack expansion cases, suppress generation of angle brackets. */
suppress = TRUE;
} /* if */
/* Advance past the "I" or "J". */
ptr++;
if (!suppress) write_id_ch('<', dctl);
for (;;) {
if (*ptr == 'X') {
/* An expression, X <expression> E. */
ptr = demangle_expression(ptr+1, dctl);
ptr = advance_past('E', ptr, dctl);
} else if (*ptr == 'L') {
/* Literal or external name. */
ptr = demangle_expr_primary(ptr, dctl);
} else if (*ptr == 'J' ||
(*ptr == 'I' && emulate_gnu_abi_bugs)) {
/* Template argument pack. */
ptr = demangle_template_args(ptr, dctl);
} else if (*ptr == 'E') {
/* No template arguments. */
break;
} else {
/* Type template argument. */
ptr = demangle_type(ptr, dctl);
} /* if */
/* "E" ends the template argument list. */
if (*ptr == 'E') break;
/* Stop on an error. */
if (dctl->err_in_id) break;
/* Continuing, so put out a comma between template arguments. */
write_id_str(", ", dctl);
} /* for */
ptr = advance_past('E', ptr, dctl);
if (!suppress) write_id_ch('>', dctl);
return ptr;
} /* demangle_template_args */
static a_const_char *demangle_nested_name_components(
a_const_char *ptr,
unsigned long num_levels,
a_boolean *is_no_return_name,
a_boolean *has_templ_arg_list,
char *ctor_dtor_kind,
a_const_char **last_component_name,
a_decode_control_block_ptr dctl)
/*
Demangle one or more name level components of an IA-64 <nested-name>.
Each level is either an unqualified name or a substitution, optionally
followed by a template argument list. ptr points to the beginning of
the <nested-name>, after the initial "N" and the <CV-qualifiers> if any.
If num_levels is zero, scan all components of the nested name, stopping
on the final "E"; otherwise, scan num_levels levels and then stop.
Note that a substitution counts as one level even if it represents
several. Return a pointer to the character position following what
was demangled. *is_no_return_name is returned TRUE if the final
component scanned is a function name of a kind that does not take a
return type (constructor, destructor, or conversion function).
*has_templ_arg_list is returned TRUE if the final component includes a
template argument list. If the final component is a constructor or
destructor name, *ctor_dtor_kind is set to the character identifying
the kind of constructor or destructor. If last_component_name is non-NULL,
*last_component_name will be set to the start position of the encoding
for the name of the last component. If the last component is a
substitution, the name of the last component in the substitution is used.
*/
{
a_const_char *prev_component_name = NULL;
a_const_char *first_component_start = ptr;
unsigned long level_num = 0;
*is_no_return_name = FALSE;
*has_templ_arg_list = FALSE;
*ctor_dtor_kind = ' ';
for (;;) {
/* Demangle one level of the nested name. */
a_boolean is_substitution = FALSE;
a_boolean suppress_qualification = FALSE;
level_num++;
*is_no_return_name = FALSE;
*has_templ_arg_list = FALSE;
if (*ptr == 'E' || *ptr == '\0') {
/* Error, unexpected end of nested name. */
bad_mangled_name(dctl);
} else if (*ptr == 'S') {
/* A substitution. */
is_substitution = TRUE;
ptr = demangle_substitution(ptr, 0, CVQ_NONE,
/*under_lhs_declarator=*/FALSE,
/*need_trailing_space=*/FALSE,
&prev_component_name,
(a_const_char **)NULL,
dctl);
/* A substitution cannot be the last thing; it must be followed
by another name or a template argument list. */
if (*ptr == 'E') {
bad_mangled_name(dctl);
} /* if */
} else if (*ptr == 'T') {
/* A <template-param>. */
ptr = demangle_template_param(ptr, dctl);
} else if (*ptr == 'D' && (ptr[1] == 't' || ptr[1] == 'T')) {
/* A <decltype>. */
ptr = demangle_type(ptr, dctl);
} else {
/* Not a substitution or template parameter, so an <unqualified-name>. */
if (*ptr != 'C' && (*ptr != 'D' || ptr[1] == 'C')) {
/* Normal case, not a constructor or destructor name. */
prev_component_name = ptr;
ptr = demangle_unqualified_name(ptr, is_no_return_name, dctl);
} else {
/* A constructor or destructor name (or their C++/CLI counterparts:
a static constructor or finalizer). Put out the class name again
(it's provided by prev_component_name). */
*is_no_return_name = TRUE;
if (*ptr == 'D') {
if (ptr[1] == '7') {
/* A C++/CLI finalizer. */
write_id_ch('!', dctl);
} else {
/* Some type of destructor. */
write_id_ch('~', dctl);
} /* if */
} /* if */
if (prev_component_name == NULL ||
*prev_component_name == 'S') {
/* The constructor or destructor code is the first thing in the
nested name or the previous name is a substitution (we're
supposed to have gotten the name from inside the
substitution). */
bad_mangled_name(dctl);
} else {
a_boolean dummy;
/* Rescan and output the class name (no template argument list). */
(void)demangle_unqualified_name(prev_component_name, &dummy, dctl);
/* Check that the second character of the constructor/destructor
name is a valid digit. */
/* "C3" is for an allocating constructor (not generated by the
EDG Front End but part of the ABI spec). */
/* "D7" is the code used by the EDG C++ Front End for C++/CLI
finalizers. It's not part of the ABI spec. */
/* "C8" is the code used by the EDG C++ Front End for C++/CLI
static constructors. It's not part of the ABI spec. */
/* "C9" is the code used by the EDG C++ Front End for the
common routine called by the various delegating constructor
entry points. It's not part of the ABI spec. */
/* "D9" is the code used by the EDG C++ Front End for the
delegation destructor alternate entry point. It's not part
of the ABI spec. */
if (ptr[1] == '1' || ptr[1] == '2' || ptr[1] == '9' ||
(ptr[0] == 'C' ? (ptr[1] == '3' || ptr[1] == '8') :
(ptr[1] == '0' || ptr[1] == '7'))) {
/* Okay. */
*ctor_dtor_kind = ptr[1];
ptr += 2;
if (*ptr == 'B') {
/* A 'B' suffix for a <source-name> is a GNU-specific extension
for an abi_tag attribute. */
ptr = demangle_abi_tag_attribute(ptr, dctl);
} /* if */
} else {
/* The second character of the constructor or destructor name
encoding is bad. */
bad_mangled_name(dctl);
} /* if */
} /* if */
} /* if */
if (*ptr == 'M') {
/* A <data-member-prefix>. No further output is required (the
member's <source-name> has been emitted above). */
ptr++;
} /* if */
} /* if */
if (*ptr == 'I') {
/* A <template-args> list. */
/* Record a potential substitution on the template prefix up to
this point, but not if the entire prefix is a substitution. */
if (!is_substitution) {
record_substitutable_entity(first_component_start,
subk_template_prefix, level_num-1,
/*parse_template_args=*/FALSE, dctl);
} /* if */
/* Scan the template argument list. */
ptr = demangle_template_args(ptr, dctl);
*has_templ_arg_list = TRUE;
is_substitution = FALSE;
} /* if */
/* "E" marks the end of the list. */
if (*ptr == 'E') break;
if (!is_substitution) {
/* Record a potential substitution on the prefix up to this point,
but not if the entire prefix is a substitution (without
template argument list). */
record_substitutable_entity(first_component_start, subk_prefix,
level_num,
/*parse_template_args=*/FALSE, dctl);
} /* if */
/* Stop on an error. */
if (dctl->err_in_id) break;
/* Stop if we've done enough levels. */
if (num_levels != 0 && level_num >= num_levels) break;
/* Going around again, so the part put out so far is a qualifier and
needs to be followed by "::". */
if (!suppress_qualification) write_id_str("::", dctl);
} /* for */
if (last_component_name != NULL) *last_component_name = prev_component_name;
return ptr;
} /* demangle_nested_name_components */
static a_const_char *demangle_nested_name(
a_const_char *ptr,
a_func_block *func_block,
a_decode_control_block_ptr dctl)
/*
Demangle an IA-64 <nested-name> and output the demangled form. Return
a pointer to the character position following what was demangled.
A <nested-name> represents a qualified name, e.g., A::B::x.
The syntax is:
<nested-name> ::= N [<CV-qualifiers>] [<ref-qualifier>]
<prefix> <unqualified-name> E
::= N [<CV-qualifiers>] [<ref-qualifier>]
<template-prefix> <template-args> E
<prefix> ::= <prefix> <unqualified-name>
::= <template-prefix> <template-args>
::= <template-param>
::= <decltype>
::= # empty
::= <substitution>
::= <prefix> <data-member-prefix>
<template-prefix> ::= <prefix> <template unqualified-name>
::= <template-param>
::= <substitution>
<data-member-prefix> := <member source-name> M
For function names, additional information is returned in *func_block.
*/
{
a_boolean has_templ_arg_list;
a_boolean is_no_return_name;
clear_func_block(func_block);
/* Skip the initial "N". */
ptr++;
/* Accumulate <CV-qualifiers> if present. */
ptr = get_cv_qualifiers(ptr, &func_block->cv_quals);
/* Save a <ref-qualifier> if present. */
ptr = get_ref_qualifier(ptr, &func_block->ref_qual);
/* Get all the components of the nested name. */
ptr = demangle_nested_name_components(ptr,
/*num_levels=*/0,
&is_no_return_name,
&has_templ_arg_list,
&func_block->ctor_dtor_kind,
(a_const_char **)NULL,
dctl);
ptr = advance_past('E', ptr, dctl);
/* The function will have no return type if it is not a template. */
if (!has_templ_arg_list) {
func_block->no_return_type = TRUE;
} /* if */
/* The function will have no return type if it is a constructor,
destructor, or conversion function. */
if (is_no_return_name) {
func_block->no_return_type = TRUE;
} /* if */
return ptr;
} /* demangle_nested_name */
static a_const_char *demangle_local_name(
a_const_char *ptr,
a_func_block *func_block,
a_decode_control_block_ptr dctl)
/*
Demangle an IA-64 <local-name> and output the demangled form. Return
a pointer to the character position following what was demangled.
A <local-name> represents an entity local to a function, and
includes the mangled name of the enclosing function.
The syntax is:
<local-name> := Z <function encoding> E [d [<trailing-param number>] _]
<entity name> [<discriminator>]
:= Z <function encoding> E s [<discriminator>]
<discriminator> := _ <non-negative number> # when number <= 9
:= __ <non-negative number> _ # when number >= 10
For function names, additional information is returned in *func_block.
*/
{
clear_func_block(func_block);
/* Skip over the "Z". */
ptr++;
/* Demangle the function name. */
ptr = demangle_encoding(ptr, /*include_func_params=*/TRUE, dctl);
ptr = advance_past('E', ptr, dctl);
write_id_str("::", dctl);
if (*ptr == 's') {
/* String literal. */
write_id_str("string", dctl);
ptr++;
} else {
if (*ptr == 'd') {
/* Demangle the optional trailing parameter number. */
long param = -1;
ptr += 1;
if (*ptr != '_') {
ptr = get_number(ptr, &param, dctl);
if (param < 0 || *ptr != '_') {
bad_mangled_name(dctl);
} else {
/* Advance past underscore. */
ptr += 1;
} /* if */
} else {
/* Advance past underscore. */
ptr += 1;
} /* if */
if (!dctl->err_in_id) {
write_id_str("[default argument ", dctl);
write_id_signed_number(param+2, dctl);
write_id_str(" (from end)]::", dctl);
} /* if */
} /* if */
/* Demangle the entity name. */
ptr = demangle_name(ptr, func_block, /*options=*/DNO_ALL, dctl);
} /* if */
if (!dctl->err_in_id && *ptr == '_') {
/* Demangle the discriminator. */
long num = -1;
if (isdigit((unsigned char)ptr[1])) {
/* _n (n is single digit) case: */
num = (char)ptr[1] - '0';
ptr += 2;
} else if (ptr[1] == '_' && isdigit((unsigned char)ptr[2])) {
/* __nn_ (nn is at least two digits) case: */
ptr = get_number(ptr+2, &num, dctl);
if (*ptr == '_') {
ptr += 1;
} else {
num = -1;
} /* if */
} /* if */
if (num < 0) {
bad_mangled_name(dctl);
} else {
write_id_str(" (instance ", dctl);
write_id_signed_number(num+2, dctl);
write_id_ch(')', dctl);
} /* if */
} /* if */
return ptr;
} /* demangle_local_name */
static a_const_char *demangle_unscoped_name(
a_const_char *ptr,
a_func_block *func_block,
a_decode_control_block_ptr dctl)
/*
Demangle an IA-64 <unscoped-name> and output the demangled form.
Return a pointer to the character position following what was demangled.
The syntax is:
<unscoped-name> ::= <unqualified-name>
::= St <unqualified-name> # ::std::
For function names, additional information is updated in *func_block.
*/
{
a_boolean is_no_return_name;
if (*ptr == 'S' && ptr[1] == 't') {
/* "St" for "std::". */
write_id_str("std::", dctl);
ptr += 2;
} /* if */
ptr = demangle_unqualified_name(ptr, &is_no_return_name, dctl);
func_block->no_return_type = is_no_return_name;
return ptr;
} /* demangle_unscoped_name */
static a_const_char *demangle_name(a_const_char *ptr,
a_func_block *func_block,
a_demangle_name_option options,
a_decode_control_block_ptr dctl)
/*
Demangle selected portions of an IA-64 <name> and output the demangled form.
Return a pointer to the character position following what was demangled.
The syntax is:
<name> ::= <nested-name>
::= <unscoped-name>
::= <unscoped-template-name> <template-args>
::= <local-name>
<unscoped-template-name> ::= <unscoped-name>
::= <substitution>
For function names, additional information is returned in *func_block.
options is a bit mask that specifies which portion(s) of the name should
be emitted (the entire name is scanned, i.e., the returned value does not
depend on the options specified).
As an EDG extension, allow
B <source-name>
as a prefix to specify a module id for an externalized name.
*/
{
clear_func_block(func_block);
if (*ptr == 'B') {
/* Module-id prefix for externalized name. */
if ((options & DNO_EXTERNALIZATION) == 0) dctl->suppress_id_output++;
write_id_str("[static from ", dctl);
ptr = demangle_source_name(ptr+1, /*is_module_id=*/TRUE, dctl);
write_id_str("] ", dctl);
if ((options & DNO_EXTERNALIZATION) == 0) dctl->suppress_id_output--;
} /* if */
if ((options & DNO_NAME) == 0) dctl->suppress_id_output++;
if (*ptr == 'N') {
/* Nested name, for something like "A::f". */
ptr = demangle_nested_name(ptr, func_block, dctl);
} else if (*ptr == 'Z') {
/* Local name, identifies function and entity local to the function. */
ptr = demangle_local_name(ptr, func_block, dctl);
} else {
/* <unscoped-name> or <unscoped-template-name> <template-args>. */
if (*ptr == 'S' && ptr[1] != '\0' && ptr[2] == 'I') {
/* <substitution> in <unscoped-template-name>, because it's
followed by the "I" beginning a <template-args>. */
ptr = demangle_substitution(ptr, 0, CVQ_NONE,
/*under_lhs_declarator=*/FALSE,
/*need_trailing_space=*/FALSE,
(a_const_char **)NULL,
(a_const_char **)NULL,
dctl);
} else {
/* An <unscoped-name>, possibly as the whole of an
<unscoped-template-name>. */
a_const_char *start = ptr;
ptr = demangle_unscoped_name(ptr, func_block, dctl);
if (*ptr == 'I') {
/* This is a template because it is followed by a template arguments
list. Record the template as a potential substitution. */
record_substitutable_entity(start, subk_unscoped_template_name, 0L,
/*parse_template_args=*/FALSE, dctl);
} /* if */
} /* if */
if (*ptr == 'I') {
/* A <template-args> list. */
ptr = demangle_template_args(ptr, dctl);
} else {
/* Non-template functions do not have return types encoded. */
func_block->no_return_type = TRUE;
} /* if */
} /* if */
if ((options & DNO_NAME) == 0) dctl->suppress_id_output--;
return ptr;
} /* demangle_name */
static a_const_char *demangle_simple_id(a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Demangle a <simple-id>:
<simple-id> ::= <source-name> [ <template-args> ]
*/
{
ptr = demangle_source_name(ptr, /*is_module_id=*/FALSE, dctl);
if (!dctl->err_in_id && *ptr == 'I') {
/* A <template-args> list is present. */
ptr = demangle_template_args(ptr, dctl);
} /* if */
return ptr;
} /* demangle_simple_id */
static a_const_char *demangle_base_unresolved_name(
a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Demangle a <base-unresolved-name>:
<base-unresolved-name> ::= <simple-id>
# unresolved name
::= on <operator-name>
# unresolved operator-function-id
::= on <operator-name> <template-args>
# unresolved operator template-id
::= dn <destructor-name>
# destructor or pseudo-destructor;
# e.g. ~X or ~X<N-1>
<destructor-name> ::= <unresolved-type> # e.g., ~T or ~decltype(f())
::= <simple-id> # e.g., ~A<2*N>
*/
{
int num_operands, length;
a_const_char *op_str, *close_str;
if (*ptr == 'o' && ptr[1] == 'n') {
/* Operator name. */
ptr += 2;
op_str = get_operator_name(ptr, &num_operands, &length, &close_str,
dctl);
if (op_str == NULL) {
bad_mangled_name(dctl);
} else {
ptr += length;
write_id_str("operator ", dctl);
if (strcmp(op_str, "cast") == 0) {
/* A conversion operator has a type. */
ptr = demangle_type(ptr, dctl);
} else {
write_id_str(op_str, dctl);
} /* if */
if (!dctl->err_in_id && *ptr == 'I') {
/* A <template-args> list is present. */
ptr = demangle_template_args(ptr, dctl);
} /* if */
} /* if */
} else if (*ptr == 'd' && ptr[1] == 'n') {
/* <destructor-name> */
ptr += 2;
write_id_ch('~', dctl);
if (isdigit((unsigned char)*ptr)) {
ptr = demangle_simple_id(ptr, dctl);
} else {
ptr = demangle_type(ptr, dctl);
} /* if */
} else {
/* <simple-id> */
ptr = demangle_simple_id(ptr, dctl);
} /* if */
return ptr;
} /* demangle_base_unresolved_name */
static a_const_char *demangle_unresolved_name(a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Demangle an <unresolved-name>:
<unresolved-name> ::= [gs] <base-unresolved-name>
# x or (with "gs") ::x
::= sr <unresolved-type> <base-unresolved-name>
# T::x / decltype(p)::x
::= srN <unresolved-type> <unresolved-qualifier-level>+ E
<base-unresolved-name>
# T::N::x /decltype(p)::N::x
::= [gs] sr <unresolved-qualifier-level>+ E
<base-unresolved-name>
# A::x, N::y, A<T>::z; "gs" means leading "::"
<unresolved-type> ::= <template-param>
::= <decltype>
::= <substitution>
<unresolved-qualifier-level> ::= <simple-id>
Note that the "gs" may already have been stripped by the caller (since it
can also appear at the <expression> level).
*/
{
a_boolean gpp_qualified_name = FALSE;
if (*ptr == 'g' && ptr[1] == 's') {
/* Global scope: "::". */
write_id_str("::", dctl);
ptr += 2;
} /* if */
if (*ptr == 's' && ptr[1] == 'r') {
/* Scope resolution "::":
::= sr <unresolved-type> <base-unresolved-name>
::= srN <unresolved-type> <unresolved-qualifier-level>+ E
<base-unresolved-name>
::= [gs] sr <unresolved-qualifier-level>+ E <base-unresolved-name>
Differentiate between the first and third cases by looking to see if
the character after the "sr" is numeric (in which case it must be
an <unresolved-qualifier-level>).
*/
ptr += 2;
if (isdigit((unsigned char)*ptr)) {
/* We've got this case:
::= [gs] sr <unresolved-qualifier-level>+ E <base-unresolved-name>
*/
while (!dctl->err_in_id && *ptr != 'E') {
if (*ptr == '\0') {
bad_mangled_name(dctl);
} else {
ptr = demangle_simple_id(ptr, dctl);
write_id_str("::", dctl);
} /* if */
} /* while */
ptr = advance_past('E', ptr, dctl);
} else {
if (emulate_gnu_abi_bugs) {
/* g++ 3.2 sometimes puts out a qualified name as the second
operand. Look ahead to see whether that form is used.
If so, we want to skip over the type but not output it,
because the qualified name repeats that type. */
a_const_char *ptr2;
dctl->suppress_id_output++;
dctl->suppress_substitution_recording++;
ptr2 = demangle_type(ptr, dctl);
dctl->suppress_id_output--;
dctl->suppress_substitution_recording--;
if (*ptr2 == 'N') {
gpp_qualified_name = TRUE;
/* Scan the type again to get substitutions recorded. */
dctl->suppress_id_output++;
ptr = demangle_type(ptr, dctl);
dctl->suppress_id_output--;
} /* if */
} /* if */
if (!gpp_qualified_name) {
/* We've got one of these two cases:
::= sr <unresolved-type> <base-unresolved-name>
::= srN <unresolved-type> <unresolved-qualifier-level>+ E
<base-unresolved-name>
*/
if (*ptr == 'N') {
ptr = demangle_type(ptr+1, dctl);
write_id_str("::", dctl);
while (!dctl->err_in_id && *ptr != 'E') {
if (*ptr == '\0') {
bad_mangled_name(dctl);
} else {
ptr = demangle_simple_id(ptr, dctl);
write_id_str("::", dctl);
} /* if */
} /* while */
ptr = advance_past('E', ptr, dctl);
} else {
ptr = demangle_type(ptr, dctl);
write_id_str("::", dctl);
} /* if */
} /* if */
} /* if */
if (!dctl->err_in_id) {
/* The qualifiers have been processed, now only a <base-unresolved-name>
remains. */
ptr = demangle_base_unresolved_name(ptr, dctl);
} /* if */
} else {
/* <base-unresolved-name> */
ptr = demangle_base_unresolved_name(ptr, dctl);
} /* if */
return ptr;
} /* demangle_unresolved_name */
static a_const_char *demangle_call_offset(a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Demangle an IA-64 <call_offset> and output the demangled form. Return
a pointer to the character position following what was demangled.
A <call-offset> is used in the encoded name for a thunk for a
virtual function. The syntax is:
<call-offset> ::= h <nv-offset> _
::= v <v-offset> _
<nv-offset> ::= <offset number> # non-virtual base override
<v-offset> ::= <offset number> _ <virtual offset number>
# virtual base override, with vcall offset
*/
{
long num;
a_boolean v_form = FALSE;
if (*ptr != 'h' && *ptr != 'v') {
bad_mangled_name(dctl);
} else {
v_form = (*ptr == 'v');
write_id_str("(offset ", dctl);
ptr = get_number(ptr+1, &num, dctl);
write_id_signed_number(num, dctl);
if (v_form) {
write_id_str(", virtual offset ", dctl);
ptr = advance_past_underscore(ptr, dctl);
ptr = get_number(ptr, &num, dctl);
write_id_signed_number(num, dctl);
} /* if */
ptr = advance_past_underscore(ptr, dctl);
write_id_str(") ", dctl);
} /* if */
return ptr;
} /* demangle_call_offset */
static a_const_char *demangle_special_name(a_const_char *ptr,
a_decode_control_block_ptr dctl)
/*
Demangle an IA-64 <special-name> and output the demangled form. Return
a pointer to the character position following what was demangled.
Special names are used for generated things like virtual function tables.
The syntax is:
<special-name> ::= TV <type> # virtual table
::= TT <type> # VTT structure (construction vtable index)
::= TI <type> # typeinfo structure
::= TS <type> # typeinfo name (null-terminated byte string)
::= GV <object name> # Guard variable for one-time init
::= TW <object name> # Thread-local wrapper
::= TH <object name> # Thread-local initialization
::= T <call-offset> <base encoding>
# base is the nominal target function of thunk
::= Tc <call-offset> <call-offset> <base encoding>
# base is the nominal target function of thunk
# first call-offset is 'this' adjustment
# second call-offset is result adjustment
*/
{
a_func_block func_block;
if (*ptr == 'G') {
if (ptr[1] == 'V') {
/* Guard variable, GV <object name>. */
write_id_str("Initialization guard variable for ", dctl);
ptr = demangle_name(ptr+2, &func_block, /*options=*/DNO_ALL, dctl);
} else {
bad_mangled_name(dctl);
} /* if */
} else if (*ptr == 'T') {
if (ptr[1] == 'V') {
/* Virtual table, TV <type>. */
write_id_str("Virtual function table for ", dctl);
ptr = demangle_type(ptr+2, dctl);
} else if (ptr[1] == 'T') {
/* Virtual table table, TT <type>. */
write_id_str("Virtual table table for ", dctl);
ptr = demangle_type(ptr+2, dctl);
} else if (ptr[1] == 'I') {
/* Typeinfo, TI <type>. */
write_id_str("Typeinfo for ", dctl);
ptr = demangle_type(ptr+2, dctl);
} else if (ptr[1] == 'S') {
/* Typeinfo name, TS <type>. */
write_id_str("Typeinfo name for ", dctl);
ptr = demangle_type(ptr+2, dctl);
} else if (ptr[1] == 'c') {
/* Covariant thunk, Tc <call-offset> <call-offset> <base encoding>. */
write_id_str("Covariant thunk for ", dctl);
ptr = demangle_call_offset(ptr+2, dctl);
ptr = demangle_call_offset(ptr, dctl);
ptr = demangle_encoding(ptr, /*include_func_params=*/TRUE, dctl);
} else if (ptr[1] == 'h' || ptr[1] == 'v') {
/* Thunk, T <call-offset> <base encoding>. */
write_id_str("Thunk for ", dctl);
ptr = demangle_call_offset(ptr+1, dctl);
ptr = demangle_encoding(ptr, /*include_func_params=*/TRUE, dctl);
} else if (ptr[1] == 'H') {
/* Thread-local initialization alias for <object name>. */
write_id_str("Thread-local initialization routine for ", dctl);
ptr = demangle_name(ptr+2, &func_block, /*options=*/DNO_ALL, dctl);
} else if (ptr[1] == 'W') {
/* Thread-local wrapper for <object name>. */
write_id_str("Thread-local wrapper routine for ", dctl);
ptr = demangle_name(ptr+2, &func_block, /*options=*/DNO_ALL, dctl);
} else {
bad_mangled_name(dctl);
} /* if */
} else {
bad_mangled_name(dctl);
} /* if */
return ptr;
} /* demangle_special_name */
static a_const_char *demangle_function_or_data_name(
a_const_char *ptr,
a_boolean include_func_params,
a_boolean first_scan,
a_decode_control_block_ptr dctl)
/*
Demangle selected pieces of an IA-64 <function name><bare-function-type> or
<data name> and output the demangled form. Return a pointer to the character
position following what was demangled. Do not output function parameters if
include_func_params is FALSE. This demangling occurs in two passes, on the
first scan (when first_scan is TRUE), only the return type of the template
function is emitted; on the second scan (when first_scan is FALSE) the
remainder of the demangling is produced. Only template functions require
two scans, but since it's not known ahead of time if a template function
is being demangled, all names are subject to the two pass method (with only
the optional externalization information being emitted on the first pass for
non-template functions).
*/
{
a_func_block func_block;
a_bare_function_type_option bft_option;
a_demangle_name_option dno_option;
/* This routine is invoked in two passes, and in turn invokes
demangle_name and demangle_bare_function_type to emit various pieces
of the name at various times. For example for this mangled name:
_ZB19_7_x4280_C_a9e5c9ef4sft7IiEiT_
the demangled name along with the pass in which each element is
emitted and the option that controls its output is:
[static from x4280_C] int sft7<int>(T1)
^^^^^---- second pass, BFT_PARAMS
^^^^--------- second pass, DNO_NAME
^^^^------------- first pass, BFT_RETURN
^^^^^^^^^^^^^^^^^^^^^^----------------- first pass, DNO_EXTERNALIZATION
*/
if (first_scan) {
/* The return type of a template function (if present) and
externalization information (if present) is emitted on the first
scan. */
dno_option = DNO_EXTERNALIZATION;
bft_option = BFT_RETURN;
} else {
/* On the second scan, emit the name and any template parameters
(if present). */
dno_option = DNO_NAME;
bft_option = BFT_PARAMS;
/* The first pass recorded all of the substitutions, so suppress
substitution recording on the second pass. */
dctl->suppress_substitution_recording++;
} /* if */
ptr = demangle_name(ptr, &func_block, dno_option, dctl);
/* For the first scan, generally speaking, suppress the remaining output
(except for the call to demangle_bare_function_type below). */
if (first_scan) dctl->suppress_id_output++;
/* If there's more, it's the <bare-function-type>. */
if (*ptr != '\0' && *ptr != 'E') {
/* Q <nested-name> indicates a function that is explicitly
overridden. This is an extension over the IA-64 ABI spec. */
if (*ptr == 'Q') {
a_func_block dummy_func_block;
write_id_str(" [overriding ", dctl);
ptr = demangle_name(ptr+1, &dummy_func_block, dno_option, dctl);
write_id_str("] ", dctl);
} /* if */
if (first_scan) dctl->suppress_id_output--;
if (!include_func_params) dctl->suppress_id_output++;
ptr = demangle_bare_function_type(ptr, func_block.no_return_type,
bft_option, dctl);
if (!include_func_params) dctl->suppress_id_output--;
if (first_scan) dctl->suppress_id_output++;
if (func_block.cv_quals != 0) {
/* Put out cv-qualifiers for a member function. */
write_id_ch(' ', dctl);
output_cv_qualifiers(func_block.cv_quals,
/*trailing_space=*/FALSE, dctl);
} /* if */
if (func_block.ref_qual != REFQ_NONE) {
/* Put out ref-qualifier for a member function. */
write_id_ch(' ', dctl);
output_ref_qualifier(func_block.ref_qual, dctl);
} /* if */
} /* if */
if (func_block.ctor_dtor_kind != ' ') {
/* Identify the kind of constructor or destructor if necessary. */
switch (func_block.ctor_dtor_kind) {
case '0':
write_id_str(" [deleting]", dctl);
break;
case '1':
/* Complete constructor or destructor gets no extra label. */
break;
case '2':
write_id_str(" [subobject]", dctl);
break;
case '3':
/* Not generated by the EDG front end, but part of the ABI spec. */
write_id_str(" [allocating]", dctl);
break;
case '7':
/* An EDG extension for C++/CLI finalizers (no extra label). */
break;
case '8':
/* An EDG extension for C++/CLI static constructors. */
write_id_str(" [static]", dctl);
break;
case '9':
/* The EDG front end uses "C9" for the common constructor routine
called by delegating constructor alternate entry points, and
"D9" for (corresponding) delegation destructors. */
write_id_str(" [delegation]", dctl);
break;
default:
/* Bad character. This shouldn't happen, because the character
was checked earlier. */
bad_mangled_name(dctl);
} /* switch */
} /* if */
if (first_scan) {
dctl->suppress_id_output--;
} else {
dctl->suppress_substitution_recording--;
} /* if */
return ptr;
} /* demangle_function_or_data_name */
static a_const_char *demangle_encoding(
a_const_char *ptr,
a_boolean include_func_params,
a_decode_control_block_ptr dctl)
/*
Demangle an IA-64 <encoding> and output the demangled form. Return
a pointer to the character position following what was demangled.
<encoding> is almost the top-level term in the grammar; it's what
follows the initial "_Z" in a mangled name. The syntax is:
<encoding> ::= <function name> <bare-function-type>
::= <data name>
::= <special-name>
Do not output function parameters if include_func_params is FALSE.
*/
{
/* Special names begin with "T" (e.g., TV for a virtual function table)
or "GV" for a guard variable. */
if (*ptr == 'T' || (*ptr == 'G' && ptr[1] == 'V')) {
ptr = demangle_special_name(ptr, dctl);
} else {
/* Function or data name. */
/* Scan the <function name> or <data name> twice in order to emit a
potential return type for a template function (emitted in the first
scan) before the name of the template function (second scan). */
(void)demangle_function_or_data_name(ptr, include_func_params,
/*first_scan=*/TRUE, dctl);
if (!dctl->err_in_id) {
ptr = demangle_function_or_data_name(ptr, include_func_params,
/*first_scan=*/FALSE, dctl);
} /* if */
} /* if */
return ptr;
} /* demangle_encoding */
static void init_demangle_state(char *output_buffer,
sizeof_t output_buffer_size,
a_decode_control_block_ptr dctl)
/*
Utility to set the state of the demangler to its initial values.
*/
{
clear_control_block(dctl);
dctl->output_id = output_buffer;
dctl->output_id_size = output_buffer_size;
num_substitutions = 0;
} /* init_demangle_state */
/* Make sure that decode_identifier doesn't collide with symbols in user
programs when being compiled as part of lib_src. */
#if COMPILE_DECODE_FOR_LIB_SRC
static
#endif /* COMPILE_DECODE_FOR_LIB_SRC */
void decode_identifier(a_const_char *id,
char *output_buffer,
sizeof_t output_buffer_size,
a_boolean *err,
a_boolean *buffer_overflow_err,
sizeof_t *required_buffer_size)
/*
Demangle the identifier id (which is null-terminated), and put the demangled
form (null-terminated) into the output_buffer provided by the caller.
A name that does not begin with the "_Z" indicating an external name is
demangled as a type name (see the ABI description of __cxa_demangle).
output_buffer_size gives the allocated size of output_buffer. If there
is some error in the demangling process, *err will be returned TRUE.
In addition, if the error is that the output buffer is too small,
*buffer_overflow_err will (also) be returned TRUE, and *required_buffer_size
is set to the size of buffer required to do the demangling. Note that
if the mangled name is compressed, and the buffer size is smaller than
the size of the uncompressed mangled name, the size returned will be
enough to uncompress the name but not enough to produce the demangled form.
The caller must be prepared in that case to loop a second time (the
length returned the second time will be correct).
*/
{
a_const_char *end_ptr;
a_decode_control_block control_block;
a_decode_control_block_ptr dctl = &control_block;
init_demangle_state(output_buffer, output_buffer_size, dctl);
{
/* Determine whether host is little-endian or big-endian. */
int i = 1;
host_little_endian = (*(char *)&i) == 1;
}
for (;;) {
if (start_of_id_is("_Z", id)) {
/* A mangled name, beginning with "_Z". */
end_ptr = demangle_encoding(id+2, /*include_func_params=*/TRUE, dctl);
} else {
/* A non-external name, assumed to be a mangled type name. */
end_ptr = demangle_type(id, dctl);
} /* if */
if (dctl->err_in_id &&
dctl->contains_conversion_operator &&
!dctl->parse_template_args_after_conversion_operator) {
/* If demangling failed and the mangled name contained a conversion
operator (i.e., "cv <type>"), retry the demangling operation, but
this time, parse any template args that may appear after a
templated conversion operator. This needed because of a demangling
ambiguity that exists for templated conversion operators. */
init_demangle_state(output_buffer, output_buffer_size, dctl);
dctl->parse_template_args_after_conversion_operator = TRUE;
} else {
break;
} /* if */
} /* for */
if (dctl->output_overflow_err) {
dctl->err_in_id = TRUE;
} else {
/* Add a terminating null. */
dctl->output_id[dctl->output_id_len] = 0;
} /* if */
/* Make sure the whole identifier was taken. */
if (!dctl->err_in_id && end_ptr != NULL && *end_ptr != '\0') {
bad_mangled_name(dctl);
} /* if */
*err = dctl->err_in_id;
*buffer_overflow_err = dctl->output_overflow_err;
*required_buffer_size = dctl->output_id_len + 1; /* +1 for final null. */
} /* decode_identifier */
/*
Result status codes used by __cxa_demangle.
*/
#define CXA_DEMANGLE_SUCCESS 0
#define CXA_DEMANGLE_ALLOC_FAILURE -1
#define CXA_DEMANGLE_INVALID_NAME -2
#define CXA_DEMANGLE_INVALID_ARGUMENTS -3
#if COMPILE_DECODE_FOR_LIB_SRC && defined(__EDG_RUNTIME_USES_NAMESPACES)
namespace __cxxabiv1 {
#endif /* COMPILE_DECODE_FOR_LIB_SRC&&defined(__EDG_RUNTIME_USES_NAMESPACES) */
EXTERN_C char *__cxa_demangle(char *mangled_name,
char *user_buffer,
true_size_t *user_buffer_size,
int *status)
/*
Demangling library interface specified by the IA-64 ABI. "mangled_name"
is the name to be demangled. "user_buffer" is the buffer into which the
demangled name should be placed. "user_buffer_size" is the size of
"user_buffer". If "user_buffer" is NULL or is too small, it is reallocated
and "user_buffer_size" is set to the new size.
*/
{
#define TEMP_BUFFER_SIZE 256
int result_status = CXA_DEMANGLE_SUCCESS;
char temp_buffer[TEMP_BUFFER_SIZE];
char *buf_to_use = NULL;
a_boolean temp_buffer_used = FALSE;
sizeof_t buf_size = 0;
if (user_buffer != NULL && user_buffer_size == NULL) {
/* A buffer was provided but its size is not specified. */
result_status = CXA_DEMANGLE_INVALID_ARGUMENTS;
} else {
/* Demangle the name. */
a_boolean err;
a_boolean buffer_overflow_err;
sizeof_t required_buffer_size;
/* If no buffer was provided by the caller, try using temp_buffer. */
if (user_buffer == NULL) {
buf_to_use = temp_buffer;
temp_buffer_used = TRUE;
buf_size = TEMP_BUFFER_SIZE;
} else {
buf_to_use = user_buffer;
buf_size = *user_buffer_size;
} /* if */
do {
decode_identifier(mangled_name, buf_to_use, buf_size, &err,
&buffer_overflow_err, &required_buffer_size);
if (buffer_overflow_err) {
/* The buffer was too small. Allocate a new buffer. */
if (temp_buffer_used || buf_to_use == user_buffer) {
/* We previously used a local buffer or we used the buffer
supplied by the user. Allocate a new one. Note that we
don't free the user buffer yet because an error might still
occur and we can only provide the new buffer address in cases
where we return successfully. */
buf_to_use = (char*)malloc((true_size_t)required_buffer_size);
temp_buffer_used = FALSE;
} else {
/* We are using a user-buffer. Reallocate that buffer. */
buf_to_use = (char*)realloc(buf_to_use,
(true_size_t)required_buffer_size);
} /* if */
buf_size = required_buffer_size;
if (buf_to_use == NULL) {
/* The allocation failed. */
result_status = CXA_DEMANGLE_ALLOC_FAILURE;
} /* if */
} else if (err) {
/* A name decoding error occurred. */
result_status = CXA_DEMANGLE_INVALID_NAME;
} /* if */
/* Continue looping until decode_identifier succeeds. If an error
was detected, terminate the loop. */
} while (err && result_status == CXA_DEMANGLE_SUCCESS);
if (result_status == CXA_DEMANGLE_SUCCESS && temp_buffer_used) {
/* The temporary buffer was used. Copy the result to a dynamically
allocated buffer. */
true_size_t size;
size = strlen(temp_buffer) + 1;
buf_to_use = (char*)malloc(size);
if (buf_to_use == NULL) {
result_status = CXA_DEMANGLE_ALLOC_FAILURE;
} else {
(void)strcpy(buf_to_use, temp_buffer);
} /* if */
} /* if */
} /* if */
/* Return the status to the caller. */
if (status != NULL) *status = result_status;
/* Return NULL if there was an error. */
if (result_status != CXA_DEMANGLE_SUCCESS) {
/* If the buffer being used was allocated above, free it now. */
if (!temp_buffer_used &&
user_buffer != NULL && buf_to_use != user_buffer) {
free(buf_to_use);
} /* if */
buf_to_use = NULL;
} else {
/* The demangling was successful. */
/* If the buffer being returned is not the buffer supplied by the
user, free the user buffer. */
if (user_buffer != NULL && buf_to_use != user_buffer) {
free(user_buffer);
/* Update the size parameter passed in. */
if (user_buffer_size != NULL) {
*user_buffer_size = buf_size;
} /* if */
} /* if */
} /* if */
return buf_to_use;
#undef TEMP_BUFFER_SIZE
} /* __cxa_demangle */
#if COMPILE_DECODE_FOR_LIB_SRC && defined(__EDG_RUNTIME_USES_NAMESPACES)
} /* namespace __cxxabiv1 */
#endif /* COMPILE_DECODE_FOR_LIB_SRC&&defined(__EDG_RUNTIME_USES_NAMESPACES) */
#endif /* !IA64_ABI */
/******************************************************************************
* \ ___ / *
* / \ *
* Edison Design Group C++/C Front End - | \^/ | - *
* \ / *
* / | | \ *
* Copyright 1996-2018 Edison Design Group Inc. [_] *
* *
******************************************************************************/